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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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/45] 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 92ff702ecc1c5adc11fe8371b9ab2dc9cc8a5422 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:04:58 +0200 Subject: [PATCH 19/45] 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 725725d35b9b82ad0685e6a1f0451ce938f7bb84 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:17:44 +0200 Subject: [PATCH 20/45] 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 21/45] 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 22/45] 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 23/45] 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 24/45] 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 25/45] 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 26/45] 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 6a819fe8a466a3bd3230e01fa44718418be38343 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Fri, 9 Aug 2024 11:06:48 +0200 Subject: [PATCH 27/45] 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 28/45] 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 29/45] 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 1f39884ea018cc4cacd20e494410425d0301e3d7 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Sat, 17 Aug 2024 14:24:55 +0200 Subject: [PATCH 30/45] 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 f83367519c0c2bbcb213430d87e26558f9c7cb65 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Fri, 23 Aug 2024 10:39:46 +0200 Subject: [PATCH 31/45] Remove detailed timers for third_in and sync --- nestkernel/conn_builder.cpp | 27 --------------------------- nestkernel/connection_manager.cpp | 15 --------------- nestkernel/connection_manager.h | 5 ----- nestkernel/mpi_manager.cpp | 11 ----------- nestkernel/mpi_manager.h | 3 --- 5 files changed, 61 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 7bb9d8d9f2..3c5c8bd5fe 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -691,10 +691,6 @@ 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 ); @@ -707,11 +703,6 @@ 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() ] = @@ -720,11 +711,6 @@ 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 ); @@ -743,22 +729,12 @@ 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 @@ -780,9 +756,6 @@ 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 3ee4aeac00..1d47802ff9 100644 --- a/nestkernel/connection_manager.cpp +++ b/nestkernel/connection_manager.cpp @@ -120,11 +120,6 @@ 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(); @@ -232,16 +227,6 @@ 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 dc437e5147..431ab7d1d3 100644 --- a/nestkernel/connection_manager.h +++ b/nestkernel/connection_manager.h @@ -462,11 +462,6 @@ 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 ); diff --git a/nestkernel/mpi_manager.cpp b/nestkernel/mpi_manager.cpp index 8ca8d3ff8a..b7a348322e 100644 --- a/nestkernel/mpi_manager.cpp +++ b/nestkernel/mpi_manager.cpp @@ -185,8 +185,6 @@ 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 @@ -262,7 +260,6 @@ 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 @@ -801,15 +798,7 @@ 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 faaf16a796..6b6b2a6dea 100644 --- a/nestkernel/mpi_manager.h +++ b/nestkernel/mpi_manager.h @@ -349,9 +349,6 @@ 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 742c36ff34270114ba3cc562b41b5e268b4c750b Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Aug 2024 08:41:15 +0200 Subject: [PATCH 32/45] Fill ThirdIn buffers more efficiently --- nestkernel/conn_builder.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 3c5c8bd5fe..f99e09821d 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -711,15 +711,22 @@ 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; - // 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 ); + // send buffer for third rank-third gid pairs + std::vector< size_t > send_stg( num_ranks * slots_per_rank, 0 ); // send buffer + + // vector mapping destination rank to next entry in send_stg to write to + // initialization based on example in https://en.cppreference.com/w/cpp/iterator/back_insert_iterator + std::vector< size_t > rank_idx; + rank_idx.reserve( num_ranks ); + std::generate_n( std::back_insert_iterator< std::vector< size_t > >( rank_idx ), + num_ranks, + [ rk = 0, slots_per_rank ]() mutable { return ( rk++ ) * slots_per_rank; } ); for ( auto stgp : source_third_gids_ ) { for ( auto& stg : *stgp ) { - const auto ix = stg.third_rank * slots_per_rank + rank_idx[ stg.third_rank ]; + const auto ix = 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; From c5a22fb6604b5430ff2f6c33e1560f07d60657d5 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Aug 2024 08:42:06 +0200 Subject: [PATCH 33/45] Parse ThirdIn receive buffer more efficiently --- nestkernel/conn_builder.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index f99e09821d..b435244bc4 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -709,6 +709,13 @@ nest::ThirdInBuilder::connect_() *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() ); + + if ( global_max_stc == 0 ) + { + // no rank has any any connections requiring ThirdIn connections + return; + } + const size_t slots_per_rank = 2 * global_max_stc; // send buffer for third rank-third gid pairs @@ -752,10 +759,17 @@ nest::ThirdInBuilder::connect_() for ( size_t idx = 0; idx < recv_stg.size(); idx += 2 ) { - // 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 ( third_gid == 0 ) + { + // No more entries from this rank, jump to beginning of next rank + // Subtract 2 because 2 is added again by the loop increment expression + // Since slots_per_rank >= 1 by definition, idx >= 0 is ensured + idx = ( idx / slots_per_rank + 1 ) * slots_per_rank - 2; + continue; + } + + if ( kernel().vp_manager.is_node_id_vp_local( third_gid ) ) { const auto source_gid = recv_stg[ idx + 1 ]; assert( source_gid > 0 ); From 7c5cd287f41e50aaf19e3eae00b25b213f104373 Mon Sep 17 00:00:00 2001 From: HanjiaJiang Date: Mon, 26 Aug 2024 10:42:33 +0200 Subject: [PATCH 34/45] Change parameter values in astrocyte example scripts according to Iiro's suggestions Co-Authored-by: Iiro Ahokainen <59561196+IiroAhokainen@users.noreply.github.com> Co-Authored-by: Jugoslava Acimovic --- .../examples/astrocytes/astrocyte_brunel_bernoulli.py | 10 +--------- .../astrocytes/astrocyte_brunel_fixed_indegree.py | 10 +--------- pynest/examples/astrocytes/astrocyte_interaction.py | 2 +- pynest/examples/astrocytes/astrocyte_single.py | 4 ++-- pynest/examples/astrocytes/astrocyte_small_network.py | 3 +-- 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py index 74673697e5..b648ed375a 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py @@ -76,7 +76,6 @@ 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 @@ -99,7 +98,7 @@ } syn_params = { - "w_a2n": 0.01, # weight of astrocyte-to-neuron connection + "w_a2n": 0.05, # 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 @@ -112,8 +111,6 @@ 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 } ############################################################################### @@ -329,7 +326,6 @@ def run_simulation(): 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 @@ -355,10 +351,6 @@ def run_simulation(): 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) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py index 167c52908c..f9f5d56351 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py @@ -76,7 +76,6 @@ 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 @@ -100,7 +99,7 @@ } syn_params = { - "w_a2n": 0.01, # weight of astrocyte-to-neuron connection + "w_a2n": 0.05, # 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 @@ -113,8 +112,6 @@ 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 } ############################################################################### @@ -326,7 +323,6 @@ def run_simulation(): 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 @@ -352,10 +348,6 @@ def run_simulation(): 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) diff --git a/pynest/examples/astrocytes/astrocyte_interaction.py b/pynest/examples/astrocytes/astrocyte_interaction.py index 4be19b1371..f75fa0ea16 100644 --- a/pynest/examples/astrocytes/astrocyte_interaction.py +++ b/pynest/examples/astrocytes/astrocyte_interaction.py @@ -85,7 +85,7 @@ # neuron parameters params_neuro = {"tau_syn_ex": 2.0} # astrocyte parameters -params_astro = {"IP3_0": 0.16} +params_astro = {"delta_IP3": 0.2} # weights of connections w_pre2astro = 1.0 w_pre2post = 1.0 diff --git a/pynest/examples/astrocytes/astrocyte_single.py b/pynest/examples/astrocytes/astrocyte_single.py index e3ef58a130..a28e31602e 100644 --- a/pynest/examples/astrocytes/astrocyte_single.py +++ b/pynest/examples/astrocytes/astrocyte_single.py @@ -68,10 +68,10 @@ # simulation time sim_time = 60000 # astrocyte parameters -params_astro = {"IP3_0": 0.16} +params_astro = {"delta_IP3": 0.2} # Poisson input for the astrocyte poisson_rate = 1.0 -poisson_weight = 0.1 +poisson_weight = 1.0 ############################################################################### # Create astrocyte and devices and connect them. diff --git a/pynest/examples/astrocytes/astrocyte_small_network.py b/pynest/examples/astrocytes/astrocyte_small_network.py index 00aecd5e47..3d1838c6c5 100644 --- a/pynest/examples/astrocytes/astrocyte_small_network.py +++ b/pynest/examples/astrocytes/astrocyte_small_network.py @@ -165,8 +165,7 @@ astrocyte_model = "astrocyte_lr_1994" astrocyte_params = { "IP3": 0.4, # IP3 initial value in µM - "delta_IP3": 2.0, # parameter determining the increase in astrocytic IP3 concentration induced by synaptic input - "tau_IP3": 10.0, # time constant of the exponential decay of astrocytic IP3 + "delta_IP3": 0.2, # parameter determining the increase in astrocytic IP3 concentration induced by synaptic input } ############################################################################### From 2e65b02d23e53f08bee39598517b6de0f26213ef Mon Sep 17 00:00:00 2001 From: HanjiaJiang Date: Mon, 26 Aug 2024 10:49:55 +0200 Subject: [PATCH 35/45] Replace astrocyte_interaction.png with new version --- .../static/img/astrocyte_interaction.png | Bin 58914 -> 54194 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/htmldoc/static/img/astrocyte_interaction.png b/doc/htmldoc/static/img/astrocyte_interaction.png index bffc8da56f4114532ba2b9bd2c3e038f8a59e7a1..1560b5b6ceaf8e05dbf9d7720817f85efb7b86b2 100644 GIT binary patch literal 54194 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

_%DMwhqcSgm=cA&SEuY6uM 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�fJNwrmn*Bd{H}SX2@^$&e1XRf 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 literal 58914 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 T8u-K$`0tR`QO#@(vupng=#&_D From a53ce913a3e0c5aa8b38e3024efe2c50f8aa04ab Mon Sep 17 00:00:00 2001 From: HanjiaJiang Date: Mon, 26 Aug 2024 14:48:09 +0200 Subject: [PATCH 36/45] Change astrocyte_lr_1994 parameter default value for delta_IP3 to physiologically plausible value --- models/astrocyte_lr_1994.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/astrocyte_lr_1994.cpp b/models/astrocyte_lr_1994.cpp index 3e448bb5cf..4db152a27f 100644 --- a/models/astrocyte_lr_1994.cpp +++ b/models/astrocyte_lr_1994.cpp @@ -123,7 +123,7 @@ nest::astrocyte_lr_1994::Parameters_::Parameters_() , Km_SERCA_( 0.1 ) // µM , SIC_scale_( 1.0 ) , SIC_th_( 0.19669 ) // µM - , delta_IP3_( 5.0 ) // µM + , delta_IP3_( 0.0002 ) // µM , k_IP3R_( 0.0002 ) // 1/(µM*ms) , rate_IP3R_( 0.006 ) // 1/ms , rate_L_( 0.00011 ) // 1/ms From f62ddac8aaab16a148be6a7822809aad6f439335 Mon Sep 17 00:00:00 2001 From: HanjiaJiang Date: Mon, 26 Aug 2024 20:39:33 +0200 Subject: [PATCH 37/45] Update test_astrocyte.dat and astrocyte_model_implementation.ipynb due to change in delta_IP3 default --- .../astrocyte_model_implementation.ipynb | 6 +- testsuite/pytests/test_astrocyte.dat | 1800 ++++++++--------- 2 files changed, 903 insertions(+), 903 deletions(-) diff --git a/doc/htmldoc/model_details/astrocyte_model_implementation.ipynb b/doc/htmldoc/model_details/astrocyte_model_implementation.ipynb index fa73230bee..4a3c12cf3c 100644 --- a/doc/htmldoc/model_details/astrocyte_model_implementation.ipynb +++ b/doc/htmldoc/model_details/astrocyte_model_implementation.ipynb @@ -170,7 +170,7 @@ " y[0, 1] = 1.0\n", " y[0, 2] = 1.0\n", " fos = [] # full output dict from odeint()\n", - " delta_ip3 = 5.0 # parameter determining the increase in IP3 induced by synaptic input\n", + " delta_ip3 = p.delta_IP3 # parameter determining the increase in IP3 induced by synaptic input\n", "\n", " # update time-step by time-step\n", " for k in range(1, n):\n", @@ -211,7 +211,7 @@ " \"Kd_IP3_2\": 0.9434,\n", " \"Km_SERCA\": 0.1,\n", " \"ratio_ER_cyt\": 0.185,\n", - " \"delta_IP3\": 5.0,\n", + " \"delta_IP3\": 0.0002,\n", " \"k_IP3R\": 0.0002,\n", " \"rate_L\": 0.00011,\n", " \"tau_IP3\": 7142.0,\n", @@ -318,7 +318,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.10" + "version": "3.12.1" } }, "nbformat": 4, diff --git a/testsuite/pytests/test_astrocyte.dat b/testsuite/pytests/test_astrocyte.dat index 5646c90265..371917ac07 100644 --- a/testsuite/pytests/test_astrocyte.dat +++ b/testsuite/pytests/test_astrocyte.dat @@ -109,903 +109,903 @@ 9.700000000000001066e+00 9.988599174812692327e-01 1.017774845693790953e+00 9.980457041121012507e-01 9.800000000000000711e+00 9.988481721152127069e-01 1.017954190638546974e+00 9.980254107730242019e-01 9.900000000000000355e+00 9.988364269136102980e-01 1.018133456792510882e+00 9.980051145154504866e-01 -1.000000000000000000e+01 5.998824681876460119e+00 1.018312644180662252e+00 9.979848153411911005e-01 -1.010000000000000142e+01 5.998742929092975018e+00 1.018583490423119020e+00 9.979645251557320851e-01 -1.020000000000000107e+01 5.998661177454162718e+00 1.018854189799071497e+00 9.979442303550029036e-01 -1.030000000000000071e+01 5.998579426960006344e+00 1.019124742361360747e+00 9.979239309423314497e-01 -1.040000000000000036e+01 5.998497677610489909e+00 1.019395148162870912e+00 9.979036269210456167e-01 -1.050000000000000000e+01 5.998415929405597424e+00 1.019665407256519662e+00 9.978833182944716329e-01 -1.060000000000000142e+01 5.998334182345312016e+00 1.019935519695264414e+00 9.978630050659342832e-01 -1.070000000000000107e+01 5.998252436429618584e+00 1.020205485532088119e+00 9.978426872387569091e-01 -1.080000000000000071e+01 5.998170691658501141e+00 1.020475304820019247e+00 9.978223648162619641e-01 -1.090000000000000036e+01 5.998088948031942813e+00 1.020744977612122462e+00 9.978020378017703473e-01 -1.100000000000000000e+01 5.998007205549928500e+00 1.021014503961490627e+00 9.977817061986018476e-01 -1.110000000000000142e+01 5.997925464212442215e+00 1.021283883921258129e+00 9.977613700100749217e-01 -1.120000000000000107e+01 5.997843724019468858e+00 1.021553117544587552e+00 9.977410292395062497e-01 -1.130000000000000071e+01 5.997761984970988891e+00 1.021822204884684115e+00 9.977206838902121788e-01 -1.140000000000000036e+01 5.997680247066988990e+00 1.022091145994782790e+00 9.977003339655068359e-01 -1.150000000000000000e+01 5.997598510307454056e+00 1.022359940928156741e+00 9.976799794687036815e-01 -1.160000000000000142e+01 5.997516774692367214e+00 1.022628589738109106e+00 9.976596204031144000e-01 -1.170000000000000107e+01 5.997435040221713365e+00 1.022897092477980996e+00 9.976392567720497873e-01 -1.180000000000000071e+01 5.997353306895473857e+00 1.023165449201144828e+00 9.976188885788187521e-01 -1.190000000000000036e+01 5.997271574713635367e+00 1.023433659961011211e+00 9.975985158267299813e-01 -1.200000000000000000e+01 5.997189843676179244e+00 1.023701724811022062e+00 9.975781385190899408e-01 -1.210000000000000142e+01 5.997108113783093053e+00 1.023969643804649943e+00 9.975577566592036538e-01 -1.220000000000000107e+01 5.997026385034359031e+00 1.024237416995406713e+00 9.975373702503753659e-01 -1.230000000000000071e+01 5.996944657429959413e+00 1.024505044436837542e+00 9.975169792959082127e-01 -1.240000000000000036e+01 5.996862930969880878e+00 1.024772526182514687e+00 9.974965837991034423e-01 -1.250000000000000000e+01 5.996781205654106550e+00 1.025039862286049708e+00 9.974761837632614148e-01 -1.260000000000000142e+01 5.996699481482620442e+00 1.025307052801084806e+00 9.974557791916810467e-01 -1.270000000000000107e+01 5.996617758455406566e+00 1.025574097781294380e+00 9.974353700876597006e-01 -1.280000000000000071e+01 5.996536036572447159e+00 1.025840997280388356e+00 9.974149564544937396e-01 -1.290000000000000036e+01 5.996454315833728010e+00 1.026107751352104636e+00 9.973945382954783057e-01 -1.300000000000000000e+01 5.996372596239233133e+00 1.026374360050218426e+00 9.973741156139069863e-01 -1.310000000000000142e+01 5.996290877788945650e+00 1.026640823428535132e+00 9.973536884130721480e-01 -1.320000000000000107e+01 5.996209160482850464e+00 1.026907141540891022e+00 9.973332566962647139e-01 -1.330000000000000071e+01 5.996127444320931588e+00 1.027173314441157670e+00 9.973128204667748298e-01 -1.340000000000000036e+01 5.996045729303173921e+00 1.027439342183237070e+00 9.972923797278905322e-01 -1.350000000000000000e+01 5.995964015429560590e+00 1.027705224821060082e+00 9.972719344828991916e-01 -1.360000000000000142e+01 5.995882302700075606e+00 1.027970962408592870e+00 9.972514847350866241e-01 -1.370000000000000107e+01 5.995800591114701206e+00 1.028236554999832020e+00 9.972310304877370912e-01 -1.380000000000000071e+01 5.995718880673422291e+00 1.028502002648803648e+00 9.972105717441338557e-01 -1.390000000000000036e+01 5.995637171376224650e+00 1.028767305409566735e+00 9.971901085075591809e-01 -1.400000000000000000e+01 5.995555463223091408e+00 1.029032463336211567e+00 9.971696407812931096e-01 -1.410000000000000142e+01 5.995473756214005689e+00 1.029297476482859519e+00 9.971491685686153517e-01 -1.420000000000000107e+01 5.995392050348952395e+00 1.029562344903661719e+00 9.971286918728036186e-01 -1.430000000000000071e+01 5.995310345627915538e+00 1.029827068652801048e+00 9.971082106971345116e-01 -1.440000000000000036e+01 5.995228642050880019e+00 1.030091647784488584e+00 9.970877250448835216e-01 -1.450000000000000000e+01 5.995146939617827186e+00 1.030356082352967606e+00 9.970672349193244743e-01 -1.460000000000000142e+01 5.995065238328743717e+00 1.030620372412511143e+00 9.970467403237300852e-01 -1.470000000000000107e+01 5.994983538183612737e+00 1.030884518017422646e+00 9.970262412613717373e-01 -1.480000000000000071e+01 5.994901839182418257e+00 1.031148519222035542e+00 9.970057377355195927e-01 -1.490000000000000036e+01 5.994820141325145180e+00 1.031412376080712567e+00 9.969852297494422588e-01 -1.500000000000000000e+01 5.994738444611776629e+00 1.031676088647845768e+00 9.969647173064072332e-01 -1.510000000000000142e+01 5.994656749042293953e+00 1.031939656977857611e+00 9.969442004096804588e-01 -1.520000000000000107e+01 5.994575054616683829e+00 1.032203081125200539e+00 9.969236790625268796e-01 -1.530000000000000071e+01 5.994493361334931159e+00 1.032466361144353639e+00 9.969031532682096630e-01 -1.540000000000000036e+01 5.994411669197019066e+00 1.032729497089826420e+00 9.968826230299910884e-01 -1.550000000000000000e+01 5.994329978202930675e+00 1.032992489016158366e+00 9.968620883511321029e-01 -1.560000000000000142e+01 5.994248288352652665e+00 1.033255336977917160e+00 9.968415492348918772e-01 -1.570000000000000107e+01 5.994166599646165494e+00 1.033518041029699130e+00 9.968210056845289158e-01 -1.580000000000000071e+01 5.994084912083454952e+00 1.033780601226128804e+00 9.968004577032998359e-01 -1.590000000000000036e+01 5.994003225664505941e+00 1.034043017621859128e+00 9.967799052944600335e-01 -1.600000000000000000e+01 5.993921540389299807e+00 1.034305290271572142e+00 9.967593484612640164e-01 -1.610000000000000142e+01 5.993839856257823229e+00 1.034567419229976970e+00 9.967387872069644050e-01 -1.619999999999999929e+01 5.993758173270058442e+00 1.034829404551812049e+00 9.967182215348128205e-01 -1.630000000000000071e+01 5.993676491425991237e+00 1.035091246291843792e+00 9.966976514480594407e-01 -1.640000000000000213e+01 5.993594810725604738e+00 1.035352944504864148e+00 9.966770769499530003e-01 -1.650000000000000000e+01 5.993513131168882957e+00 1.035614499245695264e+00 9.966564980437411236e-01 -1.660000000000000142e+01 5.993431452755809019e+00 1.035875910569184599e+00 9.966359147326703249e-01 -1.669999999999999929e+01 5.993349775486368713e+00 1.036137178530208702e+00 9.966153270199853420e-01 -1.680000000000000071e+01 5.993268099360544277e+00 1.036398303183670322e+00 9.965947349089294693e-01 -1.690000000000000213e+01 5.993186424378321497e+00 1.036659284584499296e+00 9.965741384027450023e-01 -1.700000000000000000e+01 5.993104750539682612e+00 1.036920122787654330e+00 9.965535375046731259e-01 -1.710000000000000142e+01 5.993023077844614299e+00 1.037180817848116776e+00 9.965329322179531379e-01 -1.719999999999999929e+01 5.992941406293096129e+00 1.037441369820899517e+00 9.965123225458234479e-01 -1.730000000000000071e+01 5.992859735885116557e+00 1.037701778761041860e+00 9.964917084915209111e-01 -1.740000000000000213e+01 5.992778066620657818e+00 1.037962044723603316e+00 9.964710900582809394e-01 -1.750000000000000000e+01 5.992696398499703037e+00 1.038222167763676040e+00 9.964504672493379456e-01 -1.760000000000000142e+01 5.992614731522238891e+00 1.038482147936375499e+00 9.964298400679247880e-01 -1.769999999999999929e+01 5.992533065688246730e+00 1.038741985296844472e+00 9.964092085172729929e-01 -1.780000000000000071e+01 5.992451400997711453e+00 1.039001679900253272e+00 9.963885726006128651e-01 -1.790000000000000213e+01 5.992369737450616185e+00 1.039261231801792862e+00 9.963679323211732664e-01 -1.800000000000000000e+01 5.992288075046946716e+00 1.039520641056684847e+00 9.963472876821818369e-01 -1.810000000000000142e+01 5.992206413786686170e+00 1.039779907720173924e+00 9.963266386868648850e-01 -1.819999999999999929e+01 5.992124753669817672e+00 1.040039031847530770e+00 9.963059853384470532e-01 -1.830000000000000071e+01 5.992043094696327898e+00 1.040298013494051821e+00 9.962853276401519853e-01 -1.840000000000000213e+01 5.991961436866198198e+00 1.040556852715058600e+00 9.962646655952019925e-01 -1.850000000000000000e+01 5.991879780179414361e+00 1.040815549565896614e+00 9.962439992068178318e-01 -1.860000000000000142e+01 5.991798124635958622e+00 1.041074104101937348e+00 9.962233284782190390e-01 -1.869999999999999929e+01 5.991716470235816772e+00 1.041332516378574935e+00 9.962026534126239286e-01 -1.880000000000000071e+01 5.991634816978972822e+00 1.041590786451231931e+00 9.961819740132492607e-01 -1.890000000000000213e+01 5.991553164865410785e+00 1.041848914375353985e+00 9.961612902833103522e-01 -1.900000000000000000e+01 5.991471513895112899e+00 1.042106900206410058e+00 9.961406022260217430e-01 -1.910000000000000142e+01 5.991389864068063176e+00 1.042364743999893539e+00 9.961199098445960853e-01 -1.920000000000000284e+01 5.991308215384249181e+00 1.042622445811322018e+00 9.960992131422446993e-01 -1.930000000000000071e+01 5.991226567843651374e+00 1.042880005696238843e+00 9.960785121221782390e-01 -1.940000000000000213e+01 5.991144921446253768e+00 1.043137423710209122e+00 9.960578067876052488e-01 -1.950000000000000000e+01 5.991063276192043041e+00 1.043394699908820833e+00 9.960370971417330521e-01 -1.960000000000000142e+01 5.990981632081000541e+00 1.043651834347689933e+00 9.960163831877678620e-01 -1.970000000000000284e+01 5.990899989113112944e+00 1.043908827082452362e+00 9.959956649289144481e-01 -1.980000000000000071e+01 5.990818347288360712e+00 1.044165678168768707e+00 9.959749423683763592e-01 -1.990000000000000213e+01 5.990736706606731410e+00 1.044422387662322871e+00 9.959542155093557003e-01 -2.000000000000000000e+01 5.990655067068208162e+00 1.044678955618821403e+00 9.959334843550530225e-01 -2.010000000000000142e+01 5.990573428672773204e+00 1.044935382093994836e+00 9.959127489086678775e-01 -2.020000000000000284e+01 5.990491791420411438e+00 1.045191667143594350e+00 9.958920091733981517e-01 -2.030000000000000071e+01 5.990410155311109541e+00 1.045447810823396217e+00 9.958712651524407322e-01 -2.040000000000000213e+01 5.990328520344848862e+00 1.045703813189198694e+00 9.958505168489907300e-01 -2.050000000000000000e+01 5.990246886521611636e+00 1.045959674296822683e+00 9.958297642662423677e-01 -2.060000000000000142e+01 5.990165253841384541e+00 1.046215394202112181e+00 9.958090074073882025e-01 -2.070000000000000284e+01 5.990083622304152478e+00 1.046470972960932277e+00 9.957882462756196817e-01 -2.080000000000000071e+01 5.990001991909898571e+00 1.046726410629169823e+00 9.957674808741266981e-01 -2.090000000000000213e+01 5.989920362658605058e+00 1.046981707262734984e+00 9.957467112060977010e-01 -2.100000000000000000e+01 5.989838734550256838e+00 1.047236862917560574e+00 9.957259372747202519e-01 -2.110000000000000142e+01 5.989757107584837925e+00 1.047491877649599168e+00 9.957051590831800247e-01 -2.120000000000000284e+01 5.989675481762334108e+00 1.047746751514827990e+00 9.956843766346614721e-01 -2.130000000000000071e+01 5.989593857082727624e+00 1.048001484569242914e+00 9.956635899323480476e-01 -2.140000000000000213e+01 5.989512233546003372e+00 1.048256076868863129e+00 9.956427989794217615e-01 -2.150000000000000000e+01 5.989430611152146255e+00 1.048510528469727365e+00 9.956220037790626254e-01 -2.160000000000000142e+01 5.989348989901138509e+00 1.048764839427897888e+00 9.956012043344502072e-01 -2.170000000000000284e+01 5.989267369792964146e+00 1.049019009799455615e+00 9.955804006487620761e-01 -2.180000000000000071e+01 5.989185750827609844e+00 1.049273039640504335e+00 9.955595927251748023e-01 -2.190000000000000213e+01 5.989104133005056063e+00 1.049526929007169596e+00 9.955387805668635126e-01 -2.200000000000000000e+01 5.989022516325288592e+00 1.049780677955595598e+00 9.955179641770018906e-01 -2.210000000000000142e+01 5.988940900788292332e+00 1.050034286541947415e+00 9.954971435587622874e-01 -2.220000000000000284e+01 5.988859286394048631e+00 1.050287754822410990e+00 9.954763187153156112e-01 -2.230000000000000071e+01 5.988777673142545055e+00 1.050541082853193364e+00 9.954554896498317706e-01 -2.240000000000000213e+01 5.988696061033761175e+00 1.050794270690521781e+00 9.954346563654788982e-01 -2.250000000000000000e+01 5.988614450067685446e+00 1.051047318390642582e+00 9.954138188654242381e-01 -2.260000000000000142e+01 5.988532840244299216e+00 1.051300226009823424e+00 9.953929771528332582e-01 -2.270000000000000284e+01 5.988451231563588273e+00 1.051552993604352171e+00 9.953721312308699831e-01 -2.280000000000000071e+01 5.988369624025535742e+00 1.051805621230534893e+00 9.953512811026974383e-01 -2.290000000000000213e+01 5.988288017630125637e+00 1.052058108944697867e+00 9.953304267714774278e-01 -2.300000000000000000e+01 5.988206412377340193e+00 1.052310456803188687e+00 9.953095682403696465e-01 -2.310000000000000142e+01 5.988124808267166976e+00 1.052562664862372710e+00 9.952887055125332338e-01 -2.320000000000000284e+01 5.988043205299587335e+00 1.052814733178635054e+00 9.952678385911254422e-01 -2.330000000000000071e+01 5.987961603474587058e+00 1.053066661808381044e+00 9.952469674793026355e-01 -2.340000000000000213e+01 5.987880002792149270e+00 1.053318450808033102e+00 9.952260921802194016e-01 -2.350000000000000000e+01 5.987798403252257096e+00 1.053570100234034745e+00 9.952052126970291068e-01 -2.360000000000000142e+01 5.987716804854897212e+00 1.053821610142848586e+00 9.951843290328838965e-01 -2.370000000000000284e+01 5.987635207600051857e+00 1.054072980590953668e+00 9.951634411909342504e-01 -2.380000000000000071e+01 5.987553611487705041e+00 1.054324211634851238e+00 9.951425491743295382e-01 -2.390000000000000213e+01 5.987472016517840778e+00 1.054575303331057201e+00 9.951216529862176863e-01 -2.400000000000000000e+01 5.987390422690442193e+00 1.054826255736109664e+00 9.951007526297452888e-01 -2.410000000000000142e+01 5.987308830005495075e+00 1.055077068906561832e+00 9.950798481080576074e-01 -2.420000000000000284e+01 5.987227238462984324e+00 1.055327742898987120e+00 9.950589394242984609e-01 -2.430000000000000071e+01 5.987145648062891290e+00 1.055578277769977591e+00 9.950380265816102243e-01 -2.440000000000000213e+01 5.987064058805199984e+00 1.055828673576141963e+00 9.950171095831341628e-01 -2.450000000000000000e+01 5.986982470689895308e+00 1.056078930374107383e+00 9.949961884320100980e-01 -2.460000000000000142e+01 5.986900883716963939e+00 1.056329048220518541e+00 9.949752631313762974e-01 -2.470000000000000284e+01 5.986819297886386337e+00 1.056579027172039220e+00 9.949543336843699182e-01 -2.480000000000000071e+01 5.986737713198150068e+00 1.056828867285348528e+00 9.949334000941266742e-01 -2.490000000000000213e+01 5.986656129652234704e+00 1.057078568617145109e+00 9.949124623637806142e-01 -2.500000000000000000e+01 5.986574547248627809e+00 1.057328131224144707e+00 9.948915204964645653e-01 -2.510000000000000142e+01 5.986492965987311621e+00 1.057577555163079053e+00 9.948705744953104668e-01 -2.520000000000000284e+01 5.986411385868271040e+00 1.057826840490698306e+00 9.948496243634483704e-01 -2.530000000000000071e+01 5.986329806891490968e+00 1.058075987263769058e+00 9.948286701040072177e-01 -2.540000000000000213e+01 5.986248229056952752e+00 1.058324995539075664e+00 9.948077117201143960e-01 -2.550000000000000000e+01 5.986166652364643070e+00 1.058573865373417799e+00 9.947867492148958490e-01 -2.560000000000000142e+01 5.986085076814545047e+00 1.058822596823612461e+00 9.947657825914766327e-01 -2.570000000000000284e+01 5.986003502406640919e+00 1.059071189946494185e+00 9.947448118529798045e-01 -2.580000000000000071e+01 5.985921929140914699e+00 1.059319644798913496e+00 9.947238370025276444e-01 -2.590000000000000213e+01 5.985840357017353952e+00 1.059567961437737349e+00 9.947028580432406564e-01 -2.600000000000000000e+01 5.985758786035940027e+00 1.059816139919848688e+00 9.946818749782381230e-01 -2.610000000000000142e+01 5.985677216196657824e+00 1.060064180302147552e+00 9.946608878106379947e-01 -2.620000000000000284e+01 5.985595647499492244e+00 1.060312082641549081e+00 9.946398965435567785e-01 -2.630000000000000071e+01 5.985514079944424637e+00 1.060559846994984845e+00 9.946189011801098712e-01 -2.640000000000000213e+01 5.985432513531442567e+00 1.060807473419402402e+00 9.945979017234108932e-01 -2.650000000000000000e+01 5.985350948260527382e+00 1.061054961971764632e+00 9.945768981765722438e-01 -2.660000000000000142e+01 5.985269384131663983e+00 1.061302312709050844e+00 9.945558905427046570e-01 -2.670000000000000284e+01 5.985187821144837272e+00 1.061549525688254558e+00 9.945348788249182004e-01 -2.680000000000000071e+01 5.985106259300031262e+00 1.061796600966386839e+00 9.945138630263212765e-01 -2.690000000000000213e+01 5.985024698597229076e+00 1.062043538600472070e+00 9.944928431500205113e-01 -2.700000000000000000e+01 5.984943139036413839e+00 1.062290338647551513e+00 9.944718191991215317e-01 -2.710000000000000142e+01 5.984861580617572230e+00 1.062537001164680639e+00 9.944507911767286323e-01 -2.720000000000000284e+01 5.984780023340685595e+00 1.062783526208929574e+00 9.944297590859445535e-01 -2.730000000000000071e+01 5.984698467205737948e+00 1.063029913837384433e+00 9.944087229298711472e-01 -2.740000000000000213e+01 5.984616912212716855e+00 1.063276164107145538e+00 9.943876827116079342e-01 -2.750000000000000000e+01 5.984535358361603663e+00 1.063522277075329203e+00 9.943666384342541020e-01 -2.760000000000000142e+01 5.984453805652384162e+00 1.063768252799064618e+00 9.943455901009066178e-01 -2.770000000000000284e+01 5.984372254085038811e+00 1.064014091335495182e+00 9.943245377146614494e-01 -2.780000000000000071e+01 5.984290703659555177e+00 1.064259792741780730e+00 9.943034812786133436e-01 -2.790000000000000213e+01 5.984209154375915496e+00 1.064505357075095082e+00 9.942824207958554927e-01 -2.800000000000000000e+01 5.984127606234103780e+00 1.064750784392625604e+00 9.942613562694799789e-01 -2.810000000000000142e+01 5.984046059234105819e+00 1.064996074751572541e+00 9.942402877025771080e-01 -2.820000000000000284e+01 5.983964513375902960e+00 1.065241228209152791e+00 9.942192150982358534e-01 -2.830000000000000071e+01 5.983882968659480994e+00 1.065486244822595463e+00 9.941981384595440785e-01 -2.840000000000000213e+01 5.983801425084823045e+00 1.065731124649143435e+00 9.941770577895879812e-01 -2.850000000000000000e+01 5.983719882651914013e+00 1.065975867746054462e+00 9.941559730914525383e-01 -2.860000000000000142e+01 5.983638341360738799e+00 1.066220474170598509e+00 9.941348843682215053e-01 -2.870000000000000284e+01 5.983556801211279641e+00 1.066464943980060642e+00 9.941137916229770832e-01 -2.880000000000000071e+01 5.983475262203521439e+00 1.066709277231738140e+00 9.940926948587999190e-01 -2.890000000000000213e+01 5.983393724337449093e+00 1.066953473982941603e+00 9.940715940787695493e-01 -2.900000000000000000e+01 5.983312187613043953e+00 1.067197534290995398e+00 9.940504892859642894e-01 -2.910000000000000142e+01 5.983230652030293584e+00 1.067441458213237215e+00 9.940293804834604563e-01 -2.920000000000000284e+01 5.983149117589178445e+00 1.067685245807017180e+00 9.940082676743337009e-01 -2.930000000000000071e+01 5.983067584289685215e+00 1.067928897129698740e+00 9.939871508616580087e-01 -2.940000000000000213e+01 5.982986052131798793e+00 1.068172412238657776e+00 9.939660300485059219e-01 -2.950000000000000000e+01 5.982904521115500529e+00 1.068415791191282826e+00 9.939449052379485394e-01 -2.960000000000000142e+01 5.982822991240776211e+00 1.068659034044975531e+00 9.939237764330557390e-01 -2.970000000000000284e+01 5.982741462507607189e+00 1.068902140857149963e+00 9.939026436368960660e-01 -2.980000000000000071e+01 5.982659934915981026e+00 1.069145111685232186e+00 9.938815068525362895e-01 -2.990000000000000213e+01 5.982578408465879072e+00 1.069387946586661808e+00 9.938603660830424014e-01 -3.000000000000000000e+01 5.982496883157286227e+00 1.069630645618889098e+00 9.938392213314788393e-01 -3.010000000000000142e+01 5.982415358990189169e+00 1.069873208839376755e+00 9.938180726009081534e-01 -3.020000000000000284e+01 5.982333835964569246e+00 1.070115636305601248e+00 9.937969198943921167e-01 -3.030000000000000071e+01 5.982252314080411359e+00 1.070357928075048592e+00 9.937757632149908371e-01 -3.040000000000000213e+01 5.982170793337698633e+00 1.070600084205218350e+00 9.937546025657633120e-01 -3.050000000000000000e+01 5.982089273736415080e+00 1.070842104753621182e+00 9.937334379497665404e-01 -3.060000000000000142e+01 5.982007755276546490e+00 1.071083989777779300e+00 9.937122693700569664e-01 -3.070000000000000284e+01 5.981926237958073322e+00 1.071325739335226679e+00 9.936910968296891467e-01 -3.080000000000000071e+01 5.981844721780982255e+00 1.071567353483508844e+00 9.936699203317163054e-01 -3.090000000000000213e+01 5.981763206745259076e+00 1.071808832280182866e+00 9.936487398791904457e-01 -3.100000000000000000e+01 5.981681692850886023e+00 1.072050175782816250e+00 9.936275554751620165e-01 -3.110000000000000142e+01 5.981600180097846220e+00 1.072291384048988272e+00 9.936063671226799121e-01 -3.120000000000000284e+01 5.981518668486123680e+00 1.072532457136289974e+00 9.935851748247923609e-01 -3.130000000000000071e+01 5.981437158015702416e+00 1.072773395102321725e+00 9.935639785845453709e-01 -3.140000000000000213e+01 5.981355648686568216e+00 1.073014198004696773e+00 9.935427784049840616e-01 -3.150000000000000000e+01 5.981274140498704206e+00 1.073254865901037913e+00 9.935215742891519985e-01 -3.160000000000000142e+01 5.981192633452095286e+00 1.073495398848978821e+00 9.935003662400915259e-01 -3.170000000000000284e+01 5.981111127546723694e+00 1.073735796906164497e+00 9.934791542608434334e-01 -3.180000000000000071e+01 5.981029622782576105e+00 1.073976060130248600e+00 9.934579383544471787e-01 -3.190000000000000213e+01 5.980948119159634757e+00 1.074216188578897446e+00 9.934367185239406650e-01 -3.200000000000000000e+01 5.980866616677882774e+00 1.074456182309786900e+00 9.934154947723610185e-01 -3.210000000000000142e+01 5.980785115337305946e+00 1.074696041380601930e+00 9.933942671027432558e-01 -3.220000000000000284e+01 5.980703615137887397e+00 1.074935765849039937e+00 9.933730355181213945e-01 -3.230000000000000426e+01 5.980622116079612915e+00 1.075175355772805874e+00 9.933518000215278976e-01 -3.239999999999999858e+01 5.980540618162464739e+00 1.075414811209616683e+00 9.933305606159940071e-01 -3.250000000000000000e+01 5.980459121386425103e+00 1.075654132217198189e+00 9.933093173045495217e-01 -3.260000000000000142e+01 5.980377625751482462e+00 1.075893318853286429e+00 9.932880700902227966e-01 -3.270000000000000284e+01 5.980296131257618164e+00 1.076132371175626545e+00 9.932668189760409660e-01 -3.280000000000000426e+01 5.980214637904817110e+00 1.076371289241974116e+00 9.932455639650294987e-01 -3.289999999999999858e+01 5.980133145693062424e+00 1.076610073110093602e+00 9.932243050602127532e-01 -3.300000000000000000e+01 5.980051654622340784e+00 1.076848722837759009e+00 9.932030422646136447e-01 -3.310000000000000142e+01 5.979970164692631762e+00 1.077087238482753895e+00 9.931817755812535342e-01 -3.320000000000000284e+01 5.979888675903922923e+00 1.077325620102871140e+00 9.931605050131524504e-01 -3.330000000000000426e+01 5.979807188256197392e+00 1.077563867755912286e+00 9.931392305633294226e-01 -3.339999999999999858e+01 5.979725701749438294e+00 1.077801981499688422e+00 9.931179522348014821e-01 -3.350000000000000000e+01 5.979644216383629640e+00 1.078039961392018853e+00 9.930966700305846606e-01 -3.360000000000000142e+01 5.979562732158758109e+00 1.078277807490732432e+00 9.930753839536935468e-01 -3.370000000000000284e+01 5.979481249074805937e+00 1.078515519853667337e+00 9.930540940071413969e-01 -3.380000000000000426e+01 5.979399767131756249e+00 1.078753098538669519e+00 9.930328001939396909e-01 -3.389999999999999858e+01 5.979318286329593946e+00 1.078990543603593810e+00 9.930115025170990206e-01 -3.400000000000000000e+01 5.979236806668304816e+00 1.079227855106303036e+00 9.929902009796286455e-01 -3.410000000000000142e+01 5.979155328147871096e+00 1.079465033104670901e+00 9.929688955845360487e-01 -3.420000000000000284e+01 5.979073850768276799e+00 1.079702077656575554e+00 9.929475863348272702e-01 -3.430000000000000426e+01 5.978992374529505938e+00 1.079938988819906243e+00 9.929262732335074615e-01 -3.439999999999999858e+01 5.978910899431543413e+00 1.080175766652560210e+00 9.929049562835796650e-01 -3.450000000000000000e+01 5.978829425474375014e+00 1.080412411212441359e+00 9.928836354880461457e-01 -3.460000000000000142e+01 5.978747952657981202e+00 1.080648922557462921e+00 9.928623108499078365e-01 -3.470000000000000284e+01 5.978666480982346876e+00 1.080885300745545674e+00 9.928409823721640048e-01 -3.480000000000000426e+01 5.978585010447457826e+00 1.081121545834617503e+00 9.928196500578122530e-01 -3.489999999999999858e+01 5.978503541053295400e+00 1.081357657882614953e+00 9.927983139098494059e-01 -3.500000000000000000e+01 5.978422072799847165e+00 1.081593636947482118e+00 9.927769739312706232e-01 -3.510000000000000142e+01 5.978340605687094467e+00 1.081829483087170418e+00 9.927556301250696214e-01 -3.520000000000000284e+01 5.978259139715020432e+00 1.082065196359638382e+00 9.927342824942390065e-01 -3.530000000000000426e+01 5.978177674883611736e+00 1.082300776822852972e+00 9.927129310417693864e-01 -3.539999999999999858e+01 5.978096211192851506e+00 1.082536224534788039e+00 9.926915757706504806e-01 -3.550000000000000000e+01 5.978014748642725529e+00 1.082771539553424089e+00 9.926702166838705654e-01 -3.560000000000000142e+01 5.977933287233214266e+00 1.083006721936749406e+00 9.926488537844165849e-01 -3.570000000000000284e+01 5.977851826964303505e+00 1.083241771742758930e+00 9.926274870752735957e-01 -3.580000000000000426e+01 5.977770367835978149e+00 1.083476689029454265e+00 9.926061165594260993e-01 -3.589999999999999858e+01 5.977688909848221321e+00 1.083711473854845453e+00 9.925847422398565989e-01 -3.600000000000000000e+01 5.977607453001017923e+00 1.083946126276947641e+00 9.925633641195464874e-01 -3.610000000000000142e+01 5.977525997294351967e+00 1.084180646353783528e+00 9.925419822014751592e-01 -3.620000000000000284e+01 5.977444542728205690e+00 1.084415034143382472e+00 9.925205964886214538e-01 -3.630000000000000426e+01 5.977363089302565768e+00 1.084649289703779385e+00 9.924992069839626563e-01 -3.639999999999999858e+01 5.977281637017414440e+00 1.084883413093016946e+00 9.924778136904743864e-01 -3.650000000000000000e+01 5.977200185872735716e+00 1.085117404369143612e+00 9.924564166111309316e-01 -3.660000000000000142e+01 5.977118735868515387e+00 1.085351263590214499e+00 9.924350157489052471e-01 -3.670000000000000284e+01 5.977037287004736577e+00 1.085584990814290718e+00 9.924136111067688448e-01 -3.680000000000000426e+01 5.976955839281382410e+00 1.085818586099438932e+00 9.923922026876920155e-01 -3.689999999999999858e+01 5.976874392698437788e+00 1.086052049503733130e+00 9.923707904946436065e-01 -3.700000000000000000e+01 5.976792947255885835e+00 1.086285381085252411e+00 9.923493745305909108e-01 -3.710000000000000142e+01 5.976711502953712341e+00 1.086518580902081643e+00 9.923279547984997784e-01 -3.720000000000000284e+01 5.976630059791901317e+00 1.086751649012311249e+00 9.923065313013350597e-01 -3.730000000000000426e+01 5.976548617770434113e+00 1.086984585474038978e+00 9.922851040420599400e-01 -3.739999999999999858e+01 5.976467176889297406e+00 1.087217390345366574e+00 9.922636730236362723e-01 -3.750000000000000000e+01 5.976385737148476096e+00 1.087450063684402668e+00 9.922422382490243553e-01 -3.760000000000000142e+01 5.976304298547952421e+00 1.087682605549259440e+00 9.922207997211831554e-01 -3.770000000000000284e+01 5.976222861087709504e+00 1.087915015998055956e+00 9.921993574430707508e-01 -3.780000000000000426e+01 5.976141424767734023e+00 1.088147295088917055e+00 9.921779114176431102e-01 -3.789999999999999858e+01 5.976059989588007326e+00 1.088379442879971348e+00 9.921564616478552034e-01 -3.800000000000000000e+01 5.975978555548513427e+00 1.088611459429353667e+00 9.921350081366606677e-01 -3.810000000000000142e+01 5.975897122649239890e+00 1.088843344795203061e+00 9.921135508870113640e-01 -3.820000000000000284e+01 5.975815690890167176e+00 1.089075099035664795e+00 9.920920899018581540e-01 -3.830000000000000426e+01 5.975734260271281073e+00 1.089306722208887912e+00 9.920706251841505674e-01 -3.840000000000000568e+01 5.975652830792567372e+00 1.089538214373027447e+00 9.920491567368363572e-01 -3.850000000000000000e+01 5.975571402454006531e+00 1.089769575586241546e+00 9.920276845628619444e-01 -3.860000000000000142e+01 5.975489975255586117e+00 1.090000805906694570e+00 9.920062086651725286e-01 -3.870000000000000284e+01 5.975408549197288366e+00 1.090231905392554879e+00 9.919847290467120882e-01 -3.880000000000000426e+01 5.975327124279095514e+00 1.090462874101994828e+00 9.919632457104230472e-01 -3.890000000000000568e+01 5.975245700500994239e+00 1.090693712093192547e+00 9.919417586592461644e-01 -3.900000000000000000e+01 5.975164277862968554e+00 1.090924419424328828e+00 9.919202678961210884e-01 -3.910000000000000142e+01 5.975082856365001582e+00 1.091154996153590240e+00 9.918987734239861354e-01 -3.920000000000000284e+01 5.975001436007078226e+00 1.091385442339166900e+00 9.918772752457780673e-01 -3.930000000000000426e+01 5.974920016789181609e+00 1.091615758039253148e+00 9.918557733644324248e-01 -3.940000000000000568e+01 5.974838598711294857e+00 1.091845943312047096e+00 9.918342677828830833e-01 -3.950000000000000000e+01 5.974757181773402870e+00 1.092075998215751742e+00 9.918127585040629191e-01 -3.960000000000000142e+01 5.974675765975491437e+00 1.092305922808572749e+00 9.917912455309030317e-01 -3.970000000000000284e+01 5.974594351317544572e+00 1.092535717148720442e+00 9.917697288663333000e-01 -3.980000000000000426e+01 5.974512937799545398e+00 1.092765381294408922e+00 9.917482085132823810e-01 -3.990000000000000568e+01 5.974431525421476152e+00 1.092994915303855175e+00 9.917266844746772669e-01 -4.000000000000000000e+01 5.974350114183323512e+00 1.093224319235280850e+00 9.917051567534436174e-01 -4.010000000000000142e+01 5.974268704085069714e+00 1.093453593146910263e+00 9.916836253525056488e-01 -4.020000000000000284e+01 5.974187295126700548e+00 1.093682737096971058e+00 9.916620902747865784e-01 -4.030000000000000426e+01 5.974105887308198248e+00 1.093911751143695321e+00 9.916405515232077361e-01 -4.040000000000000568e+01 5.974024480629547718e+00 1.094140635345317136e+00 9.916190091006892304e-01 -4.050000000000000000e+01 5.973943075090734744e+00 1.094369389760074807e+00 9.915974630101499487e-01 -4.060000000000000142e+01 5.973861670691739789e+00 1.094598014446209078e+00 9.915759132545070020e-01 -4.070000000000000284e+01 5.973780267432549529e+00 1.094826509461964470e+00 9.915543598366767242e-01 -4.080000000000000426e+01 5.973698865313147088e+00 1.095054874865587946e+00 9.915328027595733396e-01 -4.090000000000000568e+01 5.973617464333516480e+00 1.095283110715329356e+00 9.915112420261101844e-01 -4.100000000000000000e+01 5.973536064493642606e+00 1.095511217069441434e+00 9.914896776391991517e-01 -4.110000000000000142e+01 5.973454665793511253e+00 1.095739193986179805e+00 9.914681096017504691e-01 -4.120000000000000284e+01 5.973373268233101996e+00 1.095967041523803198e+00 9.914465379166733650e-01 -4.130000000000000426e+01 5.973291871812402398e+00 1.096194759740571900e+00 9.914249625868752913e-01 -4.140000000000000568e+01 5.973210476531396473e+00 1.096422348694749971e+00 9.914033836152627011e-01 -4.150000000000000000e+01 5.973129082390064681e+00 1.096649808444603247e+00 9.913818010047402707e-01 -4.160000000000000142e+01 5.973047689388395476e+00 1.096877139048399563e+00 9.913602147582114554e-01 -4.170000000000000284e+01 5.972966297526371093e+00 1.097104340564409419e+00 9.913386248785782673e-01 -4.180000000000000426e+01 5.972884906803975547e+00 1.097331413050905979e+00 9.913170313687416080e-01 -4.190000000000000568e+01 5.972803517221193736e+00 1.097558356566164184e+00 9.912954342316006029e-01 -4.200000000000000000e+01 5.972722128778008788e+00 1.097785171168460971e+00 9.912738334700531562e-01 -4.210000000000000142e+01 5.972640741474404713e+00 1.098011856916075724e+00 9.912522290869958397e-01 -4.220000000000000284e+01 5.972559355310366414e+00 1.098238413867290042e+00 9.912306210853236710e-01 -4.230000000000000426e+01 5.972477970285877014e+00 1.098464842080385973e+00 9.912090094679304464e-01 -4.240000000000000568e+01 5.972396586400921414e+00 1.098691141613648670e+00 9.911873942377084079e-01 -4.250000000000000000e+01 5.972315203655482740e+00 1.098917312525364398e+00 9.911657753975486873e-01 -4.260000000000000142e+01 5.972233822049545005e+00 1.099143354873822087e+00 9.911441529503408621e-01 -4.270000000000000284e+01 5.972152441583092219e+00 1.099369268717310666e+00 9.911225268989727333e-01 -4.280000000000000426e+01 5.972071062256111063e+00 1.099595054114121950e+00 9.911008972463314359e-01 -4.290000000000000568e+01 5.971989684068583770e+00 1.099820711122547978e+00 9.910792639953021066e-01 -4.300000000000000000e+01 5.971908307020493467e+00 1.100046239800883674e+00 9.910576271487687716e-01 -4.310000000000000142e+01 5.971826931111824166e+00 1.100271640207424406e+00 9.910359867096140141e-01 -4.320000000000000284e+01 5.971745556342561656e+00 1.100496912400466210e+00 9.910143426807191958e-01 -4.330000000000000426e+01 5.971664182712688174e+00 1.100722056438307339e+00 9.909926950649640132e-01 -4.340000000000000568e+01 5.971582810222191284e+00 1.100947072379246716e+00 9.909710438652270525e-01 -4.350000000000000000e+01 5.971501438871050560e+00 1.101171960281584150e+00 9.909493890843851238e-01 -4.360000000000000142e+01 5.971420068659253566e+00 1.101396720203620561e+00 9.909277307253140377e-01 -4.370000000000000284e+01 5.971338699586782539e+00 1.101621352203657533e+00 9.909060687908880505e-01 -4.380000000000000426e+01 5.971257331653622380e+00 1.101845856339997987e+00 9.908844032839798643e-01 -4.390000000000000568e+01 5.971175964859757102e+00 1.102070232670945504e+00 9.908627342074611821e-01 -4.400000000000000000e+01 5.971094599205169828e+00 1.102294481254803671e+00 9.908410615642018193e-01 -4.410000000000000142e+01 5.971013234689846350e+00 1.102518602149876958e+00 9.908193853570708143e-01 -4.420000000000000284e+01 5.970931871313768902e+00 1.102742595414470950e+00 9.907977055889352069e-01 -4.430000000000000426e+01 5.970850509076922386e+00 1.102966461106891005e+00 9.907760222626610380e-01 -4.440000000000000568e+01 5.970769147979289926e+00 1.103190199285443152e+00 9.907543353811127940e-01 -4.450000000000000000e+01 5.970687788020857312e+00 1.103413810008433638e+00 9.907326449471537400e-01 -4.460000000000000142e+01 5.970606429201606780e+00 1.103637293334169378e+00 9.907109509636453648e-01 -4.470000000000000284e+01 5.970525071521525007e+00 1.103860649320957288e+00 9.906892534334482692e-01 -4.480000000000000426e+01 5.970443714980594230e+00 1.104083878027104060e+00 9.906675523594213884e-01 -4.490000000000000568e+01 5.970362359578798461e+00 1.104306979510916165e+00 9.906458477444223254e-01 -4.500000000000000000e+01 5.970281005316122602e+00 1.104529953830700961e+00 9.906241395913070180e-01 -4.510000000000000142e+01 5.970199652192549777e+00 1.104752801044765809e+00 9.906024279029302937e-01 -4.520000000000000284e+01 5.970118300208063999e+00 1.104975521211416956e+00 9.905807126821458697e-01 -4.530000000000000426e+01 5.970036949362651946e+00 1.105198114388961095e+00 9.905589939318056869e-01 -4.540000000000000568e+01 5.969955599656294964e+00 1.105420580635704697e+00 9.905372716547601319e-01 -4.550000000000000000e+01 5.969874251088977068e+00 1.105642920009953123e+00 9.905155458538587032e-01 -4.560000000000000142e+01 5.969792903660684047e+00 1.105865132570012621e+00 9.904938165319493448e-01 -4.570000000000000284e+01 5.969711557371399913e+00 1.106087218374187664e+00 9.904720836918782245e-01 -4.580000000000000426e+01 5.969630212221108678e+00 1.106309177480782946e+00 9.904503473364905108e-01 -4.590000000000000568e+01 5.969548868209791692e+00 1.106531009948102051e+00 9.904286074686300401e-01 -4.600000000000000000e+01 5.969467525337435632e+00 1.106752715834448120e+00 9.904068640911388721e-01 -4.610000000000000142e+01 5.969386183604023621e+00 1.106974295198124292e+00 9.903851172068580677e-01 -4.620000000000000284e+01 5.969304843009540562e+00 1.107195748097431487e+00 9.903633668186271333e-01 -4.630000000000000426e+01 5.969223503553970467e+00 1.107417074590670625e+00 9.903416129292840209e-01 -4.640000000000000568e+01 5.969142165237296460e+00 1.107638274736141515e+00 9.903198555416657944e-01 -4.650000000000000000e+01 5.969060828059504331e+00 1.107859348592142856e+00 9.902980946586075195e-01 -4.660000000000000142e+01 5.968979492020576316e+00 1.108080296216972682e+00 9.902763302829433734e-01 -4.670000000000000284e+01 5.968898157120498205e+00 1.108301117668927249e+00 9.902545624175058681e-01 -4.680000000000000426e+01 5.968816823359252233e+00 1.108521813006302370e+00 9.902327910651261833e-01 -4.690000000000000568e+01 5.968735490736823301e+00 1.108742382287392303e+00 9.902110162286340556e-01 -4.700000000000000000e+01 5.968654159253194535e+00 1.108962825570489308e+00 9.901892379108577780e-01 -4.710000000000000142e+01 5.968572828908352612e+00 1.109183142913884978e+00 9.901674561146246445e-01 -4.720000000000000284e+01 5.968491499702278880e+00 1.109403334375869132e+00 9.901456708427601727e-01 -4.730000000000000426e+01 5.968410171634959127e+00 1.109623400014730477e+00 9.901238820980885480e-01 -4.740000000000000568e+01 5.968328844706378256e+00 1.109843339888755720e+00 9.901020898834328454e-01 -4.750000000000000000e+01 5.968247518916519390e+00 1.110063154056229795e+00 9.900802942016143637e-01 -4.760000000000000142e+01 5.968166194265366542e+00 1.110282842575436524e+00 9.900584950554531805e-01 -4.770000000000000284e+01 5.968084870752902837e+00 1.110502405504657508e+00 9.900366924477679298e-01 -4.780000000000000426e+01 5.968003548379113177e+00 1.110721842902172574e+00 9.900148863813760247e-01 -4.790000000000000568e+01 5.967922227143979796e+00 1.110941154826259103e+00 9.899930768590934349e-01 -4.800000000000000000e+01 5.967840907047488486e+00 1.111160341335193369e+00 9.899712638837346868e-01 -4.810000000000000142e+01 5.967759588089625034e+00 1.111379402487249646e+00 9.899494474581126413e-01 -4.820000000000000284e+01 5.967678270270372565e+00 1.111598338340699765e+00 9.899276275850393825e-01 -4.830000000000000426e+01 5.967596953589713316e+00 1.111817148953813117e+00 9.899058042673249957e-01 -4.840000000000000568e+01 5.967515638047632187e+00 1.112035834384857758e+00 9.898839775077787895e-01 -4.850000000000000000e+01 5.967434323644114968e+00 1.112254394692098414e+00 9.898621473092080736e-01 -4.860000000000000142e+01 5.967353010379143896e+00 1.112472829933797813e+00 9.898403136744192699e-01 -4.870000000000000284e+01 5.967271698252702983e+00 1.112691140168216686e+00 9.898184766062170237e-01 -4.880000000000000426e+01 5.967190387264777129e+00 1.112909325453612652e+00 9.897966361074049813e-01 -4.890000000000000568e+01 5.967109077415350349e+00 1.113127385848241557e+00 9.897747921807850124e-01 -4.900000000000000000e+01 5.967027768704405766e+00 1.113345321410355915e+00 9.897529448291579879e-01 -4.910000000000000142e+01 5.966946461131930057e+00 1.113563132198206684e+00 9.897310940553230019e-01 -4.920000000000000284e+01 5.966865154697905460e+00 1.113780818270040829e+00 9.897092398620780385e-01 -4.930000000000000426e+01 5.966783849402315099e+00 1.113998379684102868e+00 9.896873822522196384e-01 -4.940000000000000568e+01 5.966702545245143874e+00 1.114215816498635325e+00 9.896655212285425662e-01 -4.950000000000000000e+01 5.966621242226376687e+00 1.114433128771876724e+00 9.896436567938411422e-01 -4.960000000000000142e+01 5.966539940345995774e+00 1.114650316562063370e+00 9.896217889509072441e-01 -4.970000000000000284e+01 5.966458639603987812e+00 1.114867379927428459e+00 9.895999177025320837e-01 -4.980000000000000426e+01 5.966377340000335039e+00 1.115084318926201634e+00 9.895780430515052073e-01 -4.990000000000000568e+01 5.966296041535023242e+00 1.115301133616609874e+00 9.895561650006146071e-01 -5.000000000000000000e+01 5.966214744208034659e+00 1.115517824056877050e+00 9.895342835526472758e-01 -5.010000000000000142e+01 5.966133448019354191e+00 1.115734390305223478e+00 9.895123987103886520e-01 -5.020000000000000284e+01 5.966052152968966737e+00 1.115950832419865923e+00 9.894905104766227311e-01 -5.030000000000000426e+01 5.965970859056854536e+00 1.116167150459018487e+00 9.894686188541322869e-01 -5.040000000000000568e+01 5.965889566283003376e+00 1.116383344480891715e+00 9.894467238456982061e-01 -5.050000000000000000e+01 5.965808274647395493e+00 1.116599414543691715e+00 9.894248254541008203e-01 -5.060000000000000142e+01 5.965726984150015788e+00 1.116815360705622373e+00 9.894029236821183515e-01 -5.070000000000000284e+01 5.965645694790849163e+00 1.117031183024883578e+00 9.893810185325279116e-01 -5.080000000000000426e+01 5.965564406569880518e+00 1.117246881559670557e+00 9.893591100081052803e-01 -5.090000000000000568e+01 5.965483119487092978e+00 1.117462456368175872e+00 9.893371981116251268e-01 -5.100000000000000000e+01 5.965401833542469667e+00 1.117677907508588087e+00 9.893152828458597892e-01 -5.110000000000000142e+01 5.965320548735996375e+00 1.117893235039091770e+00 9.892933642135812722e-01 -5.120000000000000284e+01 5.965239265067656227e+00 1.118108439017867495e+00 9.892714422175596933e-01 -5.130000000000000426e+01 5.965157982537431458e+00 1.118323519503092722e+00 9.892495168605638378e-01 -5.140000000000000568e+01 5.965076701145309634e+00 1.118538476552939365e+00 9.892275881453611586e-01 -5.150000000000000000e+01 5.964995420891272104e+00 1.118753310225576669e+00 9.892056560747178873e-01 -5.160000000000000142e+01 5.964914141775305545e+00 1.118968020579168998e+00 9.891837206513983682e-01 -5.170000000000000284e+01 5.964832863797390416e+00 1.119182607671876939e+00 9.891617818781659466e-01 -5.180000000000000426e+01 5.964751586957514284e+00 1.119397071561856416e+00 9.891398397577826351e-01 -5.190000000000000568e+01 5.964670311255661161e+00 1.119611412307259135e+00 9.891178942930087814e-01 -5.200000000000000000e+01 5.964589036691812396e+00 1.119825629966233027e+00 9.890959454866036227e-01 -5.210000000000000142e+01 5.964507763265953777e+00 1.120039724596921138e+00 9.890739933413250640e-01 -5.220000000000000284e+01 5.964426490978069317e+00 1.120253696257462073e+00 9.890520378599292339e-01 -5.230000000000000426e+01 5.964345219828144806e+00 1.120467545005990218e+00 9.890300790451712620e-01 -5.240000000000000568e+01 5.964263949816160704e+00 1.120681270900634408e+00 9.890081168998046124e-01 -5.250000000000000000e+01 5.964182680942101911e+00 1.120894873999520147e+00 9.889861514265815279e-01 -5.260000000000000142e+01 5.964101413205954216e+00 1.121108354360767168e+00 9.889641826282530301e-01 -5.270000000000000284e+01 5.964020146607700745e+00 1.121321712042490759e+00 9.889422105075684755e-01 -5.280000000000000426e+01 5.963938881147326398e+00 1.121534947102801993e+00 9.889202350672757769e-01 -5.290000000000000568e+01 5.963857616824815189e+00 1.121748059599806391e+00 9.888982563101218481e-01 -5.300000000000000000e+01 5.963776353640150241e+00 1.121961049591605253e+00 9.888762742388517157e-01 -5.310000000000000142e+01 5.963695091593316455e+00 1.122173917136293664e+00 9.888542888562096289e-01 -5.320000000000000284e+01 5.963613830684297845e+00 1.122386662291963155e+00 9.888323001649381716e-01 -5.330000000000000426e+01 5.963532570913079311e+00 1.122599285116698820e+00 9.888103081677782624e-01 -5.340000000000000568e+01 5.963451312279643979e+00 1.122811785668582196e+00 9.887883128674698208e-01 -5.350000000000000000e+01 5.963370054783974972e+00 1.123024164005687942e+00 9.887663142667512117e-01 -5.360000000000000142e+01 5.963288798426058968e+00 1.123236420186086715e+00 9.887443123683594681e-01 -5.370000000000000284e+01 5.963207543205879091e+00 1.123448554267843180e+00 9.887223071750304015e-01 -5.380000000000000426e+01 5.963126289123418466e+00 1.123660566309016895e+00 9.887002986894980472e-01 -5.390000000000000568e+01 5.963045036178661107e+00 1.123872456367662087e+00 9.886782869144954411e-01 -5.400000000000000000e+01 5.962963784371591913e+00 1.124084224501827212e+00 9.886562718527540650e-01 -5.410000000000000142e+01 5.962882533702194898e+00 1.124295870769555838e+00 9.886342535070038462e-01 -5.420000000000000284e+01 5.962801284170454963e+00 1.124507395228885098e+00 9.886122318799739350e-01 -5.430000000000000426e+01 5.962720035776354344e+00 1.124718797937847015e+00 9.885902069743915943e-01 -5.440000000000000568e+01 5.962638788519878830e+00 1.124930078954468504e+00 9.885681787929827546e-01 -5.450000000000000000e+01 5.962557542401010657e+00 1.125141238336769156e+00 9.885461473384717923e-01 -5.460000000000000142e+01 5.962476297419736504e+00 1.125352276142764341e+00 9.885241126135823064e-01 -5.470000000000000284e+01 5.962395053576038606e+00 1.125563192430462545e+00 9.885020746210361198e-01 -5.480000000000000426e+01 5.962313810869899200e+00 1.125773987257867370e+00 9.884800333635535008e-01 -5.490000000000000568e+01 5.962232569301306739e+00 1.125984660682975758e+00 9.884579888438537187e-01 -5.500000000000000000e+01 5.962151328870243461e+00 1.126195212763779097e+00 9.884359410646547106e-01 -5.510000000000000142e+01 5.962070089576692489e+00 1.126405643558261449e+00 9.884138900286725260e-01 -5.520000000000000284e+01 5.961988851420639612e+00 1.126615953124402880e+00 9.883918357386222153e-01 -5.530000000000000426e+01 5.961907614402068845e+00 1.126826141520175684e+00 9.883697781972172747e-01 -5.540000000000000568e+01 5.961826378520961534e+00 1.127036208803546158e+00 9.883477174071703120e-01 -5.550000000000000000e+01 5.961745143777302580e+00 1.127246155032475494e+00 9.883256533711917147e-01 -5.560000000000000142e+01 5.961663910171077774e+00 1.127455980264916890e+00 9.883035860919910931e-01 -5.570000000000000284e+01 5.961582677702270239e+00 1.127665684558818882e+00 9.882815155722765033e-01 -5.580000000000000426e+01 5.961501446370866653e+00 1.127875267972121787e+00 9.882594418147548909e-01 -5.590000000000000568e+01 5.961420216176848363e+00 1.128084730562761262e+00 9.882373648221313145e-01 -5.600000000000000000e+01 5.961338987120201161e+00 1.128294072388665192e+00 9.882152845971099442e-01 -5.610000000000000142e+01 5.961257759200907280e+00 1.128503293507755689e+00 9.881932011423931739e-01 -5.620000000000000284e+01 5.961176532418951624e+00 1.128712393977947537e+00 9.881711144606823982e-01 -5.630000000000000426e+01 5.961095306774318203e+00 1.128921373857149302e+00 9.881490245546774576e-01 -5.640000000000000568e+01 5.961014082266991920e+00 1.129130233203263112e+00 9.881269314270768600e-01 -5.650000000000000000e+01 5.960932858896955011e+00 1.129338972074183323e+00 9.881048350805775593e-01 -5.660000000000000142e+01 5.960851636664193265e+00 1.129547590527798517e+00 9.880827355178753990e-01 -5.670000000000000284e+01 5.960770415568689806e+00 1.129756088621989951e+00 9.880606327416646684e-01 -5.680000000000000426e+01 5.960689195610430424e+00 1.129964466414631996e+00 9.880385267546379913e-01 -5.690000000000000568e+01 5.960607976789397355e+00 1.130172723963591919e+00 9.880164175594875475e-01 -5.700000000000000000e+01 5.960526759105577277e+00 1.130380861326730102e+00 9.879943051589031855e-01 -5.710000000000000142e+01 5.960445542558949761e+00 1.130588878561900268e+00 9.879721895555738653e-01 -5.720000000000000284e+01 5.960364327149503261e+00 1.130796775726948145e+00 9.879500707521871039e-01 -5.730000000000000426e+01 5.960283112877218237e+00 1.131004552879713021e+00 9.879279487514290858e-01 -5.740000000000000568e+01 5.960201899742081366e+00 1.131212210078026859e+00 9.879058235559842194e-01 -5.750000000000000000e+01 5.960120687744077550e+00 1.131419747379713847e+00 9.878836951685361356e-01 -5.760000000000000142e+01 5.960039476883189913e+00 1.131627164842591515e+00 9.878615635917668003e-01 -5.770000000000000284e+01 5.959958267159399803e+00 1.131834462524469620e+00 9.878394288283567359e-01 -5.780000000000000426e+01 5.959877058572694786e+00 1.132041640483150591e+00 9.878172908809851327e-01 -5.790000000000000568e+01 5.959795851123057986e+00 1.132248698776429752e+00 9.877951497523299595e-01 -5.800000000000000000e+01 5.959714644810473416e+00 1.132455637462094655e+00 9.877730054450677422e-01 -5.810000000000000142e+01 5.959633439634925978e+00 1.132662456597924638e+00 9.877508579618734519e-01 -5.820000000000000284e+01 5.959552235596397907e+00 1.132869156241692821e+00 9.877287073054209499e-01 -5.830000000000000426e+01 5.959471032694874104e+00 1.133075736451163662e+00 9.877065534783827649e-01 -5.840000000000000568e+01 5.959389830930339471e+00 1.133282197284093851e+00 9.876843964834296496e-01 -5.850000000000000000e+01 5.959308630302777132e+00 1.133488538798232970e+00 9.876622363232315793e-01 -5.860000000000000142e+01 5.959227430812171100e+00 1.133694761051322164e+00 9.876400730004564199e-01 -5.870000000000000284e+01 5.959146232458507164e+00 1.133900864101095030e+00 9.876179065177712602e-01 -5.880000000000000426e+01 5.959065035241769337e+00 1.134106848005277834e+00 9.875957368778416345e-01 -5.890000000000000568e+01 5.958983839161939855e+00 1.134312712821587743e+00 9.875735640833317452e-01 -5.900000000000000000e+01 5.958902644219004507e+00 1.134518458607735258e+00 9.875513881369044622e-01 -5.910000000000000142e+01 5.958821450412944642e+00 1.134724085421421336e+00 9.875292090412208790e-01 -5.920000000000000284e+01 5.958740257743748714e+00 1.134929593320340269e+00 9.875070267989413120e-01 -5.930000000000000426e+01 5.958659066211398070e+00 1.135134982362177247e+00 9.874848414127246343e-01 -5.940000000000000568e+01 5.958577875815878500e+00 1.135340252604609690e+00 9.874626528852277207e-01 -5.950000000000000000e+01 5.958496686557173128e+00 1.135545404105306799e+00 9.874404612191067798e-01 -5.960000000000000142e+01 5.958415498435265079e+00 1.135750436921929563e+00 9.874182664170164658e-01 -5.970000000000000284e+01 5.958334311450138365e+00 1.135955351112129863e+00 9.873960684816096567e-01 -5.980000000000000426e+01 5.958253125601777889e+00 1.136160146733552923e+00 9.873738674155385642e-01 -5.990000000000000568e+01 5.958171940890167662e+00 1.136364823843833527e+00 9.873516632214535127e-01 -6.000000000000000000e+01 5.958090757315290809e+00 1.136569382500599801e+00 9.873294559020036054e-01 -6.010000000000000142e+01 5.958009574877134895e+00 1.136773822761470321e+00 9.873072454598368353e-01 -6.020000000000000284e+01 5.957928393575679493e+00 1.136978144684055669e+00 9.872850318975993078e-01 -6.030000000000000426e+01 5.957847213410912168e+00 1.137182348325957104e+00 9.872628152179361294e-01 -6.040000000000000568e+01 5.957766034382815157e+00 1.137386433744767666e+00 9.872405954234907410e-01 -6.050000000000000000e+01 5.957684856491375136e+00 1.137590400998071960e+00 9.872183725169059176e-01 -6.060000000000000142e+01 5.957603679736574342e+00 1.137794250143445485e+00 9.871961465008219916e-01 -6.070000000000000284e+01 5.957522504118397677e+00 1.137997981238455747e+00 9.871739173778789622e-01 -6.080000000000000426e+01 5.957441329636826488e+00 1.138201594340659817e+00 9.871516851507148305e-01 -6.090000000000000568e+01 5.957360156291846565e+00 1.138405089507607881e+00 9.871294498219664870e-01 -6.100000000000000000e+01 5.957278984083441919e+00 1.138608466796839469e+00 9.871072113942692683e-01 -6.110000000000000142e+01 5.957197813011598342e+00 1.138811726265886337e+00 9.870849698702575115e-01 -6.120000000000000284e+01 5.957116643076299844e+00 1.139014867972270473e+00 9.870627252525636663e-01 -6.130000000000000426e+01 5.957035474277528664e+00 1.139217891973505203e+00 9.870404775438192946e-01 -6.140000000000000568e+01 5.956954306615269701e+00 1.139420798327095641e+00 9.870182267466544035e-01 -6.150000000000000000e+01 5.956873140089506080e+00 1.139623587090536239e+00 9.869959728636974461e-01 -6.160000000000000142e+01 5.956791974700222703e+00 1.139826258321312791e+00 9.869737158975757652e-01 -6.170000000000000284e+01 5.956710810447406246e+00 1.140028812076901987e+00 9.869514558509153712e-01 -6.180000000000000426e+01 5.956629647331037170e+00 1.140231248414771192e+00 9.869291927263407205e-01 -6.190000000000000568e+01 5.956548485351099487e+00 1.140433567392379111e+00 9.869069265264749369e-01 -6.200000000000000000e+01 5.956467324507580763e+00 1.140635769067173788e+00 9.868846572539399231e-01 -6.210000000000000142e+01 5.956386164800461458e+00 1.140837853496595278e+00 9.868623849113563606e-01 -6.220000000000000284e+01 5.956305006229728249e+00 1.141039820738072974e+00 9.868401095013431545e-01 -6.230000000000000426e+01 5.956223848795364262e+00 1.141241670849027390e+00 9.868178310265180997e-01 -6.240000000000000568e+01 5.956142692497354396e+00 1.141443403886870378e+00 9.867955494894975477e-01 -6.250000000000000000e+01 5.956061537335680889e+00 1.141645019909002468e+00 9.867732648928965178e-01 -6.260000000000000142e+01 5.955980383310329529e+00 1.141846518972815971e+00 9.867509772393284750e-01 -6.270000000000000284e+01 5.955899230421285218e+00 1.142047901135692323e+00 9.867286865314061073e-01 -6.280000000000000426e+01 5.955818078668529303e+00 1.142249166455004517e+00 9.867063927717402150e-01 -6.290000000000000568e+01 5.955736928052048462e+00 1.142450314988114668e+00 9.866840959629403773e-01 -6.300000000000000000e+01 5.955655778571824044e+00 1.142651346792376454e+00 9.866617961076146193e-01 -6.310000000000000142e+01 5.955574630227843613e+00 1.142852261925132451e+00 9.866394932083700775e-01 -6.320000000000000284e+01 5.955493483020088519e+00 1.143053060443716351e+00 9.866171872678120014e-01 -6.330000000000000426e+01 5.955412336948543661e+00 1.143253742405450746e+00 9.865948782885448631e-01 -6.340000000000000568e+01 5.955331192013193053e+00 1.143454307867649788e+00 9.865725662731712475e-01 -6.350000000000000000e+01 5.955250048214022485e+00 1.143654756887616308e+00 9.865502512242924071e-01 -6.360000000000000142e+01 5.955168905551013303e+00 1.143855089522644031e+00 9.865279331445087063e-01 -6.370000000000000284e+01 5.955087764024151298e+00 1.144055305830016023e+00 9.865056120364187331e-01 -6.380000000000000426e+01 5.955006623633420482e+00 1.144255405867005804e+00 9.864832879026198542e-01 -6.390000000000000568e+01 5.954925484378804867e+00 1.144455389690876235e+00 9.864609607457081042e-01 -6.400000000000000000e+01 5.954844346260288468e+00 1.144655257358880407e+00 9.864386305682780742e-01 -6.410000000000000853e+01 5.954763209277857960e+00 1.144855008928260531e+00 9.864162973729231343e-01 -6.420000000000000284e+01 5.954682073431493805e+00 1.145054644456248827e+00 9.863939611622352110e-01 -6.429999999999999716e+01 5.954600938721181791e+00 1.145254164000067298e+00 9.863716219388048989e-01 -6.440000000000000568e+01 5.954519805146904154e+00 1.145453567616927959e+00 9.863492797052210159e-01 -6.450000000000000000e+01 5.954438672708648461e+00 1.145652855364031719e+00 9.863269344640721581e-01 -6.460000000000000853e+01 5.954357541406396059e+00 1.145852027298569276e+00 9.863045862179442569e-01 -6.470000000000000284e+01 5.954276411240131850e+00 1.146051083477720889e+00 9.862822349694226887e-01 -6.479999999999999716e+01 5.954195282209839846e+00 1.146250023958657049e+00 9.862598807210911644e-01 -6.490000000000000568e+01 5.954114154315504948e+00 1.146448848798536480e+00 9.862375234755323961e-01 -6.500000000000000000e+01 5.954033027557111168e+00 1.146647558054507687e+00 9.862151632353270969e-01 -6.510000000000000853e+01 5.953951901934641633e+00 1.146846151783708745e+00 9.861928000030553143e-01 -6.520000000000000284e+01 5.953870777448081242e+00 1.147044630043266622e+00 9.861704337812952081e-01 -6.529999999999999716e+01 5.953789654097413120e+00 1.147242992890298519e+00 9.861480645726239391e-01 -6.540000000000000568e+01 5.953708531882622168e+00 1.147441240381909644e+00 9.861256923796170026e-01 -6.550000000000000000e+01 5.953627410803692399e+00 1.147639372575195660e+00 9.861033172048490059e-01 -6.560000000000000853e+01 5.953546290860608714e+00 1.147837389527240015e+00 9.860809390508927796e-01 -6.570000000000000284e+01 5.953465172053354237e+00 1.148035291295116611e+00 9.860585579203200446e-01 -6.579999999999999716e+01 5.953384054381913870e+00 1.148233077935886914e+00 9.860361738157008560e-01 -6.590000000000000568e+01 5.953302937846271625e+00 1.148430749506603066e+00 9.860137867396043809e-01 -6.600000000000000000e+01 5.953221822446411515e+00 1.148628306064305216e+00 9.859913966945982322e-01 -6.610000000000000853e+01 5.953140708182317553e+00 1.148825747666023078e+00 9.859690036832482463e-01 -6.620000000000000284e+01 5.953059595053973752e+00 1.149023074368774155e+00 9.859466077081197044e-01 -6.629999999999999716e+01 5.952978483061363235e+00 1.149220286229566179e+00 9.859242087717760006e-01 -6.640000000000000568e+01 5.952897372204471793e+00 1.149417383305394891e+00 9.859018068767790854e-01 -6.650000000000000000e+01 5.952816262483283438e+00 1.149614365653245374e+00 9.858794020256901325e-01 -6.660000000000000853e+01 5.952735153897781295e+00 1.149811233330090943e+00 9.858569942210684278e-01 -6.670000000000000284e+01 5.952654046447949376e+00 1.150007986392893811e+00 9.858345834654721473e-01 -6.679999999999999716e+01 5.952572940133772583e+00 1.150204624898605310e+00 9.858121697614578016e-01 -6.690000000000000568e+01 5.952491834955234040e+00 1.150401148904165227e+00 9.857897531115810130e-01 -6.700000000000000000e+01 5.952410730912321313e+00 1.150597558466501358e+00 9.857673335183959606e-01 -6.710000000000000853e+01 5.952329628005015749e+00 1.150793853642530618e+00 9.857449109844552693e-01 -6.720000000000000284e+01 5.952248526233300474e+00 1.150990034489158376e+00 9.857224855123102314e-01 -6.729999999999999716e+01 5.952167425597161277e+00 1.151186101063278899e+00 9.857000571045112514e-01 -6.740000000000000568e+01 5.952086326096582169e+00 1.151382053421774243e+00 9.856776257636067351e-01 -6.750000000000000000e+01 5.952005227731545389e+00 1.151577891621515137e+00 9.856551914921439783e-01 -6.760000000000000853e+01 5.951924130502037613e+00 1.151773615719360988e+00 9.856327542926690555e-01 -6.770000000000000284e+01 5.951843034408041078e+00 1.151969225772159211e+00 9.856103141677267088e-01 -6.779999999999999716e+01 5.951761939449542460e+00 1.152164721836745898e+00 9.855878711198600151e-01 -6.790000000000000568e+01 5.951680845626523109e+00 1.152360103969944705e+00 9.855654251516111630e-01 -6.800000000000000000e+01 5.951599752938969701e+00 1.152555372228567965e+00 9.855429762655205650e-01 -6.810000000000000853e+01 5.951518661386865361e+00 1.152750526669416908e+00 9.855205244641278561e-01 -6.820000000000000284e+01 5.951437570970192326e+00 1.152945567349279443e+00 9.854980697499705622e-01 -6.829999999999999716e+01 5.951356481688935496e+00 1.153140494324933041e+00 9.854756121255853207e-01 -6.840000000000000568e+01 5.951275393543080661e+00 1.153335307653142072e+00 9.854531515935075481e-01 -6.850000000000000000e+01 5.951194306532612721e+00 1.153530007390660250e+00 9.854306881562708842e-01 -6.860000000000000853e+01 5.951113220657513914e+00 1.153724593594228631e+00 9.854082218164084139e-01 -6.870000000000000284e+01 5.951032135917767363e+00 1.153919066320575615e+00 9.853857525764506686e-01 -6.879999999999999716e+01 5.950951052313357081e+00 1.154113425626418277e+00 9.853632804389279576e-01 -6.890000000000000568e+01 5.950869969844269747e+00 1.154307671568461702e+00 9.853408054063687027e-01 -6.900000000000000000e+01 5.950788888510488484e+00 1.154501804203398763e+00 9.853183274813001047e-01 -6.910000000000000853e+01 5.950707808311998193e+00 1.154695823587909898e+00 9.852958466662479209e-01 -6.920000000000000284e+01 5.950626729248781110e+00 1.154889729778663554e+00 9.852733629637365764e-01 -6.929999999999999716e+01 5.950545651320822138e+00 1.155083522832315968e+00 9.852508763762893862e-01 -6.940000000000000568e+01 5.950464574528107065e+00 1.155277202805510939e+00 9.852283869064282218e-01 -6.950000000000000000e+01 5.950383498870618126e+00 1.155470769754880278e+00 9.852058945566736226e-01 -6.960000000000000853e+01 5.950302424348339336e+00 1.155664223737043583e+00 9.851833993295445735e-01 -6.970000000000000284e+01 5.950221350961255595e+00 1.155857564808606908e+00 9.851609012275588384e-01 -6.979999999999999716e+01 5.950140278709350916e+00 1.156050793026165202e+00 9.851384002532328488e-01 -6.990000000000000568e+01 5.950059207592610200e+00 1.156243908446300761e+00 9.851158964090817038e-01 -7.000000000000000000e+01 5.949978137611015683e+00 1.156436911125583000e+00 9.850933896976193926e-01 -7.010000000000000853e+01 5.949897068764554930e+00 1.156629801120569123e+00 9.850708801213583499e-01 -7.020000000000000284e+01 5.949816001053208403e+00 1.156822578487803677e+00 9.850483676828095669e-01 -7.029999999999999716e+01 5.949734934476961001e+00 1.157015243283818329e+00 9.850258523844828140e-01 -7.040000000000000568e+01 5.949653869035798515e+00 1.157207795565132757e+00 9.850033342288865290e-01 -7.050000000000000000e+01 5.949572804729702291e+00 1.157400235388253318e+00 9.849808132185278176e-01 -7.060000000000000853e+01 5.949491741558659008e+00 1.157592562809674819e+00 9.849582893559124530e-01 -7.070000000000000284e+01 5.949410679522652678e+00 1.157784777885877414e+00 9.849357626435447655e-01 -7.079999999999999716e+01 5.949329618621665539e+00 1.157976880673330378e+00 9.849132330839279748e-01 -7.090000000000000568e+01 5.949248558855683378e+00 1.158168871228489216e+00 9.848907006795637464e-01 -7.100000000000000000e+01 5.949167500224691985e+00 1.158360749607797224e+00 9.848681654329525248e-01 -7.110000000000000853e+01 5.949086442728671820e+00 1.158552515867684374e+00 9.848456273465935329e-01 -7.120000000000000284e+01 5.949005386367609560e+00 1.158744170064567980e+00 9.848230864229843284e-01 -7.129999999999999716e+01 5.948924331141488331e+00 1.158935712254851813e+00 9.848005426646213589e-01 -7.140000000000000568e+01 5.948843277050292144e+00 1.159127142494927654e+00 9.847779960739997396e-01 -7.150000000000000000e+01 5.948762224094005013e+00 1.159318460841173737e+00 9.847554466536131423e-01 -7.160000000000000853e+01 5.948681172272611839e+00 1.159509667349955198e+00 9.847328944059541289e-01 -7.170000000000000284e+01 5.948600121586096634e+00 1.159700762077624736e+00 9.847103393335135957e-01 -7.179999999999999716e+01 5.948519072034443411e+00 1.159891745080521286e+00 9.846877814387814398e-01 -7.190000000000000568e+01 5.948438023617636183e+00 1.160082616414971124e+00 9.846652207242458932e-01 -7.200000000000000000e+01 5.948356976335658075e+00 1.160273376137286760e+00 9.846426571923942994e-01 -7.210000000000000853e+01 5.948275930188494875e+00 1.160464024303768715e+00 9.846200908457123369e-01 -7.220000000000000284e+01 5.948194885176129709e+00 1.160654560970702409e+00 9.845975216866843516e-01 -7.229999999999999716e+01 5.948113841298546589e+00 1.160844986194361939e+00 9.845749497177935794e-01 -7.240000000000000568e+01 5.948032798555732192e+00 1.161035300031006745e+00 9.845523749415214798e-01 -7.250000000000000000e+01 5.947951756947666979e+00 1.161225502536883836e+00 9.845297973603485131e-01 -7.260000000000000853e+01 5.947870716474336739e+00 1.161415593768226673e+00 9.845072169767540293e-01 -7.270000000000000284e+01 5.947789677135725483e+00 1.161605573781255174e+00 9.844846337932157132e-01 -7.279999999999999716e+01 5.947708638931819003e+00 1.161795442632175490e+00 9.844620478122099172e-01 -7.290000000000000568e+01 5.947627601862598645e+00 1.161985200377180893e+00 9.844394590362117725e-01 -7.300000000000000000e+01 5.947546565928050200e+00 1.162174847072451334e+00 9.844168674676951891e-01 -7.310000000000000853e+01 5.947465531128156790e+00 1.162364382774152327e+00 9.843942731091325227e-01 -7.320000000000000284e+01 5.947384497462903319e+00 1.162553807538436734e+00 9.843716759629949076e-01 -7.329999999999999716e+01 5.947303464932275574e+00 1.162743121421443648e+00 9.843490760317520349e-01 -7.340000000000000568e+01 5.947222433536254016e+00 1.162932324479298618e+00 9.843264733178727077e-01 -7.350000000000000000e+01 5.947141403274826210e+00 1.163121416768112981e+00 9.843038678238237305e-01 -7.360000000000000853e+01 5.947060374147974393e+00 1.163310398343985197e+00 9.842812595520710195e-01 -7.370000000000000284e+01 5.946979346155683466e+00 1.163499269262999292e+00 9.842586485050789369e-01 -7.379999999999999716e+01 5.946898319297936553e+00 1.163688029581226413e+00 9.842360346853109565e-01 -7.390000000000000568e+01 5.946817293574718555e+00 1.163876679354723054e+00 9.842134180952287759e-01 -7.400000000000000000e+01 5.946736268986014373e+00 1.164065218639532384e+00 9.841907987372927602e-01 -7.410000000000000853e+01 5.946655245531806244e+00 1.164253647491684029e+00 9.841681766139623866e-01 -7.420000000000000284e+01 5.946574223212079957e+00 1.164441965967193182e+00 9.841455517276952447e-01 -7.429999999999999716e+01 5.946493202026818636e+00 1.164630174122061268e+00 9.841229240809482581e-01 -7.440000000000000568e+01 5.946412181976007183e+00 1.164818272012276390e+00 9.841002936761762410e-01 -7.450000000000000000e+01 5.946331163059629610e+00 1.165006259693812218e+00 9.840776605158332302e-01 -7.460000000000000853e+01 5.946250145277669930e+00 1.165194137222628212e+00 9.840550246023719305e-01 -7.470000000000000284e+01 5.946169128630112155e+00 1.165381904654669842e+00 9.840323859382433813e-01 -7.479999999999999716e+01 5.946088113116942075e+00 1.165569562045869034e+00 9.840097445258977338e-01 -7.490000000000000568e+01 5.946007098738141927e+00 1.165757109452143503e+00 9.839871003677833627e-01 -7.500000000000000000e+01 5.945926085493694835e+00 1.165944546929396752e+00 9.839644534663477549e-01 -7.510000000000000853e+01 5.945845073383587476e+00 1.166131874533518298e+00 9.839418038240368425e-01 -7.520000000000000284e+01 5.945764062407802975e+00 1.166319092320383444e+00 9.839191514432952257e-01 -7.529999999999999716e+01 5.945683052566327120e+00 1.166506200345853062e+00 9.838964963265662833e-01 -7.540000000000000568e+01 5.945602043859141261e+00 1.166693198665774034e+00 9.838738384762918399e-01 -7.550000000000000000e+01 5.945521036286230299e+00 1.166880087335979255e+00 9.838511778949127207e-01 -7.560000000000000853e+01 5.945440029847579133e+00 1.167066866412286519e+00 9.838285145848683078e-01 -7.570000000000000284e+01 5.945359024543170001e+00 1.167253535950499854e+00 9.838058485485966509e-01 -7.579999999999999716e+01 5.945278020372989580e+00 1.167440096006409522e+00 9.837831797885345786e-01 -7.590000000000000568e+01 5.945197017337020995e+00 1.167626546635790685e+00 9.837605083071173651e-01 -7.600000000000000000e+01 5.945116015435248258e+00 1.167812887894404072e+00 9.837378341067790632e-01 -7.610000000000000853e+01 5.945035014667657158e+00 1.167999119837996425e+00 9.837151571899525049e-01 -7.620000000000000284e+01 5.944954015034229933e+00 1.168185242522299605e+00 9.836924775590691894e-01 -7.629999999999999716e+01 5.944873016534951482e+00 1.168371256003031267e+00 9.836697952165592840e-01 -7.640000000000000568e+01 5.944792019169804931e+00 1.168557160335895073e+00 9.836471101648516235e-01 -7.650000000000000000e+01 5.944711022938775180e+00 1.168742955576578701e+00 9.836244224063737107e-01 -7.660000000000000853e+01 5.944630027841847131e+00 1.168928641780756505e+00 9.836017319435514938e-01 -7.670000000000000284e+01 5.944549033879003019e+00 1.169114219004087962e+00 9.835790387788101441e-01 -7.680000000000001137e+01 5.944468041050229523e+00 1.169299687302217672e+00 9.835563429145732783e-01 -7.690000000000000568e+01 5.944387049355509767e+00 1.169485046730776023e+00 9.835336443532627371e-01 -7.700000000000000000e+01 5.944306058794827763e+00 1.169670297345378307e+00 9.835109430972998057e-01 -7.710000000000000853e+01 5.944225069368166636e+00 1.169855439201625158e+00 9.834882391491041043e-01 -7.720000000000000284e+01 5.944144081075513064e+00 1.170040472355102779e+00 9.834655325110939206e-01 -7.730000000000001137e+01 5.944063093916848395e+00 1.170225396861382050e+00 9.834428231856860991e-01 -7.740000000000000568e+01 5.943982107892157529e+00 1.170410212776019865e+00 9.834201111752963742e-01 -7.750000000000000000e+01 5.943901123001426257e+00 1.170594920154557572e+00 9.833973964823391478e-01 -7.760000000000000853e+01 5.943820139244635925e+00 1.170779519052522089e+00 9.833746791092277117e-01 -7.770000000000000284e+01 5.943739156621771436e+00 1.170964009525425231e+00 9.833519590583735814e-01 -7.780000000000001137e+01 5.943658175132818577e+00 1.171148391628764163e+00 9.833292363321872731e-01 -7.790000000000000568e+01 5.943577194777762251e+00 1.171332665418021168e+00 9.833065109330778597e-01 -7.800000000000000000e+01 5.943496215556583806e+00 1.171516830948662768e+00 9.832837828634531929e-01 -7.810000000000000853e+01 5.943415237469267254e+00 1.171700888276141495e+00 9.832610521257196812e-01 -7.820000000000000284e+01 5.943334260515800160e+00 1.171884837455894113e+00 9.832383187222825116e-01 -7.830000000000001137e+01 5.943253284696163874e+00 1.172068678543342957e+00 9.832155826555457612e-01 -7.840000000000000568e+01 5.943172310010343296e+00 1.172252411593895483e+00 9.831928439279120635e-01 -7.850000000000000000e+01 5.943091336458322438e+00 1.172436036662942715e+00 9.831701025417824980e-01 -7.860000000000000853e+01 5.943010364040086202e+00 1.172619553805862358e+00 9.831473584995571446e-01 -7.870000000000000284e+01 5.942929392755617712e+00 1.172802963078015459e+00 9.831246118036345294e-01 -7.880000000000001137e+01 5.942848422604902758e+00 1.172986264534748857e+00 9.831018624564120678e-01 -7.890000000000000568e+01 5.942767453587922688e+00 1.173169458231393403e+00 9.830791104602857322e-01 -7.900000000000000000e+01 5.942686485704663291e+00 1.173352544223265959e+00 9.830563558176502736e-01 -7.910000000000000853e+01 5.942605518955109467e+00 1.173535522565666511e+00 9.830335985308993330e-01 -7.920000000000000284e+01 5.942524553339245230e+00 1.173718393313880615e+00 9.830108386024249967e-01 -7.930000000000001137e+01 5.942443588857052816e+00 1.173901156523179168e+00 9.829880760346179080e-01 -7.940000000000000568e+01 5.942362625508518015e+00 1.174083812248816416e+00 9.829653108298676001e-01 -7.950000000000000000e+01 5.942281663293624838e+00 1.174266360546031951e+00 9.829425429905623846e-01 -7.960000000000000853e+01 5.942200702212357299e+00 1.174448801470050263e+00 9.829197725190890189e-01 -7.970000000000000284e+01 5.942119742264700299e+00 1.174631135076079858e+00 9.828969994178332614e-01 -7.980000000000001137e+01 5.942038783450636963e+00 1.174813361419314361e+00 9.828742236891794271e-01 -7.990000000000000568e+01 5.941957825770152191e+00 1.174995480554930749e+00 9.828514453355104985e-01 -8.000000000000000000e+01 5.941876869223229107e+00 1.175177492538092450e+00 9.828286643592079042e-01 -8.010000000000000853e+01 5.941795913809852614e+00 1.175359397423945351e+00 9.828058807626522952e-01 -8.020000000000000284e+01 5.941714959530006723e+00 1.175541195267621353e+00 9.827830945482228797e-01 -8.030000000000001137e+01 5.941634006383675448e+00 1.175722886124235922e+00 9.827603057182972002e-01 -8.040000000000000568e+01 5.941553054370842801e+00 1.175904470048889428e+00 9.827375142752518000e-01 -8.050000000000000000e+01 5.941472103491495460e+00 1.176085947096666251e+00 9.827147202214620014e-01 -8.060000000000000853e+01 5.941391153745614773e+00 1.176267317322634787e+00 9.826919235593015722e-01 -8.070000000000000284e+01 5.941310205133184752e+00 1.176448580781848774e+00 9.826691242911431701e-01 -8.080000000000001137e+01 5.941229257654190299e+00 1.176629737529345743e+00 9.826463224193581203e-01 -8.090000000000000568e+01 5.941148311308615426e+00 1.176810787620147458e+00 9.826235179463161939e-01 -8.100000000000000000e+01 5.941067366096445035e+00 1.176991731109259254e+00 9.826007108743862739e-01 -8.110000000000000853e+01 5.940986422017663138e+00 1.177172568051672252e+00 9.825779012059356887e-01 -8.120000000000000284e+01 5.940905479072251971e+00 1.177353298502360923e+00 9.825550889433305457e-01 -8.130000000000001137e+01 5.940824537260199101e+00 1.177533922516283305e+00 9.825322740889358419e-01 -8.140000000000000568e+01 5.940743596581484987e+00 1.177714440148383002e+00 9.825094566451145761e-01 -8.150000000000000000e+01 5.940662657036097194e+00 1.177894851453586522e+00 9.824866366142293028e-01 -8.160000000000000853e+01 5.940581718624017959e+00 1.178075156486805053e+00 9.824638139986410224e-01 -8.170000000000000284e+01 5.940500781345231296e+00 1.178255355302933349e+00 9.824409888007089586e-01 -8.180000000000001137e+01 5.940419845199722104e+00 1.178435447956851734e+00 9.824181610227917805e-01 -8.190000000000000568e+01 5.940338910187475285e+00 1.178615434503422543e+00 9.823953306672466024e-01 -8.200000000000000000e+01 5.940257976308472188e+00 1.178795314997492794e+00 9.823724977364290956e-01 -8.210000000000000853e+01 5.940177043562699488e+00 1.178975089493894624e+00 9.823496622326933769e-01 -8.220000000000000284e+01 5.940096111950140312e+00 1.179154758047442852e+00 9.823268241583927862e-01 -8.230000000000001137e+01 5.940015181470779559e+00 1.179334320712936535e+00 9.823039835158792199e-01 -8.240000000000000568e+01 5.939934252124601244e+00 1.179513777545158959e+00 9.822811403075030201e-01 -8.250000000000000000e+01 5.939853323911588490e+00 1.179693128598876983e+00 9.822582945356136408e-01 -8.260000000000000853e+01 5.939772396831726198e+00 1.179872373928841256e+00 9.822354462025589816e-01 -8.270000000000000284e+01 5.939691470885001046e+00 1.180051513589786882e+00 9.822125953106857210e-01 -8.280000000000001137e+01 5.939610546071393493e+00 1.180230547636432092e+00 9.821897418623393161e-01 -8.290000000000000568e+01 5.939529622390888441e+00 1.180409476123479351e+00 9.821668858598640028e-01 -8.300000000000000000e+01 5.939448699843470791e+00 1.180588299105614469e+00 9.821440273056022408e-01 -8.310000000000000853e+01 5.939367778429124556e+00 1.180767016637507272e+00 9.821211662018956012e-01 -8.320000000000000284e+01 5.939286858147835524e+00 1.180945628773811373e+00 9.820983025510845454e-01 -8.330000000000001137e+01 5.939205938999584156e+00 1.181124135569164180e+00 9.820754363555076472e-01 -8.340000000000000568e+01 5.939125020984357128e+00 1.181302537078186443e+00 9.820525676175030361e-01 -8.350000000000000000e+01 5.939044104102139343e+00 1.181480833355483151e+00 9.820296963394066214e-01 -8.360000000000000853e+01 5.938963188352912148e+00 1.181659024455641971e+00 9.820068225235536463e-01 -8.370000000000000284e+01 5.938882273736662221e+00 1.181837110433235249e+00 9.819839461722779106e-01 -8.380000000000001137e+01 5.938801360253373574e+00 1.182015091342818236e+00 9.819610672879119928e-01 -8.390000000000000568e+01 5.938720447903028443e+00 1.182192967238929970e+00 9.819381858727870283e-01 -8.400000000000000000e+01 5.938639536685612619e+00 1.182370738176092839e+00 9.819153019292329310e-01 -8.410000000000000853e+01 5.938558626601109225e+00 1.182548404208812576e+00 9.818924154595781717e-01 -8.420000000000000284e+01 5.938477717649504051e+00 1.182725965391579592e+00 9.818695264661503330e-01 -8.430000000000001137e+01 5.938396809830780221e+00 1.182903421778866537e+00 9.818466349512754432e-01 -8.440000000000000568e+01 5.938315903144921748e+00 1.183080773425129406e+00 9.818237409172784202e-01 -8.450000000000000000e+01 5.938234997591914421e+00 1.183258020384808873e+00 9.818008443664824059e-01 -8.460000000000000853e+01 5.938154093171741366e+00 1.183435162712327848e+00 9.817779453012097646e-01 -8.470000000000000284e+01 5.938073189884383929e+00 1.183612200462093256e+00 9.817550437237815286e-01 -8.480000000000001137e+01 5.937992287729829677e+00 1.183789133688495143e+00 9.817321396365170649e-01 -8.490000000000000568e+01 5.937911386708061734e+00 1.183965962445906905e+00 9.817092330417350743e-01 -8.500000000000000000e+01 5.937830486819065001e+00 1.184142686788685062e+00 9.816863239417523701e-01 -8.510000000000000853e+01 5.937749588062823491e+00 1.184319306771170144e+00 9.816634123388848776e-01 -8.520000000000000284e+01 5.937668690439320329e+00 1.184495822447685143e+00 9.816404982354471898e-01 -8.530000000000001137e+01 5.937587793948542192e+00 1.184672233872536840e+00 9.816175816337524562e-01 -8.540000000000000568e+01 5.937506898590469540e+00 1.184848541100014918e+00 9.815946625361126054e-01 -8.550000000000000000e+01 5.937426004365089049e+00 1.185024744184392409e+00 9.815717409448384556e-01 -8.560000000000000853e+01 5.937345111272382958e+00 1.185200843179925689e+00 9.815488168622392706e-01 -8.570000000000000284e+01 5.937264219312336166e+00 1.185376838140854261e+00 9.815258902906230931e-01 -8.580000000000001137e+01 5.937183328484934464e+00 1.185552729121400306e+00 9.815029612322969665e-01 -8.590000000000000568e+01 5.937102438790160974e+00 1.185728516175769576e+00 9.814800296895663800e-01 -8.600000000000000000e+01 5.937021550228000599e+00 1.185904199358150946e+00 9.814570956647353794e-01 -8.610000000000000853e+01 5.936940662798435575e+00 1.186079778722716194e+00 9.814341591601074555e-01 -8.620000000000000284e+01 5.936859776501450803e+00 1.186255254323620223e+00 9.814112201779838784e-01 -8.630000000000001137e+01 5.936778891337032071e+00 1.186430626215001505e+00 9.813882787206652525e-01 -8.640000000000000568e+01 5.936698007305161617e+00 1.186605894450980525e+00 9.813653347904507385e-01 -8.650000000000000000e+01 5.936617124405825230e+00 1.186781059085661116e+00 9.813423883896381650e-01 -8.660000000000000853e+01 5.936536242639006922e+00 1.186956120173130458e+00 9.813194395205242504e-01 -8.670000000000000284e+01 5.936455362004688041e+00 1.187131077767458853e+00 9.812964881854044918e-01 -8.680000000000001137e+01 5.936374482502856154e+00 1.187305931922698843e+00 9.812735343865727211e-01 -8.690000000000000568e+01 5.936293604133495272e+00 1.187480682692885869e+00 9.812505781263216598e-01 -8.700000000000000000e+01 5.936212726896587633e+00 1.187655330132038944e+00 9.812276194069430302e-01 -8.710000000000000853e+01 5.936131850792119025e+00 1.187829874294159982e+00 9.812046582307272224e-01 -8.720000000000000284e+01 5.936050975820073461e+00 1.188004315233233132e+00 9.811816945999629613e-01 -8.730000000000001137e+01 5.935970101980432290e+00 1.188178653003225671e+00 9.811587285169379724e-01 -8.740000000000000568e+01 5.935889229273183076e+00 1.188352887658087553e+00 9.811357599839387600e-01 -8.750000000000000000e+01 5.935808357698309834e+00 1.188527019251751637e+00 9.811127890032502741e-01 -8.760000000000000853e+01 5.935727487255795687e+00 1.188701047838133462e+00 9.810898155771566875e-01 -8.770000000000000284e+01 5.935646617945624648e+00 1.188874973471131913e+00 9.810668397079405079e-01 -8.780000000000001137e+01 5.935565749767778954e+00 1.189048796204627445e+00 9.810438613978829103e-01 -8.790000000000000568e+01 5.935484882722245281e+00 1.189222516092484749e+00 9.810208806492641820e-01 -8.800000000000000000e+01 5.935404016809007643e+00 1.189396133188549420e+00 9.809978974643631666e-01 -8.810000000000000853e+01 5.935323152028050941e+00 1.189569647546651288e+00 9.809749118454573757e-01 -8.820000000000000284e+01 5.935242288379357412e+00 1.189743059220602195e+00 9.809519237948229886e-01 -8.830000000000001137e+01 5.935161425862914619e+00 1.189916368264196889e+00 9.809289333147352963e-01 -8.840000000000000568e+01 5.935080564478703913e+00 1.190089574731212352e+00 9.809059404074677024e-01 -8.850000000000000000e+01 5.934999704226709305e+00 1.190262678675408026e+00 9.808829450752926116e-01 -8.860000000000000853e+01 5.934918845106914809e+00 1.190435680150526476e+00 9.808599473204814290e-01 -8.870000000000000284e+01 5.934837987119306213e+00 1.190608579210292506e+00 9.808369471453041166e-01 -8.880000000000001137e+01 5.934757130263867531e+00 1.190781375908413597e+00 9.808139445520291932e-01 -8.890000000000000568e+01 5.934676274540581886e+00 1.190954070298579914e+00 9.807909395429242894e-01 -8.900000000000000000e+01 5.934595419949433293e+00 1.191126662434463634e+00 9.807679321202553702e-01 -8.910000000000000853e+01 5.934514566490407539e+00 1.191299152369719838e+00 9.807449222862870686e-01 -8.920000000000000284e+01 5.934433714163486862e+00 1.191471540157985842e+00 9.807219100432833514e-01 -8.930000000000001137e+01 5.934352862968655273e+00 1.191643825852881200e+00 9.806988953935064091e-01 -8.940000000000000568e+01 5.934272012905898563e+00 1.191816009508008367e+00 9.806758783392173218e-01 -8.950000000000000000e+01 5.934191163975202521e+00 1.191988091176952258e+00 9.806528588826758375e-01 -8.960000000000000853e+01 5.934110316176548494e+00 1.192160070913279801e+00 9.806298370261408159e-01 -8.970000000000000284e+01 5.934029469509921384e+00 1.192331948770540384e+00 9.806068127718693406e-01 -8.980000000000001137e+01 5.933948623975305203e+00 1.192503724802265852e+00 9.805837861221172735e-01 -8.990000000000000568e+01 5.933867779572683965e+00 1.192675399061970731e+00 9.805607570791396999e-01 -9.000000000000000000e+01 5.933786936302042569e+00 1.192846971603150452e+00 9.805377256451899282e-01 -9.010000000000000853e+01 5.933706094163364142e+00 1.193018442479284902e+00 9.805146918225201569e-01 -9.020000000000000284e+01 5.933625253156634471e+00 1.193189811743834206e+00 9.804916556133816963e-01 -9.030000000000001137e+01 5.933544413281837571e+00 1.193361079450242279e+00 9.804686170200239692e-01 -9.040000000000000568e+01 5.933463574538956564e+00 1.193532245651934165e+00 9.804455760446955104e-01 -9.050000000000000000e+01 5.933382736927975465e+00 1.193703310402317364e+00 9.804225326896434112e-01 -9.060000000000000853e+01 5.933301900448880062e+00 1.193874273754782278e+00 9.803994869571137638e-01 -9.070000000000000284e+01 5.933221065101652592e+00 1.194045135762701104e+00 9.803764388493512172e-01 -9.080000000000001137e+01 5.933140230886277955e+00 1.194215896479428052e+00 9.803533883685994210e-01 -9.090000000000000568e+01 5.933059397802741941e+00 1.194386555958299345e+00 9.803303355171003597e-01 -9.100000000000000000e+01 5.932978565851026787e+00 1.194557114252634111e+00 9.803072802970949073e-01 -9.110000000000000853e+01 5.932897735031114728e+00 1.194727571415732603e+00 9.802842227108228279e-01 -9.120000000000000284e+01 5.932816905342993330e+00 1.194897927500877532e+00 9.802611627605223310e-01 -9.130000000000001137e+01 5.932736076786645718e+00 1.195068182561334291e+00 9.802381004484309601e-01 -9.140000000000000568e+01 5.932655249362058569e+00 1.195238336650349176e+00 9.802150357767842603e-01 -9.150000000000000000e+01 5.932574423069212344e+00 1.195408389821151829e+00 9.801919687478169996e-01 -9.160000000000000853e+01 5.932493597908091942e+00 1.195578342126952798e+00 9.801688993637625025e-01 -9.170000000000000284e+01 5.932412773878682266e+00 1.195748193620945088e+00 9.801458276268530945e-01 -9.180000000000001137e+01 5.932331950980968216e+00 1.195917944356304163e+00 9.801227535393196577e-01 -9.190000000000000568e+01 5.932251129214931140e+00 1.196087594386186836e+00 9.800996771033916311e-01 -9.200000000000000000e+01 5.932170308580559492e+00 1.196257143763732156e+00 9.800765983212975652e-01 -9.210000000000000853e+01 5.932089489077833733e+00 1.196426592542060741e+00 9.800535171952646785e-01 -9.220000000000000284e+01 5.932008670706741427e+00 1.196595940774275668e+00 9.800304337275184130e-01 -9.230000000000001137e+01 5.931927853467263922e+00 1.196765188513461586e+00 9.800073479202837667e-01 -9.240000000000000568e+01 5.931847037359385233e+00 1.196934335812684935e+00 9.799842597757840723e-01 -9.250000000000000000e+01 5.931766222383092035e+00 1.197103382724994391e+00 9.799611692962413301e-01 -9.260000000000000853e+01 5.931685408538366566e+00 1.197272329303420646e+00 9.799380764838764302e-01 -9.270000000000000284e+01 5.931604595825193726e+00 1.197441175600975516e+00 9.799149813409091525e-01 -9.280000000000001137e+01 5.931523784243558417e+00 1.197609921670653277e+00 9.798918838695579447e-01 -9.290000000000000568e+01 5.931442973793442874e+00 1.197778567565429775e+00 9.798687840720397002e-01 -9.300000000000000000e+01 5.931362164474831999e+00 1.197947113338262204e+00 9.798456819505705351e-01 -9.310000000000000853e+01 5.931281356287711581e+00 1.198115559042089773e+00 9.798225775073650112e-01 -9.320000000000000284e+01 5.931200549232063857e+00 1.198283904729834592e+00 9.797994707446363583e-01 -9.330000000000001137e+01 5.931119743307874614e+00 1.198452150454398790e+00 9.797763616645969176e-01 -9.340000000000000568e+01 5.931038938515126979e+00 1.198620296268667396e+00 9.797532502694575873e-01 -9.350000000000000000e+01 5.930958134853805852e+00 1.198788342225507009e+00 9.797301365614277113e-01 -9.360000000000000853e+01 5.930877332323893469e+00 1.198956288377764912e+00 9.797070205427159673e-01 -9.370000000000000284e+01 5.930796530925376508e+00 1.199124134778271511e+00 9.796839022155294785e-01 -9.380000000000001137e+01 5.930715730658237206e+00 1.199291881479837674e+00 9.796607815820743692e-01 -9.390000000000000568e+01 5.930634931522461351e+00 1.199459528535256725e+00 9.796376586445550982e-01 -9.400000000000000000e+01 5.930554133518032067e+00 1.199627075997303116e+00 9.796145334051750142e-01 -9.410000000000000853e+01 5.930473336644935145e+00 1.199794523918733313e+00 9.795914058661364665e-01 -9.420000000000000284e+01 5.930392540903152820e+00 1.199961872352285130e+00 9.795682760296403613e-01 -9.430000000000001137e+01 5.930311746292669994e+00 1.200129121350677730e+00 9.795451438978864944e-01 -9.440000000000000568e+01 5.930230952813471568e+00 1.200296270966612511e+00 9.795220094730733296e-01 -9.450000000000000000e+01 5.930150160465540665e+00 1.200463321252771776e+00 9.794988727573978871e-01 -9.460000000000000853e+01 5.930069369248861300e+00 1.200630272261819842e+00 9.794757337530562991e-01 -9.470000000000000284e+01 5.929988579163419260e+00 1.200797124046402153e+00 9.794525924622434765e-01 -9.480000000000001137e+01 5.929907790209196783e+00 1.200963876659145946e+00 9.794294488871527760e-01 -9.490000000000000568e+01 5.929827002386180546e+00 1.201130530152660025e+00 9.794063030299762218e-01 -9.500000000000000000e+01 5.929746215694352784e+00 1.201297084579534102e+00 9.793831548929053943e-01 -9.510000000000000853e+01 5.929665430133698401e+00 1.201463539992339902e+00 9.793600044781295422e-01 -9.520000000000000284e+01 5.929584645704199630e+00 1.201629896443630052e+00 9.793368517878375812e-01 -9.530000000000001137e+01 5.929503862405844927e+00 1.201796153985939641e+00 9.793136968242165397e-01 -9.540000000000000568e+01 5.929423080238614752e+00 1.201962312671783550e+00 9.792905395894526688e-01 -9.550000000000000000e+01 5.929342299202494893e+00 1.202128372553659563e+00 9.792673800857307764e-01 -9.560000000000000853e+01 5.929261519297467586e+00 1.202294333684046368e+00 9.792442183152346713e-01 -9.570000000000000284e+01 5.929180740523519511e+00 1.202460196115403779e+00 9.792210542801466078e-01 -9.580000000000001137e+01 5.929099962880633790e+00 1.202625959900173180e+00 9.791978879826475080e-01 -9.590000000000000568e+01 5.929019186368795324e+00 1.202791625090777528e+00 9.791747194249175168e-01 -9.600000000000000000e+01 5.928938410987988128e+00 1.202957191739620235e+00 9.791515486091351139e-01 -9.610000000000000853e+01 5.928857636738196213e+00 1.203122659899086955e+00 9.791283755374778908e-01 -9.620000000000000284e+01 5.928776863619403592e+00 1.203288029621544242e+00 9.791052002121219955e-01 -9.630000000000001137e+01 5.928696091631594278e+00 1.203453300959340000e+00 9.790820226352424660e-01 -9.640000000000000568e+01 5.928615320774751396e+00 1.203618473964803259e+00 9.790588428090128970e-01 -9.650000000000000000e+01 5.928534551048861623e+00 1.203783548690244176e+00 9.790356607356058838e-01 -9.660000000000000853e+01 5.928453782453908971e+00 1.203948525187955143e+00 9.790124764171926897e-01 -9.670000000000000284e+01 5.928373014989875678e+00 1.204113403510207903e+00 9.789892898559436896e-01 -9.680000000000001137e+01 5.928292248656745755e+00 1.204278183709257322e+00 9.789661010540272601e-01 -9.690000000000000568e+01 5.928211483454505881e+00 1.204442865837338283e+00 9.789429100136110007e-01 -9.700000000000000000e+01 5.928130719383137404e+00 1.204607449946667685e+00 9.789197167368616226e-01 -9.710000000000000853e+01 5.928049956442626112e+00 1.204771936089442663e+00 9.788965212259438387e-01 -9.720000000000000284e+01 5.927969194632956906e+00 1.204936324317841922e+00 9.788733234830216956e-01 -9.730000000000001137e+01 5.927888433954112912e+00 1.205100614684025739e+00 9.788501235102580189e-01 -9.740000000000000568e+01 5.927807674406077254e+00 1.205264807240134406e+00 9.788269213098141908e-01 -9.750000000000000000e+01 5.927726915988835721e+00 1.205428902038290673e+00 9.788037168838502611e-01 -9.760000000000000853e+01 5.927646158702372325e+00 1.205592899130597528e+00 9.787805102345253916e-01 -9.770000000000000284e+01 5.927565402546672857e+00 1.205756798569139310e+00 9.787573013639973007e-01 -9.780000000000001137e+01 5.927484647521717775e+00 1.205920600405981258e+00 9.787340902744224858e-01 -9.790000000000000568e+01 5.927403893627493758e+00 1.206084304693170184e+00 9.787108769679562226e-01 -9.800000000000000000e+01 5.927323140863985707e+00 1.206247911482732915e+00 9.786876614467527880e-01 -9.810000000000000853e+01 5.927242389231174968e+00 1.206411420826678293e+00 9.786644437129650154e-01 -9.820000000000000284e+01 5.927161638729048221e+00 1.206574832776995843e+00 9.786412237687445170e-01 -9.830000000000001137e+01 5.927080889357589477e+00 1.206738147385655768e+00 9.786180016162416839e-01 -9.840000000000000568e+01 5.927000141116782750e+00 1.206901364704609625e+00 9.785947772576056858e-01 -9.850000000000000000e+01 5.926919394006612052e+00 1.207064484785789871e+00 9.785715506949844711e-01 -9.860000000000000853e+01 5.926838648027058731e+00 1.207227507681110534e+00 9.785483219305248781e-01 -9.870000000000000284e+01 5.926757903178111242e+00 1.207390433442465438e+00 9.785250909663724128e-01 -9.880000000000001137e+01 5.926677159459752708e+00 1.207553262121729754e+00 9.785018578046713600e-01 -9.890000000000000568e+01 5.926596416871967143e+00 1.207715993770760443e+00 9.784786224475647831e-01 -9.900000000000000000e+01 5.926515675414737672e+00 1.207878628441393376e+00 9.784553848971945245e-01 -9.910000000000000853e+01 5.926434935088049194e+00 1.208041166185447546e+00 9.784321451557013161e-01 -9.920000000000000284e+01 5.926354195891886611e+00 1.208203607054721296e+00 9.784089032252247797e-01 -9.930000000000001137e+01 5.926273457826232161e+00 1.208365951100994762e+00 9.783856591079027609e-01 -9.940000000000000568e+01 5.926192720891073407e+00 1.208528198376028095e+00 9.783624128058723279e-01 -9.950000000000000000e+01 5.926111985086391698e+00 1.208690348931562797e+00 9.783391643212692168e-01 -9.960000000000000853e+01 5.926031250412171936e+00 1.208852402819321936e+00 9.783159136562283864e-01 -9.970000000000000284e+01 5.925950516868398132e+00 1.209014360091007934e+00 9.782926608128829082e-01 -9.980000000000001137e+01 5.925869784455056966e+00 1.209176220798305446e+00 9.782694057933647436e-01 -9.990000000000000568e+01 5.925789053172128895e+00 1.209337984992878035e+00 9.782461485998050765e-01 +1.000000000000000000e+01 9.990246818764599857e-01 1.018312644180662252e+00 9.979848153411911005e-01 +1.010000000000000142e+01 9.990129342034425042e-01 1.018491770775998173e+00 9.979645132537091623e-01 +1.020000000000000107e+01 9.990011866949118913e-01 1.018670818644186626e+00 9.979442082528212632e-01 +1.030000000000000071e+01 9.989894393508652604e-01 1.018849787810228502e+00 9.979239003403365116e-01 +1.040000000000000036e+01 9.989776921713007241e-01 1.019028678299129576e+00 9.979035895180630167e-01 +1.050000000000000000e+01 9.989659451562157289e-01 1.019207490135899175e+00 9.978832757878087767e-01 +1.060000000000000142e+01 9.989541983056080543e-01 1.019386223345552622e+00 9.978629591513812347e-01 +1.070000000000000107e+01 9.989424516194753689e-01 1.019564877953123005e+00 9.978426396105872787e-01 +1.080000000000000071e+01 9.989307050978152303e-01 1.019743453983624981e+00 9.978223171672321312e-01 +1.090000000000000036e+01 9.989189587406255288e-01 1.019921951462097187e+00 9.978019918231217922e-01 +1.100000000000000000e+01 9.989072125479041553e-01 1.020100370413577151e+00 9.977816635800610401e-01 +1.110000000000000142e+01 9.988954665196486671e-01 1.020278710863102622e+00 9.977613324398534322e-01 +1.120000000000000107e+01 9.988837206558566217e-01 1.020456972835728671e+00 9.977409984043029700e-01 +1.130000000000000071e+01 9.988719749565257988e-01 1.020635156356506146e+00 9.977206614752124336e-01 +1.140000000000000036e+01 9.988602294216540889e-01 1.020813261450496778e+00 9.977003216543841591e-01 +1.150000000000000000e+01 9.988484840512389384e-01 1.020991288142759412e+00 9.976799789436195942e-01 +1.160000000000000142e+01 9.988367388452780160e-01 1.021169236458364438e+00 9.976596333447198539e-01 +1.170000000000000107e+01 9.988249938037691011e-01 1.021347106422388906e+00 9.976392848594853868e-01 +1.180000000000000071e+01 9.988132489267098624e-01 1.021524898059908537e+00 9.976189334897156424e-01 +1.190000000000000036e+01 9.988015042140984123e-01 1.021702611396010374e+00 9.975985792372101812e-01 +1.200000000000000000e+01 9.987897596659319754e-01 1.021880246455777241e+00 9.975782221037670094e-01 +1.210000000000000142e+01 9.987780152822082202e-01 1.022057803264307063e+00 9.975578620911842442e-01 +1.220000000000000107e+01 9.987662710629249263e-01 1.022235281846701316e+00 9.975374992012593367e-01 +1.230000000000000071e+01 9.987545270080800952e-01 1.022412682228061254e+00 9.975171334357888497e-01 +1.240000000000000036e+01 9.987427831176710624e-01 1.022590004433496125e+00 9.974967647965685691e-01 +1.250000000000000000e+01 9.987310393916958295e-01 1.022767248488118286e+00 9.974763932853941695e-01 +1.260000000000000142e+01 9.987192958301518431e-01 1.022944414417046977e+00 9.974560189040603264e-01 +1.270000000000000107e+01 9.987075524330371046e-01 1.023121502245405878e+00 9.974356416543610493e-01 +1.280000000000000071e+01 9.986958092003491716e-01 1.023298511998321780e+00 9.974152615380900144e-01 +1.290000000000000036e+01 9.986840661320856016e-01 1.023475443700928134e+00 9.973948785570398989e-01 +1.300000000000000000e+01 9.986723232282440632e-01 1.023652297378363496e+00 9.973744927130032689e-01 +1.310000000000000142e+01 9.986605804888226690e-01 1.023829073055765537e+00 9.973541040077715802e-01 +1.320000000000000107e+01 9.986488379138187543e-01 1.024005770758284584e+00 9.973337124431358447e-01 +1.330000000000000071e+01 9.986370955032300989e-01 1.024182390511075624e+00 9.973133180208868520e-01 +1.340000000000000036e+01 9.986253532570543712e-01 1.024358932339290540e+00 9.972929207428138376e-01 +1.350000000000000000e+01 9.986136111752893507e-01 1.024535396268090981e+00 9.972725206107061480e-01 +1.360000000000000142e+01 9.986018692579328171e-01 1.024711782322639264e+00 9.972521176263521303e-01 +1.370000000000000107e+01 9.985901275049822168e-01 1.024888090528107476e+00 9.972317117915397988e-01 +1.380000000000000071e+01 9.985783859164353293e-01 1.025064320909667925e+00 9.972113031080562795e-01 +1.390000000000000036e+01 9.985666444922900453e-01 1.025240473492502913e+00 9.971908915776883653e-01 +1.400000000000000000e+01 9.985549032325439223e-01 1.025416548301794517e+00 9.971704772022220720e-01 +1.410000000000000142e+01 9.985431621371948507e-01 1.025592545362730812e+00 9.971500599834427492e-01 +1.420000000000000107e+01 9.985314212062403882e-01 1.025768464700501426e+00 9.971296399231350804e-01 +1.430000000000000071e+01 9.985196804396782033e-01 1.025944306340304424e+00 9.971092170230831941e-01 +1.440000000000000036e+01 9.985079398375059645e-01 1.026120070307340537e+00 9.970887912850708856e-01 +1.450000000000000000e+01 9.984961993997214513e-01 1.026295756626812716e+00 9.970683627108806180e-01 +1.460000000000000142e+01 9.984844591263223323e-01 1.026471365323932572e+00 9.970479313022948542e-01 +1.470000000000000107e+01 9.984727190173064981e-01 1.026646896423913935e+00 9.970274970610952803e-01 +1.480000000000000071e+01 9.984609790726712841e-01 1.026822349951973967e+00 9.970070599890628049e-01 +1.490000000000000036e+01 9.984492392924146920e-01 1.026997725933335159e+00 9.969866200879778928e-01 +1.500000000000000000e+01 9.984374996765342791e-01 1.027173024393220224e+00 9.969661773596198984e-01 +1.510000000000000142e+01 9.984257602250279362e-01 1.027348245356863199e+00 9.969457318057682871e-01 +1.520000000000000107e+01 9.984140209378933317e-01 1.027523388849497676e+00 9.969252834282014142e-01 +1.530000000000000071e+01 9.984022818151278011e-01 1.027698454896366131e+00 9.969048322286975239e-01 +1.540000000000000036e+01 9.983905428567294571e-01 1.027873443522706154e+00 9.968843782090333061e-01 +1.550000000000000000e+01 9.983788040626959681e-01 1.028048354753766880e+00 9.968639213709855618e-01 +1.560000000000000142e+01 9.983670654330246697e-01 1.028223188614799666e+00 9.968434617163303146e-01 +1.570000000000000107e+01 9.983553269677137854e-01 1.028397945131058977e+00 9.968229992468430334e-01 +1.580000000000000071e+01 9.983435886667607617e-01 1.028572624327803053e+00 9.968025339642982985e-01 +1.590000000000000036e+01 9.983318505301632673e-01 1.028747226230295464e+00 9.967820658704702463e-01 +1.600000000000000000e+01 9.983201125579191926e-01 1.028921750863801554e+00 9.967615949671323472e-01 +1.610000000000000142e+01 9.983083747500259841e-01 1.029096198253594663e+00 9.967411212560575162e-01 +1.619999999999999929e+01 9.982966371064815325e-01 1.029270568424946797e+00 9.967206447390175583e-01 +1.630000000000000071e+01 9.982848996272832842e-01 1.029444861403139511e+00 9.967001654177845005e-01 +1.640000000000000213e+01 9.982731623124290188e-01 1.029619077213452361e+00 9.966796832941293705e-01 +1.650000000000000000e+01 9.982614251619165158e-01 1.029793215881172008e+00 9.966591983698220858e-01 +1.660000000000000142e+01 9.982496881757436658e-01 1.029967277431588446e+00 9.966387106466326751e-01 +1.669999999999999929e+01 9.982379513539079152e-01 1.030141261889997439e+00 9.966182201263300566e-01 +1.680000000000000071e+01 9.982262146964071547e-01 1.030315169281692977e+00 9.965977268106825937e-01 +1.690000000000000213e+01 9.982144782032389418e-01 1.030488999631977487e+00 9.965772307014584275e-01 +1.700000000000000000e+01 9.982027418744009450e-01 1.030662752966158502e+00 9.965567318004245889e-01 +1.710000000000000142e+01 9.981910057098911659e-01 1.030836429309540225e+00 9.965362301093474429e-01 +1.719999999999999929e+01 9.981792697097068290e-01 1.031010028687437963e+00 9.965157256299932431e-01 +1.730000000000000071e+01 9.981675338738461578e-01 1.031183551125165465e+00 9.964952183641269112e-01 +1.740000000000000213e+01 9.981557982023064879e-01 1.031356996648043367e+00 9.964747083135135908e-01 +1.750000000000000000e+01 9.981440626950857098e-01 1.031530365281393413e+00 9.964541954799169821e-01 +1.760000000000000142e+01 9.981323273521813810e-01 1.031703657050543788e+00 9.964336798651005633e-01 +1.769999999999999929e+01 9.981205921735913922e-01 1.031876871980822230e+00 9.964131614708272577e-01 +1.780000000000000071e+01 9.981088571593134118e-01 1.032050010097564252e+00 9.963926402988591002e-01 +1.790000000000000213e+01 9.980971223093448863e-01 1.032223071426106698e+00 9.963721163509576817e-01 +1.800000000000000000e+01 9.980853876236837063e-01 1.032396055991787298e+00 9.963515896288839269e-01 +1.810000000000000142e+01 9.980736531023276514e-01 1.032568963819952002e+00 9.963310601343982054e-01 +1.819999999999999929e+01 9.980619187452742791e-01 1.032741794935947865e+00 9.963105278692598876e-01 +1.830000000000000071e+01 9.980501845525214799e-01 1.032914549365124834e+00 9.962899928352282330e-01 +1.840000000000000213e+01 9.980384505240667004e-01 1.033087227132837516e+00 9.962694550340615018e-01 +1.850000000000000000e+01 9.980267166599081641e-01 1.033259828264442515e+00 9.962489144675173991e-01 +1.860000000000000142e+01 9.980149829600430955e-01 1.033432352785300656e+00 9.962283711373531858e-01 +1.869999999999999929e+01 9.980032494244692742e-01 1.033604800720776984e+00 9.962078250453255679e-01 +1.880000000000000071e+01 9.979915160531841467e-01 1.033777172096236541e+00 9.961872761931899189e-01 +1.890000000000000213e+01 9.979797828461859366e-01 1.033949466937049255e+00 9.961667245827018347e-01 +1.900000000000000000e+01 9.979680498034720904e-01 1.034121685268589497e+00 9.961461702156158005e-01 +1.910000000000000142e+01 9.979563169250402765e-01 1.034293827116233855e+00 9.961256130936857467e-01 +1.920000000000000284e+01 9.979445842108884968e-01 1.034465892505362028e+00 9.961050532186653816e-01 +1.930000000000000071e+01 9.979328516610139754e-01 1.034637881461356823e+00 9.960844905923070813e-01 +1.940000000000000213e+01 9.979211192754148252e-01 1.034809794009605044e+00 9.960639252163633328e-01 +1.950000000000000000e+01 9.979093870540884925e-01 1.034981630175494161e+00 9.960433570925849578e-01 +1.960000000000000142e+01 9.978976549970329790e-01 1.035153389984418304e+00 9.960227862227233331e-01 +1.970000000000000284e+01 9.978859231042455091e-01 1.035325073461770939e+00 9.960022126085285032e-01 +1.980000000000000071e+01 9.978741913757241955e-01 1.035496680632949973e+00 9.959816362517497357e-01 +1.990000000000000213e+01 9.978624598114665956e-01 1.035668211523358195e+00 9.959610571541364088e-01 +2.000000000000000000e+01 9.978507284114704889e-01 1.035839666158399286e+00 9.959404753174367908e-01 +2.010000000000000142e+01 9.978389971757334331e-01 1.036011044563480032e+00 9.959198907433984838e-01 +2.020000000000000284e+01 9.978272661042532077e-01 1.036182346764009665e+00 9.958993034337687567e-01 +2.030000000000000071e+01 9.978155351970274811e-01 1.036353572785401411e+00 9.958787133902936572e-01 +2.040000000000000213e+01 9.978038044540540330e-01 1.036524722653072272e+00 9.958581206147192333e-01 +2.050000000000000000e+01 9.977920738753305319e-01 1.036695796392439473e+00 9.958375251087905333e-01 +2.060000000000000142e+01 9.977803434608546462e-01 1.036866794028924454e+00 9.958169268742522728e-01 +2.070000000000000284e+01 9.977686132106241557e-01 1.037037715587951103e+00 9.957963259128480571e-01 +2.080000000000000071e+01 9.977568831246368397e-01 1.037208561094948633e+00 9.957757222263214913e-01 +2.090000000000000213e+01 9.977451532028904779e-01 1.037379330575342928e+00 9.957551158164150706e-01 +2.100000000000000000e+01 9.977334234453824058e-01 1.037550024054568976e+00 9.957345066848706239e-01 +2.110000000000000142e+01 9.977216938521104028e-01 1.037720641558061985e+00 9.957138948334299799e-01 +2.120000000000000284e+01 9.977099644230723596e-01 1.037891183111260052e+00 9.956932802638336355e-01 +2.130000000000000071e+01 9.976982351582662778e-01 1.038061648739602605e+00 9.956726629778216431e-01 +2.140000000000000213e+01 9.976865060576892708e-01 1.038232038468533291e+00 9.956520429771337222e-01 +2.150000000000000000e+01 9.976747771213394511e-01 1.038402352323497757e+00 9.956314202635084820e-01 +2.160000000000000142e+01 9.976630483492141543e-01 1.038572590329944756e+00 9.956107948386843098e-01 +2.170000000000000284e+01 9.976513197413113820e-01 1.038742752513323042e+00 9.955901667043987047e-01 +2.180000000000000071e+01 9.976395912976285807e-01 1.038912838899087587e+00 9.955695358623888325e-01 +2.190000000000000213e+01 9.976278630181637519e-01 1.039082849512696027e+00 9.955489023143908600e-01 +2.200000000000000000e+01 9.976161349029143421e-01 1.039252784379604888e+00 9.955282660621406210e-01 +2.210000000000000142e+01 9.976044069518782420e-01 1.039422643525276024e+00 9.955076271073733940e-01 +2.220000000000000284e+01 9.975926791650533421e-01 1.039592426975171735e+00 9.954869854518232364e-01 +2.230000000000000071e+01 9.975809515424370888e-01 1.039762134754758760e+00 9.954663410972240944e-01 +2.240000000000000213e+01 9.975692240840269287e-01 1.039931766889505171e+00 9.954456940453092484e-01 +2.250000000000000000e+01 9.975574967898208634e-01 1.040101323404881484e+00 9.954250442978110902e-01 +2.260000000000000142e+01 9.975457696598165613e-01 1.040270804326359988e+00 9.954043918564616789e-01 +2.270000000000000284e+01 9.975340426940119132e-01 1.040440209679417638e+00 9.953837367229921851e-01 +2.280000000000000071e+01 9.975223158924043654e-01 1.040609539489530277e+00 9.953630788991334466e-01 +2.290000000000000213e+01 9.975105892549915865e-01 1.040778793782179079e+00 9.953424183866155239e-01 +2.300000000000000000e+01 9.974988627817715781e-01 1.040947972582845438e+00 9.953217551871675894e-01 +2.310000000000000142e+01 9.974871364727417866e-01 1.041117075917014745e+00 9.953010893025187045e-01 +2.320000000000000284e+01 9.974754103279002138e-01 1.041286103810172170e+00 9.952804207343968201e-01 +2.330000000000000071e+01 9.974636843472443060e-01 1.041455056287808212e+00 9.952597494845294435e-01 +2.340000000000000213e+01 9.974519585307716207e-01 1.041623933375413591e+00 9.952390755546438594e-01 +2.350000000000000000e+01 9.974402328784800487e-01 1.041792735098481693e+00 9.952183989464656877e-01 +2.360000000000000142e+01 9.974285073903674803e-01 1.041961461482506346e+00 9.951977196617211030e-01 +2.370000000000000284e+01 9.974167820664313622e-01 1.042130112552987153e+00 9.951770377021347258e-01 +2.380000000000000071e+01 9.974050569066695848e-01 1.042298688335422163e+00 9.951563530694309545e-01 +2.390000000000000213e+01 9.973933319110797058e-01 1.042467188855314086e+00 9.951356657653337434e-01 +2.400000000000000000e+01 9.973816070796596156e-01 1.042635614138165856e+00 9.951149757915659366e-01 +2.410000000000000142e+01 9.973698824124067608e-01 1.042803964209484180e+00 9.950942831498501562e-01 +2.420000000000000284e+01 9.973581579093189209e-01 1.042972239094775988e+00 9.950735878419080249e-01 +2.430000000000000071e+01 9.973464335703939865e-01 1.043140438819551985e+00 9.950528898694610547e-01 +2.440000000000000213e+01 9.973347093956295151e-01 1.043308563409323320e+00 9.950321892342295360e-01 +2.450000000000000000e+01 9.973229853850231752e-01 1.043476612889603583e+00 9.950114859379335375e-01 +2.460000000000000142e+01 9.973112615385727464e-01 1.043644587285908365e+00 9.949907799822923504e-01 +2.470000000000000284e+01 9.972995378562760083e-01 1.043812486623755476e+00 9.949700713690247111e-01 +2.480000000000000071e+01 9.972878143381302962e-01 1.043980310928664057e+00 9.949493600998486897e-01 +2.490000000000000213e+01 9.972760909841337229e-01 1.044148060226154806e+00 9.949286461764815792e-01 +2.500000000000000000e+01 9.972643677942839568e-01 1.044315734541751528e+00 9.949079296006402284e-01 +2.510000000000000142e+01 9.972526447685785556e-01 1.044483333900978694e+00 9.948872103740405981e-01 +2.520000000000000284e+01 9.972409219070152986e-01 1.044650858329363663e+00 9.948664884983985379e-01 +2.530000000000000071e+01 9.972291992095919655e-01 1.044818307852434014e+00 9.948457639754286763e-01 +2.540000000000000213e+01 9.972174766763063358e-01 1.044985682495720214e+00 9.948250368068455307e-01 +2.550000000000000000e+01 9.972057543071557451e-01 1.045152982284754506e+00 9.948043069943623973e-01 +2.560000000000000142e+01 9.971940321021380838e-01 1.045320207245070465e+00 9.947835745396924612e-01 +2.570000000000000284e+01 9.971823100612511315e-01 1.045487357402204109e+00 9.947628394445482414e-01 +2.580000000000000071e+01 9.971705881844926678e-01 1.045654432781691234e+00 9.947421017106411467e-01 +2.590000000000000213e+01 9.971588664718601391e-01 1.045821433409070966e+00 9.947213613396824750e-01 +2.600000000000000000e+01 9.971471449233513251e-01 1.045988359309883542e+00 9.947006183333827467e-01 +2.610000000000000142e+01 9.971354235389643383e-01 1.046155210509670974e+00 9.946798726934515944e-01 +2.620000000000000284e+01 9.971237023186962922e-01 1.046321987033976830e+00 9.946591244215983174e-01 +2.630000000000000071e+01 9.971119812625454104e-01 1.046488688908345122e+00 9.946383735195314379e-01 +2.640000000000000213e+01 9.971002603705090284e-01 1.046655316158322524e+00 9.946176199889588121e-01 +2.650000000000000000e+01 9.970885396425849256e-01 1.046821868809458822e+00 9.945968638315880739e-01 +2.660000000000000142e+01 9.970768190787708818e-01 1.046988346887302024e+00 9.945761050491257471e-01 +2.670000000000000284e+01 9.970650986790644543e-01 1.047154750417403912e+00 9.945553436432776895e-01 +2.680000000000000071e+01 9.970533784434636448e-01 1.047321079425316048e+00 9.945345796157495366e-01 +2.690000000000000213e+01 9.970416583719660109e-01 1.047487333936593767e+00 9.945138129682459249e-01 +2.700000000000000000e+01 9.970299384645693319e-01 1.047653513976791517e+00 9.944930437024711578e-01 +2.710000000000000142e+01 9.970182187212712766e-01 1.047819619571466632e+00 9.944722718201286504e-01 +2.720000000000000284e+01 9.970064991420694023e-01 1.047985650746176889e+00 9.944514973229214849e-01 +2.730000000000000071e+01 9.969947797269614886e-01 1.048151607526481177e+00 9.944307202125516332e-01 +2.740000000000000213e+01 9.969830604759453152e-01 1.048317489937941049e+00 9.944099404907209561e-01 +2.750000000000000000e+01 9.969713413890185505e-01 1.048483298006117836e+00 9.943891581591300932e-01 +2.760000000000000142e+01 9.969596224661787520e-01 1.048649031756575978e+00 9.943683732194796843e-01 +2.770000000000000284e+01 9.969479037074239214e-01 1.048814691214879913e+00 9.943475856734693696e-01 +2.780000000000000071e+01 9.969361851127515051e-01 1.048980276406594303e+00 9.943267955227984567e-01 +2.790000000000000213e+01 9.969244666821592826e-01 1.049145787357287585e+00 9.943060027691651426e-01 +2.800000000000000000e+01 9.969127484156451446e-01 1.049311224092528194e+00 9.942852074142675134e-01 +2.810000000000000142e+01 9.969010303132065376e-01 1.049476586637884568e+00 9.942644094598025450e-01 +2.820000000000000284e+01 9.968893123748411300e-01 1.049641875018929138e+00 9.942436089074667693e-01 +2.830000000000000071e+01 9.968775946005470345e-01 1.049807089261232340e+00 9.942228057589563850e-01 +2.840000000000000213e+01 9.968658769903215866e-01 1.049972229390367939e+00 9.942020000159665916e-01 +2.850000000000000000e+01 9.968541595441625658e-01 1.050137295431909035e+00 9.941811916801918114e-01 +2.860000000000000142e+01 9.968424422620677516e-01 1.050302287411432500e+00 9.941603807533263559e-01 +2.870000000000000284e+01 9.968307251440349237e-01 1.050467205354513878e+00 9.941395672370635372e-01 +2.880000000000000071e+01 9.968190081900614175e-01 1.050632049286729597e+00 9.941187511330962234e-01 +2.890000000000000213e+01 9.968072914001455676e-01 1.050796819233658752e+00 9.940979324431165054e-01 +2.900000000000000000e+01 9.967955747742847095e-01 1.050961515220880216e+00 9.940771111688159190e-01 +2.910000000000000142e+01 9.967838583124765117e-01 1.051126137273975081e+00 9.940562873118853338e-01 +2.920000000000000284e+01 9.967721420147186429e-01 1.051290685418523774e+00 9.940354608740150644e-01 +2.930000000000000071e+01 9.967604258810088824e-01 1.051455159680109608e+00 9.940146318568945372e-01 +2.940000000000000213e+01 9.967487099113451210e-01 1.051619560084315008e+00 9.939938002622128455e-01 +2.950000000000000000e+01 9.967369941057248051e-01 1.051783886656723954e+00 9.939729660916581944e-01 +2.960000000000000142e+01 9.967252784641458252e-01 1.051948139422922202e+00 9.939521293469186780e-01 +2.970000000000000284e+01 9.967135629866059610e-01 1.052112318408494840e+00 9.939312900296810582e-01 +2.980000000000000071e+01 9.967018476731026588e-01 1.052276423639028735e+00 9.939104481416319858e-01 +2.990000000000000213e+01 9.966901325236334763e-01 1.052440455140111863e+00 9.938896036844573345e-01 +3.000000000000000000e+01 9.966784175381965261e-01 1.052604412937332867e+00 9.938687566598422007e-01 +3.010000000000000142e+01 9.966667027167892545e-01 1.052768297056280167e+00 9.938479070694710149e-01 +3.020000000000000284e+01 9.966549880594096633e-01 1.052932107522543959e+00 9.938270549150278743e-01 +3.030000000000000071e+01 9.966432735660551989e-01 1.053095844361714217e+00 9.938062001981958771e-01 +3.040000000000000213e+01 9.966315592367236409e-01 1.053259507599382694e+00 9.937853429206578992e-01 +3.050000000000000000e+01 9.966198450714128798e-01 1.053423097261141361e+00 9.937644830840959287e-01 +3.060000000000000142e+01 9.966081310701203622e-01 1.053586613372583747e+00 9.937436206901913982e-01 +3.070000000000000284e+01 9.965964172328439785e-01 1.053750055959303156e+00 9.937227557406250744e-01 +3.080000000000000071e+01 9.965847035595813974e-01 1.053913425046893781e+00 9.937018882370770578e-01 +3.090000000000000213e+01 9.965729900503302874e-01 1.054076720660950262e+00 9.936810181812268938e-01 +3.100000000000000000e+01 9.965612767050882059e-01 1.054239942827067900e+00 9.936601455747533507e-01 +3.110000000000000142e+01 9.965495635238531547e-01 1.054403091570842443e+00 9.936392704193349745e-01 +3.120000000000000284e+01 9.965378505066223580e-01 1.054566166917870751e+00 9.936183927166490903e-01 +3.130000000000000071e+01 9.965261376533940396e-01 1.054729168893749680e+00 9.935975124683725790e-01 +3.140000000000000213e+01 9.965144249641657570e-01 1.054892097524077421e+00 9.935766296761819882e-01 +3.150000000000000000e+01 9.965027124389352897e-01 1.055054952834451720e+00 9.935557443417531998e-01 +3.160000000000000142e+01 9.964910000777004173e-01 1.055217734850471434e+00 9.935348564667612070e-01 +3.170000000000000284e+01 9.964792878804584753e-01 1.055380443597735196e+00 9.935139660528804484e-01 +3.180000000000000071e+01 9.964675758472072431e-01 1.055543079101842752e+00 9.934930731017844741e-01 +3.190000000000000213e+01 9.964558639779446114e-01 1.055705641388395177e+00 9.934721776151467232e-01 +3.200000000000000000e+01 9.964441522726682487e-01 1.055868130482992884e+00 9.934512795946397468e-01 +3.210000000000000142e+01 9.964324407313758236e-01 1.056030546411236060e+00 9.934303790419354296e-01 +3.220000000000000284e+01 9.964207293540652266e-01 1.056192889198725782e+00 9.934094759587051016e-01 +3.230000000000000426e+01 9.964090181407340152e-01 1.056355158871064237e+00 9.933885703466195372e-01 +3.239999999999999858e+01 9.963973070913798580e-01 1.056517355453853169e+00 9.933676622073485118e-01 +3.250000000000000000e+01 9.963855962060004234e-01 1.056679478972695430e+00 9.933467515425615790e-01 +3.260000000000000142e+01 9.963738854845933801e-01 1.056841529453193651e+00 9.933258383539274039e-01 +3.270000000000000284e+01 9.963621749271567296e-01 1.057003506920950686e+00 9.933049226431142076e-01 +3.280000000000000426e+01 9.963504645336880294e-01 1.057165411401569610e+00 9.932840044117894340e-01 +3.289999999999999858e+01 9.963387543041848371e-01 1.057327242920653276e+00 9.932630836616200831e-01 +3.300000000000000000e+01 9.963270442386450432e-01 1.057489001503806758e+00 9.932421603942723776e-01 +3.310000000000000142e+01 9.963153343370663162e-01 1.057650687176633797e+00 9.932212346114120960e-01 +3.320000000000000284e+01 9.963036245994463247e-01 1.057812299964737912e+00 9.932003063147040178e-01 +3.330000000000000426e+01 9.962919150257829592e-01 1.057973839893724177e+00 9.931793755058122564e-01 +3.339999999999999858e+01 9.962802056160736663e-01 1.058135306989196556e+00 9.931584421864008139e-01 +3.350000000000000000e+01 9.962684963703163366e-01 1.058296701276760121e+00 9.931375063581328044e-01 +3.360000000000000142e+01 9.962567872885087494e-01 1.058458022782019947e+00 9.931165680226706760e-01 +3.370000000000000284e+01 9.962450783706481294e-01 1.058619271530579997e+00 9.930956271816760994e-01 +3.380000000000000426e+01 9.962333696167328112e-01 1.058780447548046011e+00 9.930746838368103013e-01 +3.389999999999999858e+01 9.962216610267602412e-01 1.058941550860022840e+00 9.930537379897339534e-01 +3.400000000000000000e+01 9.962099526007279771e-01 1.059102581492115558e+00 9.930327896421068390e-01 +3.410000000000000142e+01 9.961982443386339092e-01 1.059263539469929682e+00 9.930118387955884085e-01 +3.420000000000000284e+01 9.961865362404757063e-01 1.059424424819069843e+00 9.929908854518373351e-01 +3.430000000000000426e+01 9.961748283062511478e-01 1.059585237565142002e+00 9.929699296125115149e-01 +3.439999999999999858e+01 9.961631205359577912e-01 1.059745977733751010e+00 9.929489712792686218e-01 +3.450000000000000000e+01 9.961514129295935271e-01 1.059906645350501719e+00 9.929280104537651086e-01 +3.460000000000000142e+01 9.961397054871559131e-01 1.060067240440999647e+00 9.929070471376573170e-01 +3.470000000000000284e+01 9.961279982086426177e-01 1.060227763030849646e+00 9.928860813326007007e-01 +3.480000000000000426e+01 9.961162910940513093e-01 1.060388213145657232e+00 9.928651130402500469e-01 +3.489999999999999858e+01 9.961045841433801007e-01 1.060548590811026370e+00 9.928441422622598100e-01 +3.500000000000000000e+01 9.960928773566263272e-01 1.060708896052561911e+00 9.928231690002834453e-01 +3.510000000000000142e+01 9.960811707337876575e-01 1.060869128895868929e+00 9.928021932559739637e-01 +3.520000000000000284e+01 9.960694642748620931e-01 1.061029289366551831e+00 9.927812150309837103e-01 +3.530000000000000426e+01 9.960577579798471914e-01 1.061189377490214802e+00 9.927602343269643637e-01 +3.539999999999999858e+01 9.960460518487407322e-01 1.061349393292462029e+00 9.927392511455670476e-01 +3.550000000000000000e+01 9.960343458815402728e-01 1.061509336798897030e+00 9.927182654884423307e-01 +3.560000000000000142e+01 9.960226400782435929e-01 1.061669208035123768e+00 9.926972773572398934e-01 +3.570000000000000284e+01 9.960109344388484720e-01 1.061829007026745542e+00 9.926762867536089718e-01 +3.580000000000000426e+01 9.959992289633524676e-01 1.061988733799365647e+00 9.926552936791981363e-01 +3.589999999999999858e+01 9.959875236517533592e-01 1.062148388378586272e+00 9.926342981356554018e-01 +3.600000000000000000e+01 9.959758185040490375e-01 1.062307970790010492e+00 9.926133001246280063e-01 +3.610000000000000142e+01 9.959641135202368378e-01 1.062467481059240493e+00 9.925922996477626326e-01 +3.620000000000000284e+01 9.959524087003147619e-01 1.062626919211878018e+00 9.925712967067054082e-01 +3.630000000000000426e+01 9.959407040442805892e-01 1.062786285273525033e+00 9.925502913031017949e-01 +3.639999999999999858e+01 9.959289995521318772e-01 1.062945579269782392e+00 9.925292834385964769e-01 +3.650000000000000000e+01 9.959172952238661836e-01 1.063104801226250729e+00 9.925082731148333615e-01 +3.660000000000000142e+01 9.959055910594816208e-01 1.063263951168530896e+00 9.924872603334563559e-01 +3.670000000000000284e+01 9.958938870589756354e-01 1.063423029122222419e+00 9.924662450961081461e-01 +3.680000000000000426e+01 9.958821832223457848e-01 1.063582035112924373e+00 9.924452274044309741e-01 +3.689999999999999858e+01 9.958704795495900708e-01 1.063740969166236727e+00 9.924242072600667486e-01 +3.700000000000000000e+01 9.958587760407061618e-01 1.063899831307758337e+00 9.924031846646560462e-01 +3.710000000000000142e+01 9.958470726956917263e-01 1.064058621563086726e+00 9.923821596198396655e-01 +3.720000000000000284e+01 9.958353695145443218e-01 1.064217339957819197e+00 9.923611321272569619e-01 +3.730000000000000426e+01 9.958236664972617280e-01 1.064375986517553274e+00 9.923401021885472906e-01 +3.739999999999999858e+01 9.958119636438418354e-01 1.064534561267885371e+00 9.923190698053491188e-01 +3.750000000000000000e+01 9.958002609542822015e-01 1.064693064234411457e+00 9.922980349793001364e-01 +3.760000000000000142e+01 9.957885584285806058e-01 1.064851495442727503e+00 9.922769977120375895e-01 +3.770000000000000284e+01 9.957768560667347169e-01 1.065009854918427701e+00 9.922559580051982797e-01 +3.780000000000000426e+01 9.957651538687423143e-01 1.065168142687106689e+00 9.922349158604180097e-01 +3.789999999999999858e+01 9.957534518346009556e-01 1.065326358774357773e+00 9.922138712793321380e-01 +3.800000000000000000e+01 9.957417499643085312e-01 1.065484503205774480e+00 9.921928242635751349e-01 +3.810000000000000142e+01 9.957300482578625989e-01 1.065642576006948339e+00 9.921717748147813598e-01 +3.820000000000000284e+01 9.957183467152608269e-01 1.065800577203471100e+00 9.921507229345842838e-01 +3.830000000000000426e+01 9.957066453365008840e-01 1.065958506820934071e+00 9.921296686246164898e-01 +3.840000000000000568e+01 9.956949441215808827e-01 1.066116364884927448e+00 9.921086118865101167e-01 +3.850000000000000000e+01 9.956832430704982695e-01 1.066274151421040317e+00 9.920875527218968593e-01 +3.860000000000000142e+01 9.956715421832506019e-01 1.066431866454861987e+00 9.920664911324076352e-01 +3.870000000000000284e+01 9.956598414598357705e-01 1.066589510011980879e+00 9.920454271196728069e-01 +3.880000000000000426e+01 9.956481409002515548e-01 1.066747082117983192e+00 9.920243606853217377e-01 +3.890000000000000568e+01 9.956364405044955124e-01 1.066904582798455570e+00 9.920032918309837910e-01 +3.900000000000000000e+01 9.956247402725654227e-01 1.067062012078983990e+00 9.919822205582868868e-01 +3.910000000000000142e+01 9.956130402044591765e-01 1.067219369985153099e+00 9.919611468688591671e-01 +3.920000000000000284e+01 9.956013403001742201e-01 1.067376656542547320e+00 9.919400707643279969e-01 +3.930000000000000426e+01 9.955896405597083332e-01 1.067533871776749521e+00 9.919189922463194087e-01 +3.940000000000000568e+01 9.955779409830591842e-01 1.067691015713341240e+00 9.918979113164595462e-01 +3.950000000000000000e+01 9.955662415702245527e-01 1.067848088377905347e+00 9.918768279763735540e-01 +3.960000000000000142e+01 9.955545423212022182e-01 1.068005089796021156e+00 9.918557422276860214e-01 +3.970000000000000284e+01 9.955428432359897384e-01 1.068162019993268208e+00 9.918346540720209825e-01 +3.980000000000000426e+01 9.955311443145848926e-01 1.068318878995225374e+00 9.918135635110019166e-01 +3.990000000000000568e+01 9.955194455569854606e-01 1.068475666827470638e+00 9.917924705462514146e-01 +4.000000000000000000e+01 9.955077469631888887e-01 1.068632383515579987e+00 9.917713751793914012e-01 +4.010000000000000142e+01 9.954960485331932896e-01 1.068789029085129627e+00 9.917502774120434683e-01 +4.020000000000000284e+01 9.954843502669959987e-01 1.068945603561693991e+00 9.917291772458286525e-01 +4.030000000000000426e+01 9.954726521645950177e-01 1.069102106970847066e+00 9.917080746823671022e-01 +4.040000000000000568e+01 9.954609542259879040e-01 1.069258539338161063e+00 9.916869697232781888e-01 +4.050000000000000000e+01 9.954492564511724373e-01 1.069414900689208414e+00 9.916658623701809505e-01 +4.060000000000000142e+01 9.954375588401462860e-01 1.069571191049558445e+00 9.916447526246937594e-01 +4.070000000000000284e+01 9.954258613929071187e-01 1.069727410444781590e+00 9.916236404884343214e-01 +4.080000000000000426e+01 9.954141641094529369e-01 1.069883558900445619e+00 9.916025259630197874e-01 +4.090000000000000568e+01 9.954024669897811872e-01 1.070039636442118081e+00 9.915814090500664202e-01 +4.100000000000000000e+01 9.953907700338895381e-01 1.070195643095365190e+00 9.915602897511899272e-01 +4.110000000000000142e+01 9.953790732417757692e-01 1.070351578885751165e+00 9.915391680680057940e-01 +4.120000000000000284e+01 9.953673766134375489e-01 1.070507443838840222e+00 9.915180440021283959e-01 +4.130000000000000426e+01 9.953556801488727679e-01 1.070663237980195470e+00 9.914969175551716640e-01 +4.140000000000000568e+01 9.953439838480788726e-01 1.070818961335377573e+00 9.914757887287489746e-01 +4.150000000000000000e+01 9.953322877110538647e-01 1.070974613929947417e+00 9.914546575244728155e-01 +4.160000000000000142e+01 9.953205917377953016e-01 1.071130195789463668e+00 9.914335239439553416e-01 +4.170000000000000284e+01 9.953088959283007409e-01 1.071285706939483662e+00 9.914123879888079305e-01 +4.180000000000000426e+01 9.952972002825681841e-01 1.071441147405564953e+00 9.913912496606414049e-01 +4.190000000000000568e+01 9.952855048005951888e-01 1.071596517213261768e+00 9.913701089610659212e-01 +4.200000000000000000e+01 9.952738094823794235e-01 1.071751816388128553e+00 9.913489658916909697e-01 +4.210000000000000142e+01 9.952621143279185567e-01 1.071907044955717980e+00 9.913278204541252636e-01 +4.220000000000000284e+01 9.952504193372105901e-01 1.072062202941581166e+00 9.913066726499772940e-01 +4.230000000000000426e+01 9.952387245102531921e-01 1.072217290371268339e+00 9.912855224808544419e-01 +4.240000000000000568e+01 9.952270298470436982e-01 1.072372307270327507e+00 9.912643699483639770e-01 +4.250000000000000000e+01 9.952153353475802211e-01 1.072527253664306901e+00 9.912432150541121700e-01 +4.260000000000000142e+01 9.952036410118599852e-01 1.072682129578751642e+00 9.912220577997047366e-01 +4.270000000000000284e+01 9.951919468398812141e-01 1.072836935039206407e+00 9.912008981867467261e-01 +4.280000000000000426e+01 9.951802528316414653e-01 1.072991670071214765e+00 9.911797362168428549e-01 +4.290000000000000568e+01 9.951685589871382964e-01 1.073146334700318061e+00 9.911585718915969512e-01 +4.300000000000000000e+01 9.951568653063695979e-01 1.073300928952056532e+00 9.911374052126121770e-01 +4.310000000000000142e+01 9.951451717893331494e-01 1.073455452851968861e+00 9.911162361814911392e-01 +4.320000000000000284e+01 9.951334784360263974e-01 1.073609906425592841e+00 9.910950647998357788e-01 +4.330000000000000426e+01 9.951217852464473435e-01 1.073764289698463603e+00 9.910738910692474812e-01 +4.340000000000000568e+01 9.951100922205935451e-01 1.073918602696115832e+00 9.910527149913268552e-01 +4.350000000000000000e+01 9.950983993584626708e-01 1.074072845444081992e+00 9.910315365676742871e-01 +4.360000000000000142e+01 9.950867066600525002e-01 1.074227017967894104e+00 9.910103557998889423e-01 +4.370000000000000284e+01 9.950750141253607017e-01 1.074381120293081082e+00 9.909891726895696529e-01 +4.380000000000000426e+01 9.950633217543850551e-01 1.074535152445171393e+00 9.909679872383149180e-01 +4.390000000000000568e+01 9.950516295471231176e-01 1.074689114449692173e+00 9.909467994477220154e-01 +4.400000000000000000e+01 9.950399375035726690e-01 1.074843006332167672e+00 9.909256093193881121e-01 +4.410000000000000142e+01 9.950282456237315998e-01 1.074996828118121694e+00 9.909044168549093756e-01 +4.420000000000000284e+01 9.950165539075974674e-01 1.075150579833075826e+00 9.908832220558817516e-01 +4.430000000000000426e+01 9.950048623551679405e-01 1.075304261502550762e+00 9.908620249239001865e-01 +4.440000000000000568e+01 9.949931709664409096e-01 1.075457873152064092e+00 9.908408254605591825e-01 +4.450000000000000000e+01 9.949814797414138212e-01 1.075611414807133404e+00 9.908196236674523538e-01 +4.460000000000000142e+01 9.949697886800846769e-01 1.075764886493273398e+00 9.907984195461730925e-01 +4.470000000000000284e+01 9.949580977824509231e-01 1.075918288235997666e+00 9.907772130983137915e-01 +4.480000000000000426e+01 9.949464070485106726e-01 1.076071620060818246e+00 9.907560043254666216e-01 +4.490000000000000568e+01 9.949347164782611497e-01 1.076224881993244953e+00 9.907347932292227544e-01 +4.500000000000000000e+01 9.949230260717003560e-01 1.076378074058785828e+00 9.907135798111729175e-01 +4.510000000000000142e+01 9.949113358288260711e-01 1.076531196282947800e+00 9.906923640729071723e-01 +4.520000000000000284e+01 9.948996457496356305e-01 1.076684248691235357e+00 9.906711460160150251e-01 +4.530000000000000426e+01 9.948879558341270357e-01 1.076837231309151210e+00 9.906499256420852051e-01 +4.540000000000000568e+01 9.948762660822980664e-01 1.076990144162197627e+00 9.906287029527056642e-01 +4.550000000000000000e+01 9.948645764941462799e-01 1.077142987275872876e+00 9.906074779494641325e-01 +4.560000000000000142e+01 9.948528870696694559e-01 1.077295760675675229e+00 9.905862506339475626e-01 +4.570000000000000284e+01 9.948411978088653740e-01 1.077448464387099625e+00 9.905650210077423523e-01 +4.580000000000000426e+01 9.948295087117315916e-01 1.077601098435640337e+00 9.905437890724341221e-01 +4.590000000000000568e+01 9.948178197782658883e-01 1.077753662846789640e+00 9.905225548296078264e-01 +4.600000000000000000e+01 9.948061310084660436e-01 1.077906157646038032e+00 9.905013182808481975e-01 +4.610000000000000142e+01 9.947944424023297261e-01 1.078058582858872905e+00 9.904800794277385245e-01 +4.620000000000000284e+01 9.947827539598544933e-01 1.078210938510780537e+00 9.904588382718623185e-01 +4.630000000000000426e+01 9.947710656810380137e-01 1.078363224627245653e+00 9.904375948148018693e-01 +4.640000000000000568e+01 9.947593775658786219e-01 1.078515441233750538e+00 9.904163490581393559e-01 +4.650000000000000000e+01 9.947476896143735425e-01 1.078667588355775919e+00 9.903951010034560687e-01 +4.660000000000000142e+01 9.947360018265203330e-01 1.078819666018799861e+00 9.903738506523324103e-01 +4.670000000000000284e+01 9.947243142023169948e-01 1.078971674248298651e+00 9.903525980063485612e-01 +4.680000000000000426e+01 9.947126267417610856e-01 1.079123613069747911e+00 9.903313430670840356e-01 +4.690000000000000568e+01 9.947009394448504960e-01 1.079275482508619488e+00 9.903100858361173486e-01 +4.700000000000000000e+01 9.946892523115828944e-01 1.079427282590384118e+00 9.902888263150270154e-01 +4.710000000000000142e+01 9.946775653419559493e-01 1.079579013340510540e+00 9.902675645053903297e-01 +4.720000000000000284e+01 9.946658785359673294e-01 1.079730674784464606e+00 9.902463004087842524e-01 +4.730000000000000426e+01 9.946541918936147031e-01 1.079882266947710612e+00 9.902250340267851891e-01 +4.740000000000000568e+01 9.946425054148960720e-01 1.080033789855711301e+00 9.902037653609685464e-01 +4.750000000000000000e+01 9.946308190998086607e-01 1.080185243533926309e+00 9.901824944129093975e-01 +4.760000000000000142e+01 9.946191329483505816e-01 1.080336628007813937e+00 9.901612211841823719e-01 +4.770000000000000284e+01 9.946074469605193924e-01 1.080487943302830267e+00 9.901399456763610996e-01 +4.780000000000000426e+01 9.945957611363127615e-01 1.080639189444428494e+00 9.901186678910188776e-01 +4.790000000000000568e+01 9.945840754757285795e-01 1.080790366458060925e+00 9.900973878297278929e-01 +4.800000000000000000e+01 9.945723899787645150e-01 1.080941474369176536e+00 9.900761054940605543e-01 +4.810000000000000142e+01 9.945607046454182365e-01 1.081092513203223193e+00 9.900548208855878274e-01 +4.820000000000000284e+01 9.945490194756874125e-01 1.081243482985645876e+00 9.900335340058804556e-01 +4.830000000000000426e+01 9.945373344695698226e-01 1.081394383741887344e+00 9.900122448565084055e-01 +4.840000000000000568e+01 9.945256496270630242e-01 1.081545215497388579e+00 9.899909534390410881e-01 +4.850000000000000000e+01 9.945139649481650190e-01 1.081695978277587677e+00 9.899696597550472488e-01 +4.860000000000000142e+01 9.945022804328732535e-01 1.081846672107920959e+00 9.899483638060950774e-01 +4.870000000000000284e+01 9.944905960811855072e-01 1.081997297013822523e+00 9.899270655937523200e-01 +4.880000000000000426e+01 9.944789118930996707e-01 1.082147853020723804e+00 9.899057651195859453e-01 +4.890000000000000568e+01 9.944672278686134126e-01 1.082298340154054239e+00 9.898844623851619229e-01 +4.900000000000000000e+01 9.944555440077240682e-01 1.082448758439241043e+00 9.898631573920460003e-01 +4.910000000000000142e+01 9.944438603104297503e-01 1.082599107901708990e+00 9.898418501418032589e-01 +4.920000000000000284e+01 9.944321767767279052e-01 1.082749388566880633e+00 9.898205406359978920e-01 +4.930000000000000426e+01 9.944204934066166457e-01 1.082899600460176082e+00 9.897992288761939816e-01 +4.940000000000000568e+01 9.944088102000933072e-01 1.083049743607013227e+00 9.897779148639544999e-01 +4.950000000000000000e+01 9.943971271571558912e-01 1.083199818032807071e+00 9.897565986008421968e-01 +4.960000000000000142e+01 9.943854442778020664e-01 1.083349823762970843e+00 9.897352800884188229e-01 +4.970000000000000284e+01 9.943737615620291681e-01 1.083499760822914881e+00 9.897139593282457959e-01 +4.980000000000000426e+01 9.943620790098354201e-01 1.083649629238047751e+00 9.896926363218836453e-01 +4.990000000000000568e+01 9.943503966212182688e-01 1.083799429033775574e+00 9.896713110708924566e-01 +5.000000000000000000e+01 9.943387143961752717e-01 1.083949160235500919e+00 9.896499835768316489e-01 +5.010000000000000142e+01 9.943270323347043194e-01 1.084098822868625467e+00 9.896286538412600864e-01 +5.020000000000000284e+01 9.943153504368031914e-01 1.084248416958547123e+00 9.896073218657359671e-01 +5.030000000000000426e+01 9.943036687024695564e-01 1.084397942530662018e+00 9.895859876518169340e-01 +5.040000000000000568e+01 9.942919871317010827e-01 1.084547399610363838e+00 9.895646512010597418e-01 +5.050000000000000000e+01 9.942803057244956610e-01 1.084696788223043384e+00 9.895433125150205900e-01 +5.060000000000000142e+01 9.942686244808508489e-01 1.084846108394089459e+00 9.895219715952554562e-01 +5.070000000000000284e+01 9.942569434007643148e-01 1.084995360148887089e+00 9.895006284433192079e-01 +5.080000000000000426e+01 9.942452624842337272e-01 1.085144543512820636e+00 9.894792830607664902e-01 +5.090000000000000568e+01 9.942335817312571988e-01 1.085293658511270909e+00 9.894579354491508383e-01 +5.100000000000000000e+01 9.942219011418321761e-01 1.085442705169615829e+00 9.894365856100254542e-01 +5.110000000000000142e+01 9.942102207159562166e-01 1.085591683513231320e+00 9.894152335449430957e-01 +5.120000000000000284e+01 9.941985404536274329e-01 1.085740593567490642e+00 9.893938792554556327e-01 +5.130000000000000426e+01 9.941868603548430494e-01 1.085889435357764610e+00 9.893725227431143798e-01 +5.140000000000000568e+01 9.941751804196010678e-01 1.086038208909420488e+00 9.893511640094699855e-01 +5.150000000000000000e+01 9.941635006478990455e-01 1.086186914247823987e+00 9.893298030560727652e-01 +5.160000000000000142e+01 9.941518210397348732e-01 1.086335551398337484e+00 9.893084398844719241e-01 +5.170000000000000284e+01 9.941401415951062193e-01 1.086484120386321139e+00 9.892870744962164453e-01 +5.180000000000000426e+01 9.941284623140107524e-01 1.086632621237132224e+00 9.892657068928545350e-01 +5.190000000000000568e+01 9.941167831964463630e-01 1.086781053976125566e+00 9.892443370759336219e-01 +5.200000000000000000e+01 9.941051042424106088e-01 1.086929418628652666e+00 9.892229650470008018e-01 +5.210000000000000142e+01 9.940934254519010471e-01 1.087077715220063467e+00 9.892015908076025044e-01 +5.220000000000000284e+01 9.940817468249156796e-01 1.087225943775704140e+00 9.891802143592843821e-01 +5.230000000000000426e+01 9.940700683614520639e-01 1.087374104320917967e+00 9.891588357035914214e-01 +5.240000000000000568e+01 9.940583900615079793e-01 1.087522196881046677e+00 9.891374548420683865e-01 +5.250000000000000000e+01 9.940467119250812056e-01 1.087670221481428223e+00 9.891160717762587096e-01 +5.260000000000000142e+01 9.940350339521695222e-01 1.087818178147398784e+00 9.890946865077059336e-01 +5.270000000000000284e+01 9.940233561427703757e-01 1.087966066904290319e+00 9.890732990379526024e-01 +5.280000000000000426e+01 9.940116784968815455e-01 1.088113887777433675e+00 9.890519093685407048e-01 +5.290000000000000568e+01 9.940000010145007003e-01 1.088261640792155482e+00 9.890305175010117855e-01 +5.300000000000000000e+01 9.939883236956258417e-01 1.088409325973779929e+00 9.890091234369063899e-01 +5.310000000000000142e+01 9.939766465402543050e-01 1.088556943347628758e+00 9.889877271777647305e-01 +5.320000000000000284e+01 9.939649695483838698e-01 1.088704492939020385e+00 9.889663287251264645e-01 +5.330000000000000426e+01 9.939532927200125378e-01 1.088851974773271225e+00 9.889449280805303610e-01 +5.340000000000000568e+01 9.939416160551379775e-01 1.088999388875694141e+00 9.889235252455145231e-01 +5.350000000000000000e+01 9.939299395537577464e-01 1.089146735271598887e+00 9.889021202216168316e-01 +5.360000000000000142e+01 9.939182632158694020e-01 1.089294013986292553e+00 9.888807130103741683e-01 +5.370000000000000284e+01 9.939065870414710568e-01 1.089441225045079786e+00 9.888593036133230818e-01 +5.380000000000000426e+01 9.938949110305601575e-01 1.089588368473261681e+00 9.888378920319992327e-01 +5.390000000000000568e+01 9.938832351831345946e-01 1.089735444296136668e+00 9.888164782679378373e-01 +5.400000000000000000e+01 9.938715594991919255e-01 1.089882452538999846e+00 9.887950623226734459e-01 +5.410000000000000142e+01 9.938598839787299299e-01 1.090029393227144316e+00 9.887736441977402757e-01 +5.420000000000000284e+01 9.938482086217461653e-01 1.090176266385859405e+00 9.887522238946709896e-01 +5.430000000000000426e+01 9.938365334282385222e-01 1.090323072040431995e+00 9.887308014149986946e-01 +5.440000000000000568e+01 9.938248583982047801e-01 1.090469810216145419e+00 9.887093767602554983e-01 +5.450000000000000000e+01 9.938131835316423857e-01 1.090616480938280342e+00 9.886879499319728426e-01 +5.460000000000000142e+01 9.938015088285494514e-01 1.090763084232114544e+00 9.886665209316815028e-01 +5.470000000000000284e+01 9.937898342889235348e-01 1.090909620122922474e+00 9.886450897609113664e-01 +5.480000000000000426e+01 9.937781599127623045e-01 1.091056088635975918e+00 9.886236564211924316e-01 +5.490000000000000568e+01 9.937664857000634289e-01 1.091202489796543107e+00 9.886022209140535866e-01 +5.500000000000000000e+01 9.937548116508247986e-01 1.091348823629888720e+00 9.885807832410231644e-01 +5.510000000000000142e+01 9.937431377650439712e-01 1.091495090161276105e+00 9.885593434036289429e-01 +5.520000000000000284e+01 9.937314640427185042e-01 1.091641289415964389e+00 9.885379014033979228e-01 +5.530000000000000426e+01 9.937197904838462881e-01 1.091787421419209814e+00 9.885164572418565498e-01 +5.540000000000000568e+01 9.937081170884249914e-01 1.091933486196265068e+00 9.884950109205309365e-01 +5.550000000000000000e+01 9.936964438564522828e-01 1.092079483772380399e+00 9.884735624409460852e-01 +5.560000000000000142e+01 9.936847707879260527e-01 1.092225414172802278e+00 9.884521118046269983e-01 +5.570000000000000284e+01 9.936730978828441918e-01 1.092371277422774511e+00 9.884306590130974568e-01 +5.580000000000000426e+01 9.936614251412041465e-01 1.092517073547537576e+00 9.884092040678807978e-01 +5.590000000000000568e+01 9.936497525630035854e-01 1.092662802572328840e+00 9.883877469704996921e-01 +5.600000000000000000e+01 9.936380801482402880e-01 1.092808464522382561e+00 9.883662877224765886e-01 +5.610000000000000142e+01 9.936264078969120339e-01 1.092954059422929225e+00 9.883448263253327148e-01 +5.620000000000000284e+01 9.936147358090166026e-01 1.093099587299197317e+00 9.883233627805891874e-01 +5.630000000000000426e+01 9.936030638845515517e-01 1.093245048176411105e+00 9.883018970897663458e-01 +5.640000000000000568e+01 9.935913921235144386e-01 1.093390442079791969e+00 9.882804292543840852e-01 +5.650000000000000000e+01 9.935797205259032649e-01 1.093535769034557736e+00 9.882589592759610797e-01 +5.660000000000000142e+01 9.935680490917156993e-01 1.093681029065923349e+00 9.882374871560158924e-01 +5.670000000000000284e+01 9.935563778209495212e-01 1.093826222199100640e+00 9.882160128960664203e-01 +5.680000000000000426e+01 9.935447067136021770e-01 1.093971348459297888e+00 9.881945364976300050e-01 +5.690000000000000568e+01 9.935330357696715575e-01 1.094116407871719820e+00 9.881730579622231003e-01 +5.700000000000000000e+01 9.935213649891553311e-01 1.094261400461568279e+00 9.881515772913616047e-01 +5.710000000000000142e+01 9.935096943720513885e-01 1.094406326254041550e+00 9.881300944865609726e-01 +5.720000000000000284e+01 9.934980239183575090e-01 1.094551185274335037e+00 9.881086095493361032e-01 +5.730000000000000426e+01 9.934863536280709173e-01 1.094695977547640808e+00 9.880871224812010079e-01 +5.740000000000000568e+01 9.934746835011897259e-01 1.094840703099146717e+00 9.880656332836691425e-01 +5.750000000000000000e+01 9.934630135377116034e-01 1.094985361954038172e+00 9.880441419582536300e-01 +5.760000000000000142e+01 9.934513437376341072e-01 1.095129954137496586e+00 9.880226485064663722e-01 +5.770000000000000284e+01 9.934396741009550169e-01 1.095274479674700263e+00 9.880011529298191597e-01 +5.780000000000000426e+01 9.934280046276721121e-01 1.095418938590824620e+00 9.879796552298232282e-01 +5.790000000000000568e+01 9.934163353177830613e-01 1.095563330911041522e+00 9.879581554079889250e-01 +5.800000000000000000e+01 9.934046661712856441e-01 1.095707656660518392e+00 9.879366534658260424e-01 +5.810000000000000142e+01 9.933929971881776400e-01 1.095851915864419990e+00 9.879151494048437065e-01 +5.820000000000000284e+01 9.933813283684567175e-01 1.095996108547908188e+00 9.878936432265504886e-01 +5.830000000000000426e+01 9.933696597121204341e-01 1.096140234736140417e+00 9.878721349324545153e-01 +5.840000000000000568e+01 9.933579912191667916e-01 1.096284294454271002e+00 9.878506245240630257e-01 +5.850000000000000000e+01 9.933463228895932362e-01 1.096428287727451378e+00 9.878291120028827033e-01 +5.860000000000000142e+01 9.933346547233975476e-01 1.096572214580828986e+00 9.878075973704196766e-01 +5.870000000000000284e+01 9.933229867205773944e-01 1.096716075039547489e+00 9.877860806281796302e-01 +5.880000000000000426e+01 9.933113188811306671e-01 1.096859869128747444e+00 9.877645617776672493e-01 +5.890000000000000568e+01 9.932996512050550342e-01 1.097003596873566078e+00 9.877430408203868861e-01 +5.900000000000000000e+01 9.932879836923481642e-01 1.097147258299136618e+00 9.877215177578423377e-01 +5.910000000000000142e+01 9.932763163430078368e-01 1.097290853430589408e+00 9.876999925915364020e-01 +5.920000000000000284e+01 9.932646491570318315e-01 1.097434382293050126e+00 9.876784653229715438e-01 +5.930000000000000426e+01 9.932529821344174836e-01 1.097577844911642675e+00 9.876569359536495618e-01 +5.940000000000000568e+01 9.932413152751627949e-01 1.097721241311485629e+00 9.876354044850716996e-01 +5.950000000000000000e+01 9.932296485792655449e-01 1.097864571517694898e+00 9.876138709187385345e-01 +5.960000000000000142e+01 9.932179820467234022e-01 1.098007835555382616e+00 9.875923352561500890e-01 +5.970000000000000284e+01 9.932063156775341461e-01 1.098151033449656921e+00 9.875707974988056082e-01 +5.980000000000000426e+01 9.931946494716954454e-01 1.098294165225623509e+00 9.875492576482036711e-01 +5.990000000000000568e+01 9.931829834292049686e-01 1.098437230908383189e+00 9.875277157058427457e-01 +6.000000000000000000e+01 9.931713175500602730e-01 1.098580230523034107e+00 9.875061716732200789e-01 +6.010000000000000142e+01 9.931596518342592494e-01 1.098723164094669968e+00 9.874846255518326954e-01 +6.020000000000000284e+01 9.931479862817997883e-01 1.098866031648381369e+00 9.874630773431770647e-01 +6.030000000000000426e+01 9.931363208926793362e-01 1.099008833209255132e+00 9.874415270487484353e-01 +6.040000000000000568e+01 9.931246556668957837e-01 1.099151568802374079e+00 9.874199746700420555e-01 +6.050000000000000000e+01 9.931129906044466882e-01 1.099294238452817485e+00 9.873984202085521744e-01 +6.060000000000000142e+01 9.931013257053299403e-01 1.099436842185661733e+00 9.873768636657727082e-01 +6.070000000000000284e+01 9.930896609695429866e-01 1.099579380025978326e+00 9.873553050431971290e-01 +6.080000000000000426e+01 9.930779963970838287e-01 1.099721851998835653e+00 9.873337443423179094e-01 +6.090000000000000568e+01 9.930663319879501350e-01 1.099864258129298555e+00 9.873121815646267452e-01 +6.100000000000000000e+01 9.930546677421395740e-01 1.100006598442427652e+00 9.872906167116153320e-01 +6.110000000000000142e+01 9.930430036596497034e-01 1.100148872963280233e+00 9.872690497847741442e-01 +6.120000000000000284e+01 9.930313397404785247e-01 1.100291081716909369e+00 9.872474807855934342e-01 +6.130000000000000426e+01 9.930196759846238175e-01 1.100433224728364801e+00 9.872259097155630103e-01 +6.140000000000000568e+01 9.930080123920829172e-01 1.100575302022692270e+00 9.872043365761714595e-01 +6.150000000000000000e+01 9.929963489628538253e-01 1.100717313624934190e+00 9.871827613689070358e-01 +6.160000000000000142e+01 9.929846856969342106e-01 1.100859259560128312e+00 9.871611840952575490e-01 +6.170000000000000284e+01 9.929730225943217414e-01 1.101001139853309718e+00 9.871396047567100318e-01 +6.180000000000000426e+01 9.929613596550139754e-01 1.101142954529508167e+00 9.871180233547508509e-01 +6.190000000000000568e+01 9.929496968790088030e-01 1.101284703613750970e+00 9.870964398908660398e-01 +6.200000000000000000e+01 9.929380342663040038e-01 1.101426387131061002e+00 9.870748543665407437e-01 +6.210000000000000142e+01 9.929263718168972463e-01 1.101568005106457360e+00 9.870532667832596641e-01 +6.220000000000000284e+01 9.929147095307861992e-01 1.101709557564955810e+00 9.870316771425066138e-01 +6.230000000000000426e+01 9.929030474079685309e-01 1.101851044531567014e+00 9.870100854457651840e-01 +6.240000000000000568e+01 9.928913854484422430e-01 1.101992466031298745e+00 9.869884916945181885e-01 +6.250000000000000000e+01 9.928797236522047820e-01 1.102133822089155002e+00 9.869668958902475531e-01 +6.260000000000000142e+01 9.928680620192539275e-01 1.102275112730134898e+00 9.869452980344349813e-01 +6.270000000000000284e+01 9.928564005495874589e-01 1.102416337979234884e+00 9.869236981285613997e-01 +6.280000000000000426e+01 9.928447392432031560e-01 1.102557497861446079e+00 9.869020961741071796e-01 +6.290000000000000568e+01 9.928330781000985761e-01 1.102698592401757161e+00 9.868804921725520263e-01 +6.300000000000000000e+01 9.928214171202713878e-01 1.102839621625151922e+00 9.868588861253750899e-01 +6.310000000000000142e+01 9.928097563037195927e-01 1.102980585556610604e+00 9.868372780340548545e-01 +6.320000000000000284e+01 9.927980956504406373e-01 1.103121484221109228e+00 9.868156679000692488e-01 +6.330000000000000426e+01 9.927864351604323012e-01 1.103262317643619816e+00 9.867940557248955358e-01 +6.340000000000000568e+01 9.927747748336923639e-01 1.103403085849110843e+00 9.867724415100103119e-01 +6.350000000000000000e+01 9.927631146702183829e-01 1.103543788862546338e+00 9.867508252568898408e-01 +6.360000000000000142e+01 9.927514546700084708e-01 1.103684426708886335e+00 9.867292069670093868e-01 +6.370000000000000284e+01 9.927397948330598521e-01 1.103824999413087316e+00 9.867075866418437702e-01 +6.380000000000000426e+01 9.927281351593705283e-01 1.103965507000101764e+00 9.866859642828673671e-01 +6.390000000000000568e+01 9.927164756489383901e-01 1.104105949494877281e+00 9.866643398915537766e-01 +6.400000000000000000e+01 9.927048163017608839e-01 1.104246326922358357e+00 9.866427134693759315e-01 +6.410000000000000853e+01 9.926931571178356783e-01 1.104386639307484819e+00 9.866210850178062097e-01 +6.420000000000000284e+01 9.926814980971606639e-01 1.104526886675192721e+00 9.865994545383163228e-01 +6.429999999999999716e+01 9.926698392397333981e-01 1.104667069050414119e+00 9.865778220323774272e-01 +6.440000000000000568e+01 9.926581805455516605e-01 1.104807186458076851e+00 9.865561875014602355e-01 +6.450000000000000000e+01 9.926465220146131196e-01 1.104947238923104758e+00 9.865345509470347940e-01 +6.460000000000000853e+01 9.926348636469156661e-01 1.105087226470417461e+00 9.865129123705702607e-01 +6.470000000000000284e+01 9.926232054424568574e-01 1.105227149124930808e+00 9.864912717735355718e-01 +6.479999999999999716e+01 9.926115474012346951e-01 1.105367006911555761e+00 9.864696291573986642e-01 +6.490000000000000568e+01 9.925998895232467367e-01 1.105506799855200395e+00 9.864479845236270306e-01 +6.500000000000000000e+01 9.925882318084905398e-01 1.105646527980767013e+00 9.864263378736877197e-01 +6.510000000000000853e+01 9.925765742569638839e-01 1.105786191313155253e+00 9.864046892090471141e-01 +6.520000000000000284e+01 9.925649168686646595e-01 1.105925789877260312e+00 9.863830385311707083e-01 +6.529999999999999716e+01 9.925532596435903132e-01 1.106065323697972280e+00 9.863613858415237745e-01 +6.540000000000000568e+01 9.925416025817387355e-01 1.106204792800178138e+00 9.863397311415705859e-01 +6.550000000000000000e+01 9.925299456831077061e-01 1.106344197208760205e+00 9.863180744327751936e-01 +6.560000000000000853e+01 9.925182889476947823e-01 1.106483536948597024e+00 9.862964157166007606e-01 +6.570000000000000284e+01 9.925066323754978548e-01 1.106622812044562254e+00 9.862747549945100056e-01 +6.579999999999999716e+01 9.924949759665145921e-01 1.106762022521525779e+00 9.862530922679649814e-01 +6.590000000000000568e+01 9.924833197207424407e-01 1.106901168404353264e+00 9.862314275384269635e-01 +6.600000000000000000e+01 9.924716636381795132e-01 1.107040249717905933e+00 9.862097608073567834e-01 +6.610000000000000853e+01 9.924600077188237002e-01 1.107179266487041236e+00 9.861880920762146063e-01 +6.620000000000000284e+01 9.924483519626720041e-01 1.107318218736611515e+00 9.861664213464604867e-01 +6.629999999999999716e+01 9.924366963697226485e-01 1.107457106491465781e+00 9.861447486195530354e-01 +6.640000000000000568e+01 9.924250409399733019e-01 1.107595929776448163e+00 9.861230738969505305e-01 +6.650000000000000000e+01 9.924133856734215220e-01 1.107734688616399010e+00 9.861013971801111389e-01 +6.660000000000000853e+01 9.924017305700653102e-01 1.107873383036153792e+00 9.860797184704918061e-01 +6.670000000000000284e+01 9.923900756299021131e-01 1.108012013060543977e+00 9.860580377695491450e-01 +6.679999999999999716e+01 9.923784208529298212e-01 1.108150578714397483e+00 9.860363550787392128e-01 +6.690000000000000568e+01 9.923667662391458810e-01 1.108289080022536677e+00 9.860146703995169570e-01 +6.700000000000000000e+01 9.923551117885482942e-01 1.108427517009779928e+00 9.859929837333378799e-01 +6.710000000000000853e+01 9.923434575011347292e-01 1.108565889700941831e+00 9.859712950816554855e-01 +6.720000000000000284e+01 9.923318033769028546e-01 1.108704198120831874e+00 9.859496044459234998e-01 +6.729999999999999716e+01 9.923201494158502278e-01 1.108842442294255770e+00 9.859279118275949827e-01 +6.740000000000000568e+01 9.923084956179750726e-01 1.108980622246014791e+00 9.859062172281221059e-01 +6.750000000000000000e+01 9.922968419832745024e-01 1.109118738000905768e+00 9.858845206489565971e-01 +6.760000000000000853e+01 9.922851885117466297e-01 1.109256789583721092e+00 9.858628220915497398e-01 +6.770000000000000284e+01 9.922735352033891232e-01 1.109394777019248934e+00 9.858411215573519293e-01 +6.779999999999999716e+01 9.922618820581993182e-01 1.109532700332272803e+00 9.858194190478130059e-01 +6.790000000000000568e+01 9.922502290761756605e-01 1.109670559547572211e+00 9.857977145643824768e-01 +6.800000000000000000e+01 9.922385762573151524e-01 1.109808354689921339e+00 9.857760081085086279e-01 +6.810000000000000853e+01 9.922269236016160177e-01 1.109946085784091707e+00 9.857542996816400782e-01 +6.820000000000000284e+01 9.922152711090755917e-01 1.110083752854847949e+00 9.857325892852238924e-01 +6.829999999999999716e+01 9.922036187796919871e-01 1.110221355926952702e+00 9.857108769207070242e-01 +6.840000000000000568e+01 9.921919666134627613e-01 1.110358895025162385e+00 9.856891625895357611e-01 +6.850000000000000000e+01 9.921803146103853610e-01 1.110496370174230085e+00 9.856674462931558356e-01 +6.860000000000000853e+01 9.921686627704578987e-01 1.110633781398904008e+00 9.856457280330123139e-01 +6.870000000000000284e+01 9.921570110936780429e-01 1.110771128723927692e+00 9.856240078105494851e-01 +6.879999999999999716e+01 9.921453595800434622e-01 1.110908412174040460e+00 9.856022856272113053e-01 +6.890000000000000568e+01 9.921337082295518250e-01 1.111045631773977194e+00 9.855805614844411755e-01 +6.900000000000000000e+01 9.921220570422008000e-01 1.111182787548467887e+00 9.855588353836814974e-01 +6.910000000000000853e+01 9.921104060179881667e-01 1.111319879522238319e+00 9.855371073263745618e-01 +6.920000000000000284e+01 9.920987551569117047e-01 1.111456907720010046e+00 9.855153773139614382e-01 +6.929999999999999716e+01 9.920871044589690824e-01 1.111593872166499075e+00 9.854936453478831959e-01 +6.940000000000000568e+01 9.920754539241579684e-01 1.111730772886417640e+00 9.854719114295800164e-01 +6.950000000000000000e+01 9.920638035524759202e-01 1.111867609904473531e+00 9.854501755604915259e-01 +6.960000000000000853e+01 9.920521533439208284e-01 1.112004383245369876e+00 9.854284377420567953e-01 +6.970000000000000284e+01 9.920405032984908056e-01 1.112141092933805142e+00 9.854066979757141187e-01 +6.979999999999999716e+01 9.920288534161829652e-01 1.112277738994473131e+00 9.853849562629015679e-01 +6.990000000000000568e+01 9.920172036969953089e-01 1.112414321452063204e+00 9.853632126050559936e-01 +7.000000000000000000e+01 9.920055541409256161e-01 1.112550840331260282e+00 9.853414670036140244e-01 +7.010000000000000853e+01 9.919939047479714445e-01 1.112687295656744180e+00 9.853197194600119557e-01 +7.020000000000000284e+01 9.919822555181305734e-01 1.112823687453190491e+00 9.852979699756849730e-01 +7.029999999999999716e+01 9.919706064514008936e-01 1.112960015745270370e+00 9.852762185520678173e-01 +7.040000000000000568e+01 9.919589575477800736e-01 1.113096280557650086e+00 9.852544651905948969e-01 +7.050000000000000000e+01 9.919473088072654487e-01 1.113232481914991467e+00 9.852327098926996207e-01 +7.060000000000000853e+01 9.919356602298552428e-01 1.113368619841951235e+00 9.852109526598149536e-01 +7.070000000000000284e+01 9.919240118155467911e-01 1.113504694363182113e+00 9.851891934933733053e-01 +7.079999999999999716e+01 9.919123635643379844e-01 1.113640705503331274e+00 9.851674323948066414e-01 +7.090000000000000568e+01 9.919007154762266021e-01 1.113776653287042562e+00 9.851456693655461505e-01 +7.100000000000000000e+01 9.918890675512103128e-01 1.113912537738953601e+00 9.851239044070222439e-01 +7.110000000000000853e+01 9.918774197892867850e-01 1.114048358883698686e+00 9.851021375206648889e-01 +7.120000000000000284e+01 9.918657721904535762e-01 1.114184116745906561e+00 9.850803687079033866e-01 +7.129999999999999716e+01 9.918541247547086881e-01 1.114319811350201084e+00 9.850585979701665940e-01 +7.140000000000000568e+01 9.918424774820497891e-01 1.114455442721202560e+00 9.850368253088829240e-01 +7.150000000000000000e+01 9.918308303724745478e-01 1.114591010883525968e+00 9.850150507254795684e-01 +7.160000000000000853e+01 9.918191834259808548e-01 1.114726515861781175e+00 9.849932742213836079e-01 +7.170000000000000284e+01 9.918075366425663786e-01 1.114861957680573612e+00 9.849714957980215679e-01 +7.179999999999999716e+01 9.917958900222287877e-01 1.114997336364504266e+00 9.849497154568189750e-01 +7.190000000000000568e+01 9.917842435649656396e-01 1.115132651938169017e+00 9.849279331992012443e-01 +7.200000000000000000e+01 9.917725972707748250e-01 1.115267904426158863e+00 9.849061490265929031e-01 +7.210000000000000853e+01 9.917609511396540123e-01 1.115403093853060357e+00 9.848843629404177014e-01 +7.220000000000000284e+01 9.917493051716010921e-01 1.115538220243455170e+00 9.848625749420990561e-01 +7.229999999999999716e+01 9.917376593666135109e-01 1.115673283621920309e+00 9.848407850330600510e-01 +7.240000000000000568e+01 9.917260137246890483e-01 1.115808284013027896e+00 9.848189932147224379e-01 +7.250000000000000000e+01 9.917143682458255949e-01 1.115943221441345390e+00 9.847971994885078573e-01 +7.260000000000000853e+01 9.917027229300207081e-01 1.116078095931435143e+00 9.847754038558376166e-01 +7.270000000000000284e+01 9.916910777772722785e-01 1.116212907507854846e+00 9.847536063181316912e-01 +7.279999999999999716e+01 9.916794327875777526e-01 1.116347656195157079e+00 9.847318068768098343e-01 +7.290000000000000568e+01 9.916677879609351320e-01 1.116482342017889984e+00 9.847100055332914659e-01 +7.300000000000000000e+01 9.916561432973420853e-01 1.116616965000597261e+00 9.846882022889950070e-01 +7.310000000000000853e+01 9.916444987967960589e-01 1.116751525167816839e+00 9.846663971453383235e-01 +7.320000000000000284e+01 9.916328544592951655e-01 1.116886022544082424e+00 9.846445901037389481e-01 +7.329999999999999716e+01 9.916212102848369625e-01 1.117020457153922619e+00 9.846227811656137474e-01 +7.340000000000000568e+01 9.916095662734192295e-01 1.117154829021861140e+00 9.846009703323788109e-01 +7.350000000000000000e+01 9.915979224250395241e-01 1.117289138172416596e+00 9.845791576054495620e-01 +7.360000000000000853e+01 9.915862787396957367e-01 1.117423384630103600e+00 9.845573429862412018e-01 +7.370000000000000284e+01 9.915746352173854250e-01 1.117557568419430991e+00 9.845355264761679326e-01 +7.379999999999999716e+01 9.915629918581064794e-01 1.117691689564903168e+00 9.845137080766434012e-01 +7.390000000000000568e+01 9.915513486618565686e-01 1.117825748091019200e+00 9.844918877890810327e-01 +7.400000000000000000e+01 9.915397056286333610e-01 1.117959744022273716e+00 9.844700656148933637e-01 +7.410000000000000853e+01 9.915280627584346362e-01 1.118093677383155793e+00 9.844482415554922650e-01 +7.420000000000000284e+01 9.915164200512579518e-01 1.118227548198150956e+00 9.844264156122891629e-01 +7.429999999999999716e+01 9.915047775071011982e-01 1.118361356491737846e+00 9.844045877866947070e-01 +7.440000000000000568e+01 9.914931351259620440e-01 1.118495102288391552e+00 9.843827580801193244e-01 +7.450000000000000000e+01 9.914814929078383798e-01 1.118628785612581389e+00 9.843609264939726655e-01 +7.460000000000000853e+01 9.914698508527276521e-01 1.118762406488772676e+00 9.843390930296634922e-01 +7.470000000000000284e+01 9.914582089606276405e-01 1.118895964941424959e+00 9.843172576886002334e-01 +7.479999999999999716e+01 9.914465672315362355e-01 1.119029460994992897e+00 9.842954204721907630e-01 +7.490000000000000568e+01 9.914349256654509945e-01 1.119162894673926933e+00 9.842735813818422885e-01 +7.500000000000000000e+01 9.914232842623699193e-01 1.119296266002671070e+00 9.842517404189613517e-01 +7.510000000000000853e+01 9.914116430222902343e-01 1.119429575005665534e+00 9.842298975849540499e-01 +7.520000000000000284e+01 9.914000019452101631e-01 1.119562821707345002e+00 9.842080528812255924e-01 +7.529999999999999716e+01 9.913883610311271521e-01 1.119696006132139265e+00 9.841862063091811885e-01 +7.540000000000000568e+01 9.913767202800389811e-01 1.119829128304473453e+00 9.841643578702246042e-01 +7.550000000000000000e+01 9.913650796919434294e-01 1.119962188248767143e+00 9.841425075657596055e-01 +7.560000000000000853e+01 9.913534392668381656e-01 1.120095185989434805e+00 9.841206553971892923e-01 +7.570000000000000284e+01 9.913417990047208583e-01 1.120228121550886247e+00 9.840988013659160982e-01 +7.579999999999999716e+01 9.913301589055891760e-01 1.120360994957526168e+00 9.840769454733419019e-01 +7.590000000000000568e+01 9.913185189694411203e-01 1.120493806233754164e+00 9.840550877208678049e-01 +7.600000000000000000e+01 9.913068791962740267e-01 1.120626555403964719e+00 9.840332281098945755e-01 +7.610000000000000853e+01 9.912952395860860078e-01 1.120759242492547214e+00 9.840113666418220939e-01 +7.620000000000000284e+01 9.912836001388746210e-01 1.120891867523885699e+00 9.839895033180497963e-01 +7.629999999999999716e+01 9.912719608546377570e-01 1.121024430522359783e+00 9.839676381399770078e-01 +7.640000000000000568e+01 9.912603217333727512e-01 1.121156931512343302e+00 9.839457711090016101e-01 +7.650000000000000000e+01 9.912486827750776053e-01 1.121289370518205430e+00 9.839239022265210410e-01 +7.660000000000000853e+01 9.912370439797500987e-01 1.121421747564310234e+00 9.839020314939327383e-01 +7.670000000000000284e+01 9.912254053473876780e-01 1.121554062675016450e+00 9.838801589126331404e-01 +7.680000000000001137e+01 9.912137668779883448e-01 1.121686315874677931e+00 9.838582844840180197e-01 +7.690000000000000568e+01 9.912021285715494345e-01 1.121818507187642533e+00 9.838364082094827046e-01 +7.700000000000000000e+01 9.911904904280691708e-01 1.121950636638254561e+00 9.838145300904219681e-01 +7.710000000000000853e+01 9.911788524475448892e-01 1.122082704250851437e+00 9.837926501282298064e-01 +7.720000000000000284e+01 9.911672146299745911e-01 1.122214710049766806e+00 9.837707683242997714e-01 +7.730000000000001137e+01 9.911555769753558343e-01 1.122346654059328097e+00 9.837488846800247488e-01 +7.740000000000000568e+01 9.911439394836863981e-01 1.122478536303858521e+00 9.837269991967971805e-01 +7.750000000000000000e+01 9.911323021549640622e-01 1.122610356807675513e+00 9.837051118760087309e-01 +7.760000000000000853e+01 9.911206649891863840e-01 1.122742115595091850e+00 9.836832227190503986e-01 +7.770000000000000284e+01 9.911090279863511432e-01 1.122873812690413642e+00 9.836613317273128487e-01 +7.780000000000001137e+01 9.910973911464561192e-01 1.123005448117943894e+00 9.836394389021860807e-01 +7.790000000000000568e+01 9.910857544694992027e-01 1.123137021901979393e+00 9.836175442450593165e-01 +7.800000000000000000e+01 9.910741179554778402e-01 1.123268534066811153e+00 9.835956477573215562e-01 +7.810000000000000853e+01 9.910624816043898111e-01 1.123399984636725746e+00 9.835737494403606895e-01 +7.820000000000000284e+01 9.910508454162327840e-01 1.123531373636004416e+00 9.835518492955643843e-01 +7.830000000000001137e+01 9.910392093910046496e-01 1.123662701088923077e+00 9.835299473243197532e-01 +7.840000000000000568e+01 9.910275735287029653e-01 1.123793967019752316e+00 9.835080435280127986e-01 +7.850000000000000000e+01 9.910159378293255106e-01 1.123925171452757610e+00 9.834861379080296340e-01 +7.860000000000000853e+01 9.910043022928699541e-01 1.124056314412199109e+00 9.834642304657554845e-01 +7.870000000000000284e+01 9.909926669193340754e-01 1.124187395922332078e+00 9.834423212025750205e-01 +7.880000000000001137e+01 9.909810317087157649e-01 1.124318416007405119e+00 9.834204101198721348e-01 +7.890000000000000568e+01 9.909693966610126914e-01 1.124449374691663506e+00 9.833984972190302765e-01 +7.900000000000000000e+01 9.909577617762224122e-01 1.124580271999345848e+00 9.833765825014323392e-01 +7.910000000000000853e+01 9.909461270543427069e-01 1.124711107954685874e+00 9.833546659684606617e-01 +7.920000000000000284e+01 9.909344924953713551e-01 1.124841882581912200e+00 9.833327476214968055e-01 +7.930000000000001137e+01 9.909228580993061364e-01 1.124972595905248118e+00 9.833108274619217770e-01 +7.940000000000000568e+01 9.909112238661447192e-01 1.125103247948911367e+00 9.832889054911160276e-01 +7.950000000000000000e+01 9.908995897958848831e-01 1.125233838737114356e+00 9.832669817104595644e-01 +7.960000000000000853e+01 9.908879558885239636e-01 1.125364368294064388e+00 9.832450561213316176e-01 +7.970000000000000284e+01 9.908763221440600732e-01 1.125494836643963659e+00 9.832231287251109730e-01 +7.980000000000001137e+01 9.908646885624907696e-01 1.125625243811008147e+00 9.832011995231756396e-01 +7.990000000000000568e+01 9.908530551438140543e-01 1.125755589819389391e+00 9.831792685169031820e-01 +8.000000000000000000e+01 9.908414218880272628e-01 1.125885874693293154e+00 9.831573357076707209e-01 +8.010000000000000853e+01 9.908297887951285077e-01 1.126016098456900316e+00 9.831354010968542667e-01 +8.020000000000000284e+01 9.908181558651154575e-01 1.126146261134385096e+00 9.831134646858297188e-01 +8.030000000000001137e+01 9.908065230979853366e-01 1.126276362749917936e+00 9.830915264759723105e-01 +8.040000000000000568e+01 9.907948904937363688e-01 1.126406403327663064e+00 9.830695864686567198e-01 +8.050000000000000000e+01 9.907832580523662225e-01 1.126536382891779375e+00 9.830476446652566258e-01 +8.060000000000000853e+01 9.907716257738726773e-01 1.126666301466420439e+00 9.830257010671455964e-01 +8.070000000000000284e+01 9.907599936582531797e-01 1.126796159075734716e+00 9.830037556756964223e-01 +8.080000000000001137e+01 9.907483617055056202e-01 1.126925955743864893e+00 9.829818084922813393e-01 +8.090000000000000568e+01 9.907367299156276674e-01 1.127055691494948331e+00 9.829598595182720278e-01 +8.100000000000000000e+01 9.907250982886171009e-01 1.127185366353116391e+00 9.829379087550395022e-01 +8.110000000000000853e+01 9.907134668244715892e-01 1.127314980342495998e+00 9.829159562039543330e-01 +8.120000000000000284e+01 9.907018355231885787e-01 1.127444533487208522e+00 9.828940018663864242e-01 +8.130000000000001137e+01 9.906902043847661821e-01 1.127574025811369562e+00 9.828720457437045699e-01 +8.140000000000000568e+01 9.906785734092022899e-01 1.127703457339088944e+00 9.828500878372780081e-01 +8.150000000000000000e+01 9.906669425964942377e-01 1.127832828094471607e+00 9.828281281484745335e-01 +8.160000000000000853e+01 9.906553119466398050e-01 1.127962138101616718e+00 9.828061666786618300e-01 +8.170000000000000284e+01 9.906436814596368823e-01 1.128091387384618782e+00 9.827842034292065820e-01 +8.180000000000001137e+01 9.906320511354832492e-01 1.128220575967565642e+00 9.827622384014754742e-01 +8.190000000000000568e+01 9.906204209741762412e-01 1.128349703874540033e+00 9.827402715968339697e-01 +8.200000000000000000e+01 9.906087909757140819e-01 1.128478771129619584e+00 9.827183030166474209e-01 +8.210000000000000853e+01 9.905971611400941068e-01 1.128607777756875929e+00 9.826963326622804029e-01 +8.220000000000000284e+01 9.905855314673142065e-01 1.128736723780375595e+00 9.826743605350968247e-01 +8.230000000000001137e+01 9.905739019573719384e-01 1.128865609224180000e+00 9.826523866364601512e-01 +8.240000000000000568e+01 9.905622726102653042e-01 1.128994434112343903e+00 9.826304109677331811e-01 +8.250000000000000000e+01 9.905506434259920834e-01 1.129123198468917844e+00 9.826084335302781581e-01 +8.260000000000000853e+01 9.905390144045498335e-01 1.129251902317945699e+00 9.825864543254566597e-01 +8.270000000000000284e+01 9.905273855459358900e-01 1.129380545683466019e+00 9.825644733546297083e-01 +8.280000000000001137e+01 9.905157568501484766e-01 1.129509128589512690e+00 9.825424906191581043e-01 +8.290000000000000568e+01 9.905041283171851507e-01 1.129637651060113379e+00 9.825205061204014267e-01 +8.300000000000000000e+01 9.904924999470440250e-01 1.129766113119289983e+00 9.824985198597190328e-01 +8.310000000000000853e+01 9.904808717397222129e-01 1.129894514791059512e+00 9.824765318384696133e-01 +8.320000000000000284e+01 9.904692436952177159e-01 1.130022856099432538e+00 9.824545420580114152e-01 +8.330000000000001137e+01 9.904576158135283137e-01 1.130151137068414968e+00 9.824325505197019082e-01 +8.340000000000000568e+01 9.904459880946515637e-01 1.130279357722006717e+00 9.824105572248981177e-01 +8.350000000000000000e+01 9.904343605385853566e-01 1.130407518084202589e+00 9.823885621749566255e-01 +8.360000000000000853e+01 9.904227331453271388e-01 1.130535618178990731e+00 9.823665653712329027e-01 +8.370000000000000284e+01 9.904111059148750229e-01 1.130663658030354624e+00 9.823445668150820875e-01 +8.380000000000001137e+01 9.903994788472266775e-01 1.130791637662271976e+00 9.823225665078590962e-01 +8.390000000000000568e+01 9.903878519423795490e-01 1.130919557098714501e+00 9.823005644509178458e-01 +8.400000000000000000e+01 9.903762252003314170e-01 1.131047416363648583e+00 9.822785606456116980e-01 +8.410000000000000853e+01 9.903645986210802832e-01 1.131175215481035279e+00 9.822565550932934597e-01 +8.420000000000000284e+01 9.903529722046234829e-01 1.131302954474829425e+00 9.822345477953156045e-01 +8.430000000000001137e+01 9.903413459509591288e-01 1.131430633368980754e+00 9.822125387530300511e-01 +8.440000000000000568e+01 9.903297198600847784e-01 1.131558252187433000e+00 9.821905279677872747e-01 +8.450000000000000000e+01 9.903180939319983223e-01 1.131685810954124793e+00 9.821685154409384166e-01 +8.460000000000000853e+01 9.903064681666970959e-01 1.131813309692988545e+00 9.821465011738330642e-01 +8.470000000000000284e+01 9.902948425641792118e-01 1.131940748427950894e+00 9.821244851678209153e-01 +8.480000000000001137e+01 9.902832171244420056e-01 1.132068127182933148e+00 9.821024674242505581e-01 +8.490000000000000568e+01 9.902715918474835899e-01 1.132195445981851289e+00 9.820804479444701363e-01 +8.500000000000000000e+01 9.902599667333014111e-01 1.132322704848615302e+00 9.820584267298274606e-01 +8.510000000000000853e+01 9.902483417818933598e-01 1.132449903807128955e+00 9.820364037816693425e-01 +8.520000000000000284e+01 9.902367169932571045e-01 1.132577042881291352e+00 9.820143791013424828e-01 +8.530000000000001137e+01 9.902250923673904248e-01 1.132704122094995602e+00 9.819923526901925825e-01 +8.540000000000000568e+01 9.902134679042908783e-01 1.132831141472128378e+00 9.819703245495650101e-01 +8.550000000000000000e+01 9.902018436039564664e-01 1.132958101036571685e+00 9.819482946808044677e-01 +8.560000000000000853e+01 9.901902194663845247e-01 1.133085000812200871e+00 9.819262630852549911e-01 +8.570000000000000284e+01 9.901785954915730548e-01 1.133211840822886174e+00 9.819042297642605055e-01 +8.580000000000001137e+01 9.901669716795197251e-01 1.133338621092491838e+00 9.818821947191636035e-01 +8.590000000000000568e+01 9.901553480302225374e-01 1.133465341644877000e+00 9.818601579513067668e-01 +8.600000000000000000e+01 9.901437245436789381e-01 1.133592002503894358e+00 9.818381194620316998e-01 +8.610000000000000853e+01 9.901321012198865956e-01 1.133718603693391058e+00 9.818160792526796632e-01 +8.620000000000000284e+01 9.901204780588432897e-01 1.133845145237208474e+00 9.817940373245912511e-01 +8.630000000000001137e+01 9.901088550605466887e-01 1.133971627159182205e+00 9.817719936791066138e-01 +8.640000000000000568e+01 9.900972322249947943e-01 1.134098049483142523e+00 9.817499483175653463e-01 +8.650000000000000000e+01 9.900856095521850531e-01 1.134224412232913481e+00 9.817279012413060446e-01 +8.660000000000000853e+01 9.900739870421153555e-01 1.134350715432313805e+00 9.817058524516674156e-01 +8.670000000000000284e+01 9.900623646947833700e-01 1.134476959105156002e+00 9.816838019499870560e-01 +8.680000000000001137e+01 9.900507425101867653e-01 1.134603143275247028e+00 9.816617497376018964e-01 +8.690000000000000568e+01 9.900391204883232099e-01 1.134729267966388067e+00 9.816396958158487562e-01 +8.700000000000000000e+01 9.900274986291907053e-01 1.134855333202374306e+00 9.816176401860634559e-01 +8.710000000000000853e+01 9.900158769327865871e-01 1.134981339006995160e+00 9.815955828495815938e-01 +8.720000000000000284e+01 9.900042553991088567e-01 1.135107285404034272e+00 9.815735238077378799e-01 +8.730000000000001137e+01 9.899926340281551829e-01 1.135233172417269731e+00 9.815514630618665803e-01 +8.740000000000000568e+01 9.899810128199233450e-01 1.135359000070474078e+00 9.815294006133014060e-01 +8.750000000000000000e+01 9.899693917744109006e-01 1.135484768387412968e+00 9.815073364633756237e-01 +8.760000000000000853e+01 9.899577708916157404e-01 1.135610477391847395e+00 9.814852706134215010e-01 +8.770000000000000284e+01 9.899461501715356437e-01 1.135736127107531690e+00 9.814632030647709726e-01 +8.780000000000001137e+01 9.899345296141681683e-01 1.135861717558215078e+00 9.814411338187555289e-01 +8.790000000000000568e+01 9.899229092195109825e-01 1.135987248767640123e+00 9.814190628767062163e-01 +8.800000000000000000e+01 9.899112889875620880e-01 1.136112720759544281e+00 9.813969902399528600e-01 +8.810000000000000853e+01 9.898996689183190423e-01 1.136238133557659014e+00 9.813749159098250630e-01 +8.820000000000000284e+01 9.898880490117794029e-01 1.136363487185709564e+00 9.813528398876519843e-01 +8.830000000000001137e+01 9.898764292679410604e-01 1.136488781667415182e+00 9.813307621747622278e-01 +8.840000000000000568e+01 9.898648096868019053e-01 1.136614017026490231e+00 9.813086827724838423e-01 +8.850000000000000000e+01 9.898531902683596062e-01 1.136739193286642191e+00 9.812866016821436554e-01 +8.860000000000000853e+01 9.898415710126117206e-01 1.136864310471573214e+00 9.812645189050687167e-01 +8.870000000000000284e+01 9.898299519195559171e-01 1.136989368604979678e+00 9.812424344425851874e-01 +8.880000000000001137e+01 9.898183329891901971e-01 1.137114367710551299e+00 9.812203482960185630e-01 +8.890000000000000568e+01 9.898067142215121184e-01 1.137239307811972688e+00 9.811982604666940055e-01 +8.900000000000000000e+01 9.897950956165195713e-01 1.137364188932922238e+00 9.811761709559359002e-01 +8.910000000000000853e+01 9.897834771742098914e-01 1.137489011097072567e+00 9.811540797650679657e-01 +8.920000000000000284e+01 9.897718588945814133e-01 1.137613774328089855e+00 9.811319868954135881e-01 +8.930000000000001137e+01 9.897602407776313616e-01 1.137738478649635621e+00 9.811098923482953760e-01 +8.940000000000000568e+01 9.897486228233576266e-01 1.137863124085364275e+00 9.810877961250354939e-01 +8.950000000000000000e+01 9.897370050317577661e-01 1.137987710658924678e+00 9.810656982269555515e-01 +8.960000000000000853e+01 9.897253874028297815e-01 1.138112238393959919e+00 9.810435986553766030e-01 +8.970000000000000284e+01 9.897137699365713415e-01 1.138236707314107088e+00 9.810214974116189257e-01 +8.980000000000001137e+01 9.897021526329801144e-01 1.138361117442997061e+00 9.809993944970022417e-01 +8.990000000000000568e+01 9.896905354920538800e-01 1.138485468804254941e+00 9.809772899128461621e-01 +9.000000000000000000e+01 9.896789185137901956e-01 1.138609761421500055e+00 9.809551836604691877e-01 +9.010000000000000853e+01 9.896673016981868409e-01 1.138733995318345293e+00 9.809330757411893753e-01 +9.020000000000000284e+01 9.896556850452417065e-01 1.138858170518398660e+00 9.809109661563241156e-01 +9.030000000000001137e+01 9.896440685549524607e-01 1.138982287045260833e+00 9.808888549071903551e-01 +9.040000000000000568e+01 9.896324522273166613e-01 1.139106344922527381e+00 9.808667419951047073e-01 +9.050000000000000000e+01 9.896208360623321987e-01 1.139230344173787657e+00 9.808446274213831195e-01 +9.060000000000000853e+01 9.896092200599966304e-01 1.139354284822625019e+00 9.808225111873404289e-01 +9.070000000000000284e+01 9.895976042203081802e-01 1.139478166892617050e+00 9.808003932942914727e-01 +9.080000000000001137e+01 9.895859885432641834e-01 1.139601990407334897e+00 9.807782737435501996e-01 +9.090000000000000568e+01 9.895743730288623086e-01 1.139725755390344153e+00 9.807561525364303368e-01 +9.100000000000000000e+01 9.895627576771003353e-01 1.139849461865204416e+00 9.807340296742447228e-01 +9.110000000000000853e+01 9.895511424879761542e-01 1.139973109855468625e+00 9.807119051583057523e-01 +9.120000000000000284e+01 9.895395274614873227e-01 1.140096699384684387e+00 9.806897789899250428e-01 +9.130000000000001137e+01 9.895279125976315093e-01 1.140220230476393315e+00 9.806676511704138788e-01 +9.140000000000000568e+01 9.895162978964066047e-01 1.140343703154130361e+00 9.806455217010829895e-01 +9.150000000000000000e+01 9.895046833578101664e-01 1.140467117441424927e+00 9.806233905832423270e-01 +9.160000000000000853e+01 9.894930689818403069e-01 1.140590473361800639e+00 9.806012578182015105e-01 +9.170000000000000284e+01 9.894814547684944728e-01 1.140713770938774463e+00 9.805791234072692708e-01 +9.180000000000001137e+01 9.894698407177702215e-01 1.140837010195858037e+00 9.805569873517541168e-01 +9.190000000000000568e+01 9.894582268296655547e-01 1.140960191156556114e+00 9.805348496529638913e-01 +9.200000000000000000e+01 9.894466131041781409e-01 1.141083313844368119e+00 9.805127103122056598e-01 +9.210000000000000853e+01 9.894349995413054266e-01 1.141206378282786815e+00 9.804905693307861547e-01 +9.220000000000000284e+01 9.894233861410454134e-01 1.141329384495299637e+00 9.804684267100114425e-01 +9.230000000000001137e+01 9.894117729033961028e-01 1.141452332505387579e+00 9.804462824511868124e-01 +9.240000000000000568e+01 9.894001598283546084e-01 1.141575222336525197e+00 9.804241365556174426e-01 +9.250000000000000000e+01 9.893885469159192647e-01 1.141698054012181496e+00 9.804019890246075120e-01 +9.260000000000000853e+01 9.893769341660874073e-01 1.141820827555819262e+00 9.803798398594607555e-01 +9.270000000000000284e+01 9.893653215788569266e-01 1.141943542990894844e+00 9.803576890614803530e-01 +9.280000000000001137e+01 9.893537091542254913e-01 1.142066200340859039e+00 9.803355366319691511e-01 +9.290000000000000568e+01 9.893420968921907699e-01 1.142188799629155982e+00 9.803133825722291084e-01 +9.300000000000000000e+01 9.893304847927505419e-01 1.142311340879224479e+00 9.802912268835618503e-01 +9.310000000000000853e+01 9.893188728559024758e-01 1.142433824114496455e+00 9.802690695672682253e-01 +9.320000000000000284e+01 9.893072610816443513e-01 1.142556249358398057e+00 9.802469106246485264e-01 +9.330000000000001137e+01 9.892956494699740588e-01 1.142678616634349664e+00 9.802247500570023808e-01 +9.340000000000000568e+01 9.892840380208891560e-01 1.142800925965764991e+00 9.802025878656290825e-01 +9.350000000000000000e+01 9.892724267343873112e-01 1.142923177376051758e+00 9.801804240518275924e-01 +9.360000000000000853e+01 9.892608156104664152e-01 1.143045370888611689e+00 9.801582586168955391e-01 +9.370000000000000284e+01 9.892492046491240254e-01 1.143167506526840516e+00 9.801360915621306624e-01 +9.380000000000001137e+01 9.892375938503580324e-01 1.143289584314127305e+00 9.801139228888300359e-01 +9.390000000000000568e+01 9.892259832141661047e-01 1.143411604273855797e+00 9.800917525982898448e-01 +9.400000000000000000e+01 9.892143727405460218e-01 1.143533566429402404e+00 9.800695806918057196e-01 +9.410000000000000853e+01 9.892027624294953414e-01 1.143655470804138652e+00 9.800474071706730683e-01 +9.420000000000000284e+01 9.891911522810119539e-01 1.143777317421428963e+00 9.800252320361866332e-01 +9.430000000000001137e+01 9.891795422950935279e-01 1.143899106304632207e+00 9.800030552896404901e-01 +9.440000000000000568e+01 9.891679324717376209e-01 1.144020837477100816e+00 9.799808769323281599e-01 +9.450000000000000000e+01 9.891563228109422345e-01 1.144142510962181003e+00 9.799586969655424973e-01 +9.460000000000000853e+01 9.891447133127051483e-01 1.144264126783212765e+00 9.799365153905759129e-01 +9.470000000000000284e+01 9.891331039770238087e-01 1.144385684963530325e+00 9.799143322087201513e-01 +9.480000000000001137e+01 9.891214948038962174e-01 1.144507185526461690e+00 9.798921474212666238e-01 +9.490000000000000568e+01 9.891098857933198207e-01 1.144628628495327982e+00 9.798699610295058537e-01 +9.500000000000000000e+01 9.890982769452923984e-01 1.144750013893445217e+00 9.798477730347281422e-01 +9.510000000000000853e+01 9.890866682598117299e-01 1.144871341744121640e+00 9.798255834382230134e-01 +9.520000000000000284e+01 9.890750597368757058e-01 1.144992612070660609e+00 9.798033922412792140e-01 +9.530000000000001137e+01 9.890634513764821056e-01 1.145113824896359267e+00 9.797811994451850470e-01 +9.540000000000000568e+01 9.890518431786284870e-01 1.145234980244507872e+00 9.797590050512288151e-01 +9.550000000000000000e+01 9.890402351433125183e-01 1.145356078138390910e+00 9.797368090606975999e-01 +9.560000000000000853e+01 9.890286272705319792e-01 1.145477118601286648e+00 9.797146114748782608e-01 +9.570000000000000284e+01 9.890170195602845382e-01 1.145598101656466694e+00 9.796924122950566582e-01 +9.580000000000001137e+01 9.890054120125679749e-01 1.145719027327196882e+00 9.796702115225184304e-01 +9.590000000000000568e+01 9.889938046273799577e-01 1.145839895636736605e+00 9.796480091585488825e-01 +9.600000000000000000e+01 9.889821974047184883e-01 1.145960706608339041e+00 9.796258052044322095e-01 +9.610000000000000853e+01 9.889705903445811241e-01 1.146081460265251151e+00 9.796035996614523844e-01 +9.620000000000000284e+01 9.889589834469655338e-01 1.146202156630713898e+00 9.795813925308926029e-01 +9.630000000000001137e+01 9.889473767118694969e-01 1.146322795727962029e+00 9.795591838140357277e-01 +9.640000000000000568e+01 9.889357701392907929e-01 1.146443377580223189e+00 9.795369735121639554e-01 +9.650000000000000000e+01 9.889241637292268683e-01 1.146563902210719466e+00 9.795147616265588164e-01 +9.660000000000000853e+01 9.889125574816758357e-01 1.146684369642666512e+00 9.794925481585015081e-01 +9.670000000000000284e+01 9.889009513966352527e-01 1.146804779899273763e+00 9.794703331092723397e-01 +9.680000000000001137e+01 9.888893454741026767e-01 1.146925133003744657e+00 9.794481164801515094e-01 +9.690000000000000568e+01 9.888777397140759984e-01 1.147045428979275972e+00 9.794258982724183271e-01 +9.700000000000000000e+01 9.888661341165531082e-01 1.147165667849058268e+00 9.794036784873515478e-01 +9.710000000000000853e+01 9.888545286815316748e-01 1.147285849636275890e+00 9.793814571262291491e-01 +9.720000000000000284e+01 9.888429234090093667e-01 1.147405974364106740e+00 9.793592341903289977e-01 +9.730000000000001137e+01 9.888313182989839634e-01 1.147526042055722728e+00 9.793370096809282943e-01 +9.740000000000000568e+01 9.888197133514531334e-01 1.147646052734289102e+00 9.793147835993036843e-01 +9.750000000000000000e+01 9.888081085664144343e-01 1.147766006422965113e+00 9.792925559467307028e-01 +9.760000000000000853e+01 9.887965039438658676e-01 1.147885903144903574e+00 9.792703267244852183e-01 +9.770000000000000284e+01 9.887848994838049910e-01 1.148005742923251304e+00 9.792480959338418778e-01 +9.780000000000001137e+01 9.887732951862294728e-01 1.148125525781148237e+00 9.792258635760749952e-01 +9.790000000000000568e+01 9.887616910511372037e-01 1.148245251741728090e+00 9.792036296524583294e-01 +9.800000000000000000e+01 9.887500870785258522e-01 1.148364920828118363e+00 9.791813941642650843e-01 +9.810000000000000853e+01 9.887384832683933089e-01 1.148484533063441004e+00 9.791591571127676863e-01 +9.820000000000000284e+01 9.887268796207371313e-01 1.148604088470810414e+00 9.791369184992384511e-01 +9.830000000000001137e+01 9.887152761355549879e-01 1.148723587073334995e+00 9.791146783249486951e-01 +9.840000000000000568e+01 9.887036728128447693e-01 1.148843028894117380e+00 9.790924365911692906e-01 +9.850000000000000000e+01 9.886920696526040331e-01 1.148962413956253092e+00 9.790701932991708878e-01 +9.860000000000000853e+01 9.886804666548310028e-01 1.149081742282832108e+00 9.790479484502229157e-01 +9.870000000000000284e+01 9.886688638195227918e-01 1.149201013896937074e+00 9.790257020455946924e-01 +9.880000000000001137e+01 9.886572611466772909e-01 1.149320228821645085e+00 9.790034540865550916e-01 +9.890000000000000568e+01 9.886456586362922794e-01 1.149439387080026798e+00 9.789812045743719882e-01 +9.900000000000000000e+01 9.886340562883656480e-01 1.149558488695145764e+00 9.789589535103131457e-01 +9.910000000000000853e+01 9.886224541028949542e-01 1.149677533690060427e+00 9.789367008956454397e-01 +9.920000000000000284e+01 9.886108520798780885e-01 1.149796522087821460e+00 9.789144467316353015e-01 +9.930000000000001137e+01 9.885992502193123865e-01 1.149915453911474650e+00 9.788921910195484966e-01 +9.940000000000000568e+01 9.885876485211958498e-01 1.150034329184058235e+00 9.788699337606503459e-01 +9.950000000000000000e+01 9.885760469855264798e-01 1.150153147928604236e+00 9.788476749562060597e-01 +9.960000000000000853e+01 9.885644456123015011e-01 1.150271910168139122e+00 9.788254146074794049e-01 +9.970000000000000284e+01 9.885528444015190264e-01 1.150390615925681592e+00 9.788031527157340372e-01 +9.980000000000001137e+01 9.885412433531765020e-01 1.150509265224245237e+00 9.787808892822331686e-01 +9.990000000000000568e+01 9.885296424672718185e-01 1.150627858086835875e+00 9.787586243082391224e-01 From 0f981d49855bb2feaff9d66ae52f42c3afdbcdd9 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Aug 2024 17:33:39 +0200 Subject: [PATCH 38/45] Fix wording in comment --- nestkernel/conn_builder.h | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 743cbb3569..6a1b31d693 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -653,35 +653,6 @@ class PoissonBuilder : public BipartiteConnBuilder void inner_connect_( const int, RngPtr, Node*, size_t ); ParameterDatum pairwise_avg_num_conns_; //!< Mean number of connections }; -/** - * Helper class to support parameter handling for tripartite builders. - * - * In tripartite builders, the actual builder class decides which connections to create and - * handles parameterization of the primary connection. For each third-party connection, - * it maintains an AuxiliaryBuilder which handles the parameterization of the corresponding - * third-party connection. - */ -/* -class AuxiliaryBuilder : public BipartiteConnBuilder -{ -public: - AuxiliaryBuilder( NodeCollectionPTR, - NodeCollectionPTR, - const DictionaryDatum&, - const std::vector< DictionaryDatum >& ); - - //! forwards to single_connect_() in underlying ConnBuilder - void single_connect( size_t, Node&, size_t, RngPtr ); - -protected: - void - connect_() override - { - // The auxiliary builder does not create connections, it only parameterizes them. - assert( false ); - } -}; -*/ class SymmetricBernoulliBuilder : public BipartiteConnBuilder { From ca73b3ea709b5b062faf7d883416a77ed5dd05d3 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Aug 2024 17:34:04 +0200 Subject: [PATCH 39/45] Remove dead code --- nestkernel/conn_builder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index b435244bc4..c9c8dd7a5d 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -924,7 +924,7 @@ nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, No pool_it = new_pool_it; } - // select third-factor neuron randomly from pool for this target + // select third-factor node randomly from pool for this target const auto third_index = pool_size_ == 1 ? 0 : rng->ulrand( pool_size_ ); const auto third_node_id = ( pool_it->second )[ third_index ].node_id; From 672008e90c504faacbb59b79972c83deac841cbe Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Aug 2024 17:59:55 +0200 Subject: [PATCH 40/45] Improved naming and comments for conn builder factories --- nestkernel/conn_builder_factory.h | 28 ++++++++++++++++++++-------- nestkernel/connection_manager.h | 4 ++-- nestkernel/connection_manager_impl.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/nestkernel/conn_builder_factory.h b/nestkernel/conn_builder_factory.h index 584acc8fd2..ccc62f0025 100644 --- a/nestkernel/conn_builder_factory.h +++ b/nestkernel/conn_builder_factory.h @@ -36,22 +36,28 @@ namespace nest { + /** - * Generic factory class for ConnBuilder objects. + * Generic factory class for bipartite ConnBuilder objects. * * This factory allows for flexible registration - * of ConnBuilder subclasses and object creation. + * of bipartite ConnBuilder subclasses and object creation. * */ -class GenericConnBuilderFactory +class GenericBipartiteConnBuilderFactory { public: - virtual ~GenericConnBuilderFactory() + virtual ~GenericBipartiteConnBuilderFactory() { } /** * Factory method for builders for bipartite connection rules (the default). + * + * @note + * - For plain bipartite connections, pass `nullptr` to `ThirdOutBuilder*`. + * - When the bipartite builder creates the primary connection of a tripartite connection, + * pass a pointer to a \class ThirdOutBuilder object. */ virtual BipartiteConnBuilder* create( NodeCollectionPTR, NodeCollectionPTR, @@ -61,10 +67,10 @@ class GenericConnBuilderFactory }; /** - * Factory class for ConnBuilders + * Factory class for bipartite ConnBuilders */ template < typename ConnBuilderType > -class ConnBuilderFactory : public GenericConnBuilderFactory +class BipartiteConnBuilderFactory : public GenericBipartiteConnBuilderFactory { public: BipartiteConnBuilder* @@ -78,6 +84,13 @@ class ConnBuilderFactory : public GenericConnBuilderFactory } }; +/** + * Generic factory class for tripartite ConnBuilder objects. + * + * This factory allows for flexible registration + * of tripartite ConnBuilder subclasses and object creation. + * + */ class GenericThirdConnBuilderFactory { public: @@ -86,7 +99,7 @@ class GenericThirdConnBuilderFactory } /** - * Factory method for builders for bipartite connection rules (the default). + * Factory method for builders for tripartite connection rules. */ virtual ThirdOutBuilder* create( NodeCollectionPTR, NodeCollectionPTR, @@ -113,7 +126,6 @@ class ThirdConnBuilderFactory : public GenericThirdConnBuilderFactory } }; - } // namespace nest #endif diff --git a/nestkernel/connection_manager.h b/nestkernel/connection_manager.h index 431ab7d1d3..e6c3888dff 100644 --- a/nestkernel/connection_manager.h +++ b/nestkernel/connection_manager.h @@ -52,7 +52,7 @@ namespace nest { -class GenericConnBuilderFactory; +class GenericBipartiteConnBuilderFactory; class GenericThirdConnBuilderFactory; class spikecounter; class Node; @@ -647,7 +647,7 @@ class ConnectionManager : public ManagerInterface DictionaryDatum connruledict_; //!< Dictionary for connection rules. //! ConnBuilder factories, indexed by connruledict_ elements. - std::vector< GenericConnBuilderFactory* > connbuilder_factories_; + std::vector< GenericBipartiteConnBuilderFactory* > connbuilder_factories_; DictionaryDatum thirdconnruledict_; //!< Dictionary for third-factor connection rules. diff --git a/nestkernel/connection_manager_impl.h b/nestkernel/connection_manager_impl.h index 7141ee3ec0..3c8d74034c 100644 --- a/nestkernel/connection_manager_impl.h +++ b/nestkernel/connection_manager_impl.h @@ -43,7 +43,7 @@ void ConnectionManager::register_conn_builder( const std::string& name ) { assert( not connruledict_->known( name ) ); - GenericConnBuilderFactory* cb = new ConnBuilderFactory< ConnBuilder >(); + GenericBipartiteConnBuilderFactory* cb = new BipartiteConnBuilderFactory< ConnBuilder >(); assert( cb ); const int id = connbuilder_factories_.size(); connbuilder_factories_.push_back( cb ); From 518f964467d43d07783784ba410cf3f9601210ad Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Aug 2024 18:00:15 +0200 Subject: [PATCH 41/45] Minor fixes to examples based on code review --- pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py | 8 ++++---- .../astrocytes/astrocyte_brunel_fixed_indegree.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py index 74673697e5..1c394a940e 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py @@ -20,8 +20,8 @@ # along with NEST. If not, see . """ -Random balanced network with astrocytes ---------------------------------------- +Random balanced network with astrocytes with Bernoulli connectivity +------------------------------------------------------------------- This script simulates a random balanced network with excitatory and inhibitory neurons and astrocytes. The astrocytes are modeled with ``astrocyte_lr_1994``, @@ -58,7 +58,7 @@ See Also ~~~~~~~~ -:doc:`astrocyte_small_network` +:doc:`astrocyte_small_network`, :doc:`astrocyte_brunel_fixed_indegree` """ @@ -80,7 +80,7 @@ "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_vp": 4, # number of VPs for NEST + "n_vp": 4, # number of virtual processes for NEST "seed": 100, # seed for the random module } diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py index 167c52908c..16d4eef971 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py @@ -20,8 +20,8 @@ # along with NEST. If not, see . """ -Random balanced network with astrocytes ---------------------------------------- +Random balanced network with astrocytes with fixed-indegree connectivity +------------------------------------------------------------------------ This script simulates a random balanced network with excitatory and inhibitory neurons and astrocytes. The astrocytes are modeled with ``astrocyte_lr_1994``, @@ -58,7 +58,7 @@ See Also ~~~~~~~~ -:doc:`astrocyte_small_network` +:doc:`astrocyte_small_network`, :doc:`astrocyte_brunel_bernoulli` """ @@ -80,7 +80,7 @@ "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 + "n_vp": 4, # number of virtual processes for NEST "seed": 100, # seed for the random module } From a2eee24331abcec0fe98c4f84fa19063fed6baf7 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Aug 2024 19:45:41 +0200 Subject: [PATCH 42/45] Extended comments on and some code improvements for conn builders --- nestkernel/conn_builder.cpp | 13 ++- nestkernel/conn_builder.h | 164 ++++++++++++++++++++++++++++-------- nestkernel/node.h | 17 ++-- 3 files changed, 146 insertions(+), 48 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index c9c8dd7a5d..f70f00f0b4 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -789,13 +789,6 @@ nest::ThirdOutBuilder::ThirdOutBuilder( const NodeCollectionPTR third, { } -void -nest::ThirdOutBuilder::connect() -{ - assert( false ); // should never be called -} - - nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCollectionPTR third, const NodeCollectionPTR targets, ThirdInBuilder* third_in, @@ -855,6 +848,9 @@ nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCo if ( not random_pool_ ) { + // Tell every target neuron its position in the target node collection. + // This is necessary to assign the right block pool to it. + // // 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. @@ -879,6 +875,9 @@ nest::ThirdBernoulliWithPoolBuilder::~ThirdBernoulliWithPoolBuilder() if ( not random_pool_ ) { + // Reset tmp_nc_index in target nodes in case a node has never been a target. + // We do not want non-invalid values to persist beyond the lifetime of this builder. + // // Here we can work in parallel since we just reset to invalid_index for ( auto tgt_it = targets_->thread_local_begin(); tgt_it != targets_->end(); ++tgt_it ) { diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 6a1b31d693..53276b2fb9 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -65,7 +65,7 @@ class ThirdOutBuilder; * the connect interface. Derived classes implement the connect * method. * - * @note Naming classes *Builder to avoid name confusion with Connector classes. + * @note This class is also the base class for all components of a TripartiteConnBuilder. */ class BipartiteConnBuilder { @@ -76,6 +76,16 @@ class BipartiteConnBuilder //! Delete synapses with or without structural plasticity virtual void disconnect(); + /** + * Create new bipartite builder. + * + * @param sources Source population to connect from + * @param targets Target population to connect to + * @param third_out `nullptr` if pure bipartite connection, pointer to \class ThirdOutBuilder object if this builder + * creates the primary connection of a tripartite connectivity + * @param conn_spec Connection specification (if part of tripartite, spec for the specific part) + * @param syn_specs Collection of synapse specifications (usually single element, several for collocated synapses) + */ BipartiteConnBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, ThirdOutBuilder* third_out, @@ -105,8 +115,6 @@ class BipartiteConnBuilder void set_synaptic_element_names( const std::string& pre_name, const std::string& post_name ); - bool all_parameters_scalar_() const; - /** * Updates the number of connected synaptic elements in the target and the source. * @@ -118,12 +126,14 @@ class BipartiteConnBuilder */ bool change_connected_synaptic_elements( size_t snode_id, size_t tnode_id, const size_t tid, int update ); + //! Return true if rule allows creation of symmetric connectivity virtual bool supports_symmetric() const { return false; } + //! Return true if rule automatically creates symmetric connectivity virtual bool is_symmetric() const { @@ -153,6 +163,8 @@ class BipartiteConnBuilder //! Implements the actual connection algorithm virtual void connect_() = 0; + bool all_parameters_scalar_() const; + virtual void sp_connect_() { @@ -204,17 +216,17 @@ class BipartiteConnBuilder */ bool loop_over_targets_() const; - NodeCollectionPTR sources_; - NodeCollectionPTR targets_; + NodeCollectionPTR sources_; //!< Population to connect from + NodeCollectionPTR targets_; //!< Population to connect to - ThirdOutBuilder* third_out_; //!< to be triggered when primary connection is created + ThirdOutBuilder* third_out_; //!< To be triggered when primary connection is created bool allow_autapses_; bool allow_multapses_; bool make_symmetric_; bool creates_symmetric_connections_; - //! buffer for exceptions raised in threads + //! Buffer for exceptions raised in threads std::vector< std::shared_ptr< WrappedThreadException > > exceptions_raised_; // Name of the pre synaptic and postsynaptic elements for this connection builder @@ -223,12 +235,18 @@ class BipartiteConnBuilder bool use_structural_plasticity_; - //! pointers to connection parameters specified as arrays + //! Pointers to connection parameters specified as arrays std::vector< ConnParameter* > parameters_requiring_skipping_; std::vector< size_t > synapse_model_id_; - //! dictionaries to pass to connect function, one per thread for every syn_spec + /** + * Dictionaries to pass to connect function, one per thread for every syn_spec + * + * Outer dim: syn_spec, inner dim: thread + * + * @note Each thread can independently modify its dictionary to pass parameters on + */ std::vector< std::vector< DictionaryDatum > > param_dicts_; private: @@ -300,20 +318,47 @@ class BipartiteConnBuilder * 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 `ThirdOutBuilder::third_connect()` method calls `register_connection()` + * for each source node to third-factor node connection that needs to be created to store this + * information in the `ThirdIn` builder. + * + * The `connect_()` method of this class needs to be called after all primary connections + * and third-factor to target connections have been created. It then exchanges information + * about required source-third connections with the other MPI ranks and creates required + * connections locally. + * * 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 BipartiteConnBuilder - const std::vector< DictionaryDatum >& ); + /** + * Create ThirdInBuilder + * + * @param sources Source population of primary connection + * @param third Third-factor population + * @param third_conn_spec is ignored by this builder but required to make base class happy + * @param syn_specs Collection of synapse specification for connection from primary source to third factor + * + * @todo Once DictionaryDatums are gone, see if we can remove `third_conn_spec` and just pass empty conn spec + * container to base-class constructor, since \class ThirdInBuilder has no connection rule properties to set. + */ + ThirdInBuilder( NodeCollectionPTR sources, + NodeCollectionPTR third, + const DictionaryDatum& third_conn_spec, + const std::vector< DictionaryDatum >& syn_specs ); ~ThirdInBuilder(); - void register_connection( size_t primary_source_id, size_t primary_target_id ); + /** + * Register required source node to third-factor node connection. + * + * @param primary_source_id GID of source node to connect from + * @param third_node_id GID of target node to connect to + */ + void register_connection( size_t primary_source_id, size_t third_node_id ); private: + //! Exchange required connection info via MPI and create needed connections locally void connect_() override; /** @@ -336,15 +381,16 @@ class ThirdInBuilder final : public BipartiteConnBuilder { } - size_t source_gid; - size_t third_gid; - size_t third_rank; + size_t source_gid; //!< GID of source node to connect from + size_t third_gid; //!< GID of third-factor node to connect to + size_t third_rank; //!< Rank of third-factor node (stored locally as it is needed multiple times) }; - //! source-thirdparty GID pairs to be communicated; one per thread + //! Container for register source-third connections, one per thread via pointer for collision free operation in + //! thread-local storage std::vector< BlockVector< SourceThirdInfo_ >* > source_third_gids_; - //! number of source-third pairs to send. Outer dimension writing thread, inner dimension rank to send to + //! Number of source-third pairs to send. Outer dimension is writing thread, inner dimension MPI rank to send to std::vector< std::vector< size_t >* > source_third_counts_; }; @@ -361,14 +407,33 @@ class ThirdInBuilder final : public BipartiteConnBuilder class ThirdOutBuilder : public BipartiteConnBuilder { public: - ThirdOutBuilder( NodeCollectionPTR, - NodeCollectionPTR, - ThirdInBuilder*, - const DictionaryDatum&, // only for compatibility with BipartiteConnBuilder - const std::vector< DictionaryDatum >& ); + /** + * Create ThirdOutBuilder. + * + * @param third Third-factor population + * @param targets Target population of primary connection + * @param third_in ThirdInBuilder which will create source-third connections later + * @param third_conn_spec Specification for third-factor connectivity + * @param syn_specs Collection of synapse specifications for third-target connections + */ + ThirdOutBuilder( const NodeCollectionPTR third, + const NodeCollectionPTR targets, + ThirdInBuilder* third_in, + const DictionaryDatum& third_conn_spec, + const std::vector< DictionaryDatum >& syn_specs ); - void connect() override; + void + connect() override final + { + assert( false ); + } //!< only call third_connect() on ThirdOutBuilder + /** + * Create third-factor connection for given primary connection. + * + * @param source_gid GID of source of primary connection + * @param target Target node of primary connection + */ virtual void third_connect( size_t source_gid, Node& target ) = 0; protected: @@ -392,7 +457,7 @@ class ConnBuilder * @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 + * @param syn_spec Collection of dictionaries with synapse specifications */ ConnBuilder( const std::string& primary_rule, NodeCollectionPTR sources, @@ -410,7 +475,8 @@ class ConnBuilder * @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"` + * are `"primary"`, `"third_in"`, `"third_out"`, and for each of these the value must be a collection of dictionaries + * with synapse specifications as for bipartite connectivity. */ ConnBuilder( const std::string& primary_rule, const std::string& third_rule, @@ -430,20 +496,23 @@ class ConnBuilder void disconnect(); private: - // order of declarations based on dependencies + // Order of declarations based on dependencies, do not change. ThirdInBuilder* third_in_builder_; ThirdOutBuilder* third_out_builder_; BipartiteConnBuilder* primary_builder_; }; - +/** + * Build third-factor connectivity based on Bernoulli trials, selecting third factor nodes from a fixed pool per target + * node. + */ class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder { public: ThirdBernoulliWithPoolBuilder( NodeCollectionPTR, NodeCollectionPTR, - ThirdInBuilder* third_in, - const DictionaryDatum&, // only for compatibility with BCB + ThirdInBuilder*, + const DictionaryDatum&, const std::vector< DictionaryDatum >& ); ~ThirdBernoulliWithPoolBuilder(); @@ -454,16 +523,39 @@ class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder connect_() override { assert( false ); - } + } //!< only call third_connect() + + /** + * For block pool, return index of first pool element for given target node. + * + * @param targe_index + */ 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_; + double p_; //!< probability of creating a third-factor connection + bool random_pool_; //!< random or block pool? + size_t pool_size_; //!< number of nodes per pool + size_t targets_per_third_; //!< number of target nodes per third-factor node + /** + * Type for single pool of third-factor nodes + * + * @todo Could probably be BlockVector, but currently some problem with back_inserter when sampling pool. + */ typedef std::vector< NodeIDTriple > PoolType_; + + //! Type mapping target GID to pool for this target typedef std::map< size_t, PoolType_ > TgtPoolMap_; + + /** + * Thread-specific pools of third-factor nodes. + * + * Each thread maintains a map from target node IDs to the third-factor node pool for that target node. + * Since each target lives on exactly one thread, there will be no overlap. For each node, the pool is + * created when a third-factor connection needs to be made to that node for the first time. + * The pools are deleted when the ConnBuilder is destroyed at the end of the connect call. + * We store a pointer instead of the map itself to ensure that the map is in thread-local memory. + */ std::vector< TgtPoolMap_* > pools_; // outer: threads }; diff --git a/nestkernel/node.h b/nestkernel/node.h index d90eecfb24..9fde5624fb 100644 --- a/nestkernel/node.h +++ b/nestkernel/node.h @@ -954,14 +954,16 @@ class Node DeprecationWarning deprecation_warning; /** - * This is only to be used to get the index in the NC to the ThirdOutBuilder + * Set index in node collection; required by 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 + * Return and invalidate index in node collection; required by ThirdOutBuilder. + * + * @note Not const since it invalidates index in node object. */ - size_t get_tmp_nc_index() const; + size_t get_tmp_nc_index(); private: @@ -1048,6 +1050,8 @@ class Node * @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. + * + * @note Set by set_tmp_nc_index() and invalidated by get_tmp_nc_index(). */ size_t tmp_nc_index_; }; @@ -1196,11 +1200,14 @@ Node::set_tmp_nc_index( size_t index ) } inline size_t -Node::get_tmp_nc_index() const +Node::get_tmp_nc_index() { assert( tmp_nc_index_ != invalid_index ); - return tmp_nc_index_; + const auto index = tmp_nc_index_; + tmp_nc_index_ = invalid_index; + + return index; } From 6744b09f3097ef83f8ffe95d0f0c0bf90a9f3ad8 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Aug 2024 20:17:56 +0200 Subject: [PATCH 43/45] Fix formatting --- nestkernel/conn_builder.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 53276b2fb9..801d4bb355 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -422,11 +422,12 @@ class ThirdOutBuilder : public BipartiteConnBuilder const DictionaryDatum& third_conn_spec, const std::vector< DictionaryDatum >& syn_specs ); + //! Only call third_connect() on ThirdOutBuilder void connect() override final { assert( false ); - } //!< only call third_connect() on ThirdOutBuilder + } /** * Create third-factor connection for given primary connection. From 5dda6c64abef4babd5bfafd86f3dc8d45b4ecf4b Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Aug 2024 20:21:08 +0200 Subject: [PATCH 44/45] Correct pool type in small network example --- pynest/examples/astrocytes/astrocyte_small_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynest/examples/astrocytes/astrocyte_small_network.py b/pynest/examples/astrocytes/astrocyte_small_network.py index 3d1838c6c5..77b506b227 100644 --- a/pynest/examples/astrocytes/astrocyte_small_network.py +++ b/pynest/examples/astrocytes/astrocyte_small_network.py @@ -157,7 +157,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 = "random" # the way to determine the astrocyte pool for each target neuron +pool_type = "block" # the way to determine the astrocyte pool for each target neuron ############################################################################### # Set astrocyte parameters. From 1233e08b47accff74a53a7e0046b741a60ebbab4 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 3 Sep 2024 13:53:43 +0200 Subject: [PATCH 45/45] Fixed formatting --- doc/htmldoc/synapses/connectivity_concepts.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/htmldoc/synapses/connectivity_concepts.rst b/doc/htmldoc/synapses/connectivity_concepts.rst index ead396e2d2..561421e1df 100644 --- a/doc/htmldoc/synapses/connectivity_concepts.rst +++ b/doc/htmldoc/synapses/connectivity_concepts.rst @@ -556,11 +556,11 @@ up to two randomly selected nodes in ``A`` (given ``pool_size == 2``). T = nest.Create('aeif_cond_alpha_astro', N_T) A = nest.Create('astrocyte_lr_1994', N_A) conn_spec = {'rule': 'pairwise_bernoulli', - 'p': p_primary} + '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} + '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, third_factor_conn_spec, syn_specs)