void nest::spike_generator::Parameters_::assert_valid_spike_time_and_insert_(double t, const Time& origin, const Time& now) { Time t_spike; if ( precise_times_ ) t_spike = Time::ms_stamp(t); else { // In this case, we need to force the spike time to the grid // First, convert the spike time to tics, may not be on grid t_spike = Time::ms(t); if ( not t_spike.is_grid_time() ) { if ( allow_offgrid_spikes_ ) { // In this case, we need to round to the end of the step // in which t lies, ms_stamp does that for us. t_spike = Time::ms_stamp(t); } else { std::stringstream msg; msg << "spike_generator: Time point " << t << " is not representable in current resolution."; throw BadProperty(msg.str()); } } assert(t_spike.is_grid_time()); if ( origin + t_spike == now && shift_now_spikes_ ) t_spike.advance(); } // t_spike is now the correct time stamp given the chosen options // when we get here, we know that the spike time is valid spike_stamps_.push_back(t_spike); if ( precise_times_ ) { assert(t_spike.get_ms() - t >= 0); spike_offsets_.push_back(t_spike.get_ms() - t); } }
nest::FixedOutDegreeBuilder::FixedOutDegreeBuilder( const GIDCollection& sources, const GIDCollection& targets, const DictionaryDatum& conn_spec, const DictionaryDatum& syn_spec ) : ConnBuilder( sources, targets, conn_spec, syn_spec ) , outdegree_( ( *conn_spec )[ Name( "outdegree" ) ] ) { // check for potential errors // verify that outdegree is not larger than target population if multapses are // disabled if ( not multapses_ ) { if ( ( outdegree_ > static_cast< long >( targets_->size() ) ) ) throw BadProperty( "Outdegree cannot be larger than population size." ); } }
void Node::set_status_base( const DictionaryDatum& dict ) { assert( dict.valid() ); try { set_status( dict ); } catch ( BadProperty& e ) { throw BadProperty( String::compose( "Setting status of a '%1' with GID %2: %3", get_name(), get_gid(), e.message() ) ); } updateValue< bool >( dict, names::frozen, frozen_ ); updateValue< bool >( dict, names::needs_prelim_update, needs_prelim_up_ ); }
// finds threshpassing inline nest::double_t nest::iaf_psc_alpha_canon::thresh_find_( double_t const dt ) const { switch ( P_.Interpol_ ) { case NO_INTERPOL: return dt; case LINEAR: return thresh_find1_( dt ); case QUADRATIC: return thresh_find2_( dt ); case CUBIC: return thresh_find3_( dt ); default: throw BadProperty( "Invalid interpolation order in iaf_psc_alpha_canon." ); } return 0; }
void ConnectionManager::set_prototype_status( synindex syn_id, const DictionaryDatum& d ) { assert_valid_syn_id( syn_id ); for ( thread t = 0; t < net_.get_num_threads(); ++t ) { try { prototypes_[ t ][ syn_id ]->set_status( d ); } catch ( BadProperty& e ) { throw BadProperty( String::compose( "Setting status of prototype '%1': %2", prototypes_[ t ][ syn_id ]->get_name(), e.message() ) ); } } }
double nest::iaf_psc_exp_ps::Parameters_::set(const DictionaryDatum & d) { // if E_L_ is changed, we need to adjust all variables defined relative to E_L_ const double ELold = E_L_; updateValue<double>(d, names::E_L, E_L_); const double delta_EL = E_L_ - ELold; updateValue<double>(d, names::tau_m, tau_m_); updateValue<double>(d, names::tau_syn_ex, tau_ex_); updateValue<double>(d, names::tau_syn_in, tau_in_); updateValue<double>(d, names::C_m, c_m_); updateValue<double>(d, names::t_ref, t_ref_); updateValue<double>(d, names::I_e, I_e_); if ( updateValue<double>(d, names::V_th, U_th_) ) U_th_ -= E_L_; else U_th_ -= delta_EL; if ( updateValue<double>(d, names::V_min, U_min_) ) U_min_ -= E_L_; else U_min_ -= delta_EL; if ( updateValue<double>(d, names::V_reset, U_reset_) ) U_reset_ -= E_L_; else U_reset_ -= delta_EL; if ( U_reset_ >= U_th_ ) throw BadProperty("Reset potential must be smaller than threshold."); if ( U_reset_ < U_min_ ) throw BadProperty("Reset potential must be greater equal minimum potential."); if ( c_m_ <= 0 ) throw BadProperty("Capacitance must be strictly positive."); if ( Time(Time::ms(t_ref_)).get_steps() < 1 ) throw BadProperty("Refractory time must be at least one time step."); if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 ) throw BadProperty("All time constants must be strictly positive."); if ( tau_m_ == tau_ex_ || tau_m_ == tau_in_ ) throw BadProperty("Membrane and synapse time constant(s) must differ." "See note in documentation."); return delta_EL; }
static AbstractMask* create_doughnut( const DictionaryDatum& d ) { // The doughnut (actually an annulus) is created using a DifferenceMask Position< 2 > center( 0, 0 ); if ( d->known( names::anchor ) ) center = getValue< std::vector< double > >( d, names::anchor ); const double outer = getValue< double >( d, names::outer_radius ); const double inner = getValue< double >( d, names::inner_radius ); if ( inner >= outer ) throw BadProperty( "topology::create_doughnut: " "inner_radius < outer_radius required." ); BallMask< 2 > outer_circle( center, outer ); BallMask< 2 > inner_circle( center, inner ); return new DifferenceMask< 2 >( outer_circle, inner_circle ); }
void nest::pp_pop_psc_delta::Parameters_::set( const DictionaryDatum& d ) { updateValue< long >( d, names::N, N_ ); updateValue< double >( d, names::rho_0, rho_0_ ); updateValue< double >( d, names::delta_u, delta_u_ ); updateValue< double >( d, names::len_kernel, len_kernel_ ); updateValue< double >( d, names::I_e, I_e_ ); updateValue< double >( d, names::C_m, c_m_ ); updateValue< double >( d, names::tau_m, tau_m_ ); updateValue< std::vector< double > >( d, names::taus_eta, taus_eta_ ); updateValue< std::vector< double > >( d, names::vals_eta, vals_eta_ ); if ( taus_eta_.size() != vals_eta_.size() ) throw BadProperty( String::compose( "'taus_eta' and 'vals_eta' need to have the same dimension.\nSize of taus_eta: %1\nSize of " "vals_eta: %2", taus_eta_.size(), vals_eta_.size() ) ); if ( c_m_ <= 0 ) throw BadProperty( "Capacitance must be strictly positive." ); if ( tau_m_ <= 0 ) throw BadProperty( "The time constants must be strictly positive." ); for ( uint_t i = 0; i < taus_eta_.size(); i++ ) { if ( taus_eta_[ i ] <= 0 ) throw BadProperty( "All time constants must be strictly positive." ); } if ( N_ <= 0 ) throw BadProperty( "Number of neurons must be positive." ); if ( rho_0_ < 0 ) throw BadProperty( "Rho_0 cannot be negative." ); if ( delta_u_ <= 0 ) throw BadProperty( "Delta_u must be positive." ); }
// finds threshpassing inline nest::double_t nest::iaf_psc_alpha_presc::thresh_find_( double_t const dt ) const { switch ( P_.Interpol_ ) { case NO_INTERPOL: return dt; case LINEAR: return thresh_find1_( dt ); case QUADRATIC: return thresh_find2_( dt ); case CUBIC: return thresh_find3_( dt ); default: LOG( M_ERROR, "iaf_psc_alpha_presc::thresh_find_()", "Invalid interpolation---Internal model error." ); throw BadProperty(); } return 0; }
void STDPDopaCommonProperties::set_status(const DictionaryDatum & d, ConnectorModel &cm) { CommonSynapseProperties::set_status(d, cm); long_t vtgid; if ( updateValue<long_t>(d, "vt", vtgid) ) { vt_ = dynamic_cast<volume_transmitter *>(NestModule::get_network().get_node(vtgid)); if(vt_==0) throw BadProperty("Dopamine source must be volume transmitter"); } updateValue<double_t>(d, "A_plus", A_plus_); updateValue<double_t>(d, "A_minus", A_minus_); updateValue<double_t>(d, "tau_plus", tau_plus_); updateValue<double_t>(d, "tau_c", tau_c_); updateValue<double_t>(d, "tau_n", tau_n_); updateValue<double_t>(d, "b", b_); updateValue<double_t>(d, "Wmin", Wmin_); updateValue<double_t>(d, "Wmax", Wmax_); }
void nest::aeif_cond_alpha::Parameters_::set( const DictionaryDatum& d ) { updateValue< double >( d, names::V_th, V_th ); updateValue< double >( d, names::V_peak, V_peak_ ); updateValue< double >( d, names::t_ref, t_ref_ ); updateValue< double >( d, names::E_L, E_L ); updateValue< double >( d, names::V_reset, V_reset_ ); updateValue< double >( d, names::E_ex, E_ex ); updateValue< double >( d, names::E_in, E_in ); updateValue< double >( d, names::C_m, C_m ); updateValue< double >( d, names::g_L, g_L ); updateValue< double >( d, names::tau_syn_ex, tau_syn_ex ); updateValue< double >( d, names::tau_syn_in, tau_syn_in ); updateValue< double >( d, names::a, a ); updateValue< double >( d, names::b, b ); updateValue< double >( d, names::Delta_T, Delta_T ); updateValue< double >( d, names::tau_w, tau_w ); updateValue< double >( d, names::I_e, I_e ); updateValue< double >( d, names::gsl_error_tol, gsl_error_tol ); if ( V_peak_ <= V_th ) throw BadProperty( "V_peak must be larger than threshold." ); if ( V_reset_ >= V_peak_ ) throw BadProperty( "Ensure that: V_reset < V_peak ." ); if ( C_m <= 0 ) { throw BadProperty( "Capacitance must be strictly positive." ); } if ( t_ref_ < 0 ) throw BadProperty( "Refractory time cannot be negative." ); if ( tau_syn_ex <= 0 || tau_syn_in <= 0 || tau_w <= 0 ) throw BadProperty( "All time constants must be strictly positive." ); if ( gsl_error_tol <= 0. ) throw BadProperty( "The gsl_error_tol must be strictly positive." ); }
void ConnectionManager::set_synapse_status( index gid, synindex syn_id, port p, thread tid, const DictionaryDatum& dict ) { assert_valid_syn_id( syn_id ); try { validate_pointer( connections_[ tid ].get( gid ) ) ->set_synapse_status( syn_id, *( prototypes_[ tid ][ syn_id ] ), dict, p ); } catch ( BadProperty& e ) { throw BadProperty( String::compose( "Setting status of '%1' connecting from GID %2 to port %3: %4", prototypes_[ tid ][ syn_id ]->get_name(), gid, p, e.message() ) ); } }
void nest::ac_poisson_generator::calibrate() { // This check is placed here so that it only gets executed on actual // instances, not on Model instances during a ResetKernel. // TODO: Make work for parallel simulation! if ( network()->get_num_threads() > 1 || network()->get_num_processes() > 1 ) throw BadProperty("ac_poisson_generator is presently not suitable" " for parallel simulation."); device_.calibrate(); // time resolution const double h = Time::get_resolution().get_ms(); V_.sins_.resize(B_.N_osc_); V_.coss_.resize(B_.N_osc_); V_.sins_ = std::sin(h * P_.om_); // block elements V_.coss_ = std::cos(h * P_.om_); B_.rates_.calibrate(); return; }
bool nest::ac_poisson_generator:: Parameters_::extract_array_(const DictionaryDatum &d, const std::string& dname, std::valarray<double>& data) const { if ( d->known(dname) ) { ArrayDatum *ad = dynamic_cast<ArrayDatum *>((*d)[dname].datum()); if ( ad == 0 ) throw BadProperty(); const size_t nd = ad->size(); data.resize(nd); for ( size_t n = 0 ; n < nd ; ++n ) { data[n] = getValue<double>((*ad)[n]); } return true; } else return false; }
/** * Set properties of this connection from position p in the properties * array given in dictionary. */ void STDP_Tsodyk_Connection_Plus::set_status(const DictionaryDatum & d, index p, ConnectorModel &cm) { ConnectionHetWD::set_status(d, p, cm); set_property<double_t>(d, "Us", p, U_); set_property<double_t>(d, "tau_pscs", p, tau_psc_); set_property<double_t>(d, "tau_facs", p, tau_fac_); set_property<double_t>(d, "tau_recs", p, tau_rec_); double_t x = x_; double_t y = y_; set_property<double_t>(d, "xs", p, x); set_property<double_t>(d, "ys", p, y); if (x + y > 1.0) { cm.network().message(SLIInterpreter::M_ERROR, "TsodyksConnection::set_status()", "x + y must be <= 1.0."); throw BadProperty(); } else { x_ = x; y_ = y; } set_property<double_t>(d, "us", p, u_); set_property<double_t>(d, "tau_pluss", p, tau_plus_); set_property<double_t>(d, "lambdas", p, lambda_); set_property<double_t>(d, "alphas", p, alpha_); set_property<double_t>(d, "mu_pluss", p, mu_plus_); set_property<double_t>(d, "mu_minuss", p, mu_minus_); set_property<double_t>(d, "Wmaxs", p, Wmax_); }
void nest::iaf_psc_exp_canon::Parameters_::set(const DictionaryDatum& d) { updateValue<double>(d, names::tau_m, tau_m_); updateValue<double>(d, names::tau_syn_ex, tau_ex_); updateValue<double>(d, names::tau_syn_in, tau_in_); updateValue<double>(d, names::C_m, c_m_); updateValue<double>(d, names::t_ref, t_ref_); updateValue<double>(d, names::E_L, E_L_); updateValue<double>(d, names::I_e, I_e_); if ( updateValue<double>(d, names::V_th, U_th_) ) U_th_ -= E_L_; if ( updateValue<double>(d, names::V_min, U_min_) ) U_min_ -= E_L_; if ( updateValue<double>(d, names::V_reset, U_reset_) ) U_reset_ -= E_L_; long_t tmp; if ( updateValue<long_t>(d, names::Interpol_Order, tmp) ) if ( NO_INTERPOL <= tmp && tmp < END_INTERP_ORDER ) Interpol_ = static_cast<interpOrder>(tmp); else throw BadProperty("Interpolation order must be 0, 1, 2 or 3."); if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 ) throw BadProperty("All time constants must be strictly positive."); if ( c_m_ <= 0 ) throw BadProperty("Capacitance must be strictly positive."); if ( t_ref_ < 0 ) throw BadProperty("Refractory time must not be negative."); if ( U_reset_ >= U_th_ ) throw BadProperty("Reset potential must be smaller than threshold."); if ( U_reset_ < U_min_ ) throw BadProperty("Reset potential must be greater equal minimum potential."); }
void nest::cg_connect( nest::ConnectionGeneratorDatum& cg, const index source_id, const index target_id, const DictionaryDatum& params_map, const Name& synmodel_name ) { Subnet* sources = dynamic_cast< Subnet* >( kernel().node_manager.get_node( source_id ) ); if ( sources == NULL ) { LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "sources must be a subnet." ); throw SubnetExpected(); } if ( !sources->is_homogeneous() ) { LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "sources must be a homogeneous subnet." ); throw BadProperty(); } if ( dynamic_cast< Subnet* >( *sources->local_begin() ) ) { LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "Only 1-dim subnets are supported as sources." ); throw BadProperty(); } Subnet* targets = dynamic_cast< Subnet* >( kernel().node_manager.get_node( target_id ) ); if ( targets == NULL ) { LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "targets must be a subnet." ); throw SubnetExpected(); } if ( !targets->is_homogeneous() ) { LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "targets must be a homogeneous subnet." ); throw BadProperty(); } if ( dynamic_cast< Subnet* >( *targets->local_begin() ) ) { LOG( M_ERROR, "CGConnect_cg_i_i_D_l", "Only 1-dim subnets are supported as targets." ); throw BadProperty(); } const Token synmodel = kernel().model_manager.get_synapsedict()->lookup( synmodel_name ); if ( synmodel.empty() ) throw UnknownSynapseType( synmodel_name.toString() ); const index synmodel_id = static_cast< index >( synmodel ); const modelrange source_range = kernel().modelrange_manager.get_contiguous_gid_range( ( *sources->local_begin() )->get_gid() ); index source_offset = source_range.get_first_gid(); RangeSet source_ranges; source_ranges.push_back( Range( source_range.get_first_gid(), source_range.get_last_gid() ) ); const modelrange target_range = kernel().modelrange_manager.get_contiguous_gid_range( ( *targets->local_begin() )->get_gid() ); index target_offset = target_range.get_first_gid(); RangeSet target_ranges; target_ranges.push_back( Range( target_range.get_first_gid(), target_range.get_last_gid() ) ); cg_connect( cg, source_ranges, source_offset, target_ranges, target_offset, params_map, synmodel_id ); }
void nest::amat2_psc_exp::calibrate() { // ensures initialization in case mm connected after Simulate B_.logger_.init(); const double h = Time::get_resolution().get_ms(); // numbering of state variables: // membrane potential: i_0 = 0, i_syn_ = 1, V_m_ = 2 // adaptive threshold: V_th_1_ = 1, V_th_2_ = 2 // -------------------- // membrane potential // -------------------- const double c = P_.C_; const double beta = P_.beta_; const double taum = P_.Tau_; const double tauE = P_.tau_ex_; const double tauI = P_.tau_in_; const double tauV = P_.tau_v_; // these P are independent const double eE = std::exp( -h / P_.tau_ex_ ); const double eI = std::exp( -h / P_.tau_in_ ); const double em = std::exp( -h / P_.Tau_ ); const double e1 = std::exp( -h / P_.tau_1_ ); const double e2 = std::exp( -h / P_.tau_2_ ); const double eV = std::exp( -h / P_.tau_v_ ); // V_.P00 = 1; V_.P11_ = eE; V_.P22_ = eI; V_.P33_ = em; V_.P44_ = e1; V_.P55_ = e2; V_.P66_ = eV; V_.P77_ = eV; // these depend on the above. Please do not change the order. // TODO Shortcut for beta=0 V_.P30_ = ( taum - em * taum ) / c; V_.P31_ = ( ( eE - em ) * tauE * taum ) / ( c * ( tauE - taum ) ); V_.P32_ = ( ( eI - em ) * tauI * taum ) / ( c * ( tauI - taum ) ); V_.P60_ = ( beta * ( em - eV ) * taum * tauV ) / ( c * ( taum - tauV ) ); V_.P61_ = ( beta * tauE * taum * tauV * ( eV * ( -tauE + taum ) + em * ( tauE - tauV ) + eE * ( -taum + tauV ) ) ) / ( c * ( tauE - taum ) * ( tauE - tauV ) * ( taum - tauV ) ); V_.P62_ = ( beta * tauI * taum * tauV * ( eV * ( -tauI + taum ) + em * ( tauI - tauV ) + eI * ( -taum + tauV ) ) ) / ( c * ( tauI - taum ) * ( tauI - tauV ) * ( taum - tauV ) ); V_.P63_ = ( beta * ( -em + eV ) * tauV ) / ( taum - tauV ); V_.P70_ = ( beta * taum * tauV * ( em * taum * tauV - eV * ( h * ( taum - tauV ) + taum * tauV ) ) ) / ( c * std::pow( taum - tauV, 2 ) ); V_.P71_ = ( beta * tauE * taum * tauV * ( ( em * taum * std::pow( tauE - tauV, 2 ) - eE * tauE * std::pow( taum - tauV, 2 ) ) * tauV - eV * ( tauE - taum ) * ( h * ( tauE - tauV ) * ( taum - tauV ) + tauE * taum * tauV - std::pow( tauV, 3 ) ) ) ) / ( c * ( tauE - taum ) * std::pow( tauE - tauV, 2 ) * std::pow( taum - tauV, 2 ) ); V_.P72_ = ( beta * tauI * taum * tauV * ( ( em * taum * std::pow( tauI - tauV, 2 ) - eI * tauI * std::pow( taum - tauV, 2 ) ) * tauV - eV * ( tauI - taum ) * ( h * ( tauI - tauV ) * ( taum - tauV ) + tauI * taum * tauV - std::pow( tauV, 3 ) ) ) ) / ( c * ( tauI - taum ) * std::pow( tauI - tauV, 2 ) * std::pow( taum - tauV, 2 ) ); V_.P73_ = ( beta * tauV * ( -( em * taum * tauV ) + eV * ( h * ( taum - tauV ) + taum * tauV ) ) ) / std::pow( taum - tauV, 2 ); V_.P76_ = eV * h; // tau_ref_ specifies the length of the total refractory period as // a double in ms. The grid based amat2_psc_exp can only handle refractory // periods that are integer multiples of the computation step size (h). // To ensure consistency with the overall simulation scheme such conversion // should be carried out via objects of class nest::Time. The conversion // requires 2 steps: // 1. A time object r is constructed defining representation of // tau_ref_ in tics. This representation is then converted to // computation time // steps again by a strategy defined by class nest::Time. // 2. The refractory time in units of steps is read out get_steps(), a // member function of class nest::Time. // // Choosing a tau_ref_ that is not an integer multiple of the computation time // step h will led to accurate (up to the resolution h) and self-consistent // results. However, a neuron model capable of operating with real valued // spike time may exhibit a different effective refractory time. V_.RefractoryCountsTot_ = Time( Time::ms( P_.tau_ref_ ) ).get_steps(); if ( V_.RefractoryCountsTot_ < 1 ) { throw BadProperty( "Total refractory time must be at least one time step." ); } }
void nest::RNGManager::set_status( const DictionaryDatum& d ) { // have those two for later asking, whether threads have changed: long n_threads; bool n_threads_updated = updateValue< long >( d, "local_num_threads", n_threads ); // set RNGs --- MUST come after n_threads_ is updated if ( d->known( "rngs" ) ) { // this array contains pre-seeded RNGs, so they can be used // directly, no seeding required ArrayDatum* ad = dynamic_cast< ArrayDatum* >( ( *d )[ "rngs" ].datum() ); if ( ad == 0 ) throw BadProperty(); // n_threads_ is the new value after a change of the number of // threads if ( ad->size() != ( size_t )( kernel().vp_manager.get_num_virtual_processes() ) ) { LOG( M_ERROR, "RNGManager::set_status", "Number of RNGs must equal number of virtual processes " "(threads*processes). RNGs " "unchanged." ); throw DimensionMismatch( ( size_t )( kernel().vp_manager.get_num_virtual_processes() ), ad->size() ); } // delete old generators, insert new generators this code is // robust under change of thread number in this call to // set_status, as long as it comes AFTER n_threads_ has been // upated rng_.clear(); for ( index i = 0; i < ad->size(); ++i ) if ( kernel().vp_manager.is_local_vp( i ) ) rng_.push_back( getValue< librandom::RngDatum >( ( *ad )[ kernel().vp_manager.suggest_vp( i ) ] ) ); } else if ( n_threads_updated && kernel().node_manager.size() == 0 ) { LOG( M_WARNING, "RNGManager::set_status", "Equipping threads with new default RNGs" ); create_rngs_(); } if ( d->known( "rng_seeds" ) ) { ArrayDatum* ad = dynamic_cast< ArrayDatum* >( ( *d )[ "rng_seeds" ].datum() ); if ( ad == 0 ) throw BadProperty(); if ( ad->size() != ( size_t )( kernel().vp_manager.get_num_virtual_processes() ) ) { LOG( M_ERROR, "RNGManager::set_status", "Number of seeds must equal number of virtual processes " "(threads*processes). RNGs unchanged." ); throw DimensionMismatch( ( size_t )( kernel().vp_manager.get_num_virtual_processes() ), ad->size() ); } // check if seeds are unique std::set< ulong_t > seedset; for ( index i = 0; i < ad->size(); ++i ) { long s = ( *ad )[ i ]; // SLI has no ulong tokens if ( !seedset.insert( s ).second ) { LOG( M_WARNING, "RNGManager::set_status", "Seeds are not unique across threads!" ); break; } } // now apply seeds, resets generators automatically for ( index i = 0; i < ad->size(); ++i ) { long s = ( *ad )[ i ]; if ( kernel().vp_manager.is_local_vp( i ) ) rng_[ kernel().vp_manager.vp_to_thread( kernel().vp_manager.suggest_vp( i ) ) ]->seed( s ); rng_seeds_[ i ] = s; } } // if rng_seeds // set GRNG if ( d->known( "grng" ) ) { // pre-seeded grng that can be used directly, no seeding required updateValue< librandom::RngDatum >( d, "grng", grng_ ); } else if ( n_threads_updated && kernel().node_manager.size() == 0 ) { LOG( M_WARNING, "RNGManager::set_status", "Equipping threads with new default GRNG" ); create_grng_(); } if ( d->known( "grng_seed" ) ) { const long gseed = getValue< long >( d, "grng_seed" ); // check if grng seed is unique with respect to rng seeds // if grng_seed and rng_seeds given in one SetStatus call std::set< ulong_t > seedset; seedset.insert( gseed ); if ( d->known( "rng_seeds" ) ) { ArrayDatum* ad_rngseeds = dynamic_cast< ArrayDatum* >( ( *d )[ "rng_seeds" ].datum() ); if ( ad_rngseeds == 0 ) throw BadProperty(); for ( index i = 0; i < ad_rngseeds->size(); ++i ) { const long vpseed = ( *ad_rngseeds )[ i ]; // SLI has no ulong tokens if ( !seedset.insert( vpseed ).second ) { LOG( M_WARNING, "RNGManager::set_status", "Seeds are not unique across threads!" ); break; } } } // now apply seed, resets generator automatically grng_seed_ = gseed; grng_->seed( gseed ); } // if grng_seed }
nest::ConnBuilder::ConnBuilder( const GIDCollection& sources, const GIDCollection& targets, const DictionaryDatum& conn_spec, const DictionaryDatum& syn_spec ) : sources_( &sources ) , targets_( &targets ) , autapses_( true ) , multapses_( true ) , symmetric_( false ) , exceptions_raised_( kernel().vp_manager.get_num_threads() ) , synapse_model_( kernel().model_manager.get_synapsedict()->lookup( "static_synapse" ) ) , weight_( 0 ) , delay_( 0 ) , param_dicts_() , parameters_requiring_skipping_() { // read out rule-related parameters ------------------------- // - /rule has been taken care of above // - rule-specific params are handled by subclass c'tor updateValue< bool >( conn_spec, names::autapses, autapses_ ); updateValue< bool >( conn_spec, names::multapses, multapses_ ); updateValue< bool >( conn_spec, names::symmetric, symmetric_ ); // read out synapse-related parameters ---------------------- if ( !syn_spec->known( names::model ) ) throw BadProperty( "Synapse spec must contain synapse model." ); const std::string syn_name = ( *syn_spec )[ names::model ]; if ( not kernel().model_manager.get_synapsedict()->known( syn_name ) ) throw UnknownSynapseType( syn_name ); // if another synapse than static_synapse is defined we need to make // sure that Connect can process all parameter specified if ( syn_name != "static_synapse" ) check_synapse_params_( syn_name, syn_spec ); synapse_model_ = kernel().model_manager.get_synapsedict()->lookup( syn_name ); DictionaryDatum syn_defaults = kernel().model_manager.get_connector_defaults( synapse_model_ ); // All synapse models have the possibility to set the delay (see // SynIdDelay), but some have homogeneous weights, hence it should // be possible to set the delay without the weight. default_weight_ = !syn_spec->known( names::weight ); default_delay_ = !syn_spec->known( names::delay ); // If neither weight nor delay are given in the dict, we handle this // separately. Important for hom_w synapses, on which weight cannot // be set. However, we use default weight and delay for _all_ types // of synapses. default_weight_and_delay_ = ( default_weight_ && default_delay_ ); #ifdef HAVE_MUSIC // We allow music_channel as alias for receptor_type during // connection setup ( *syn_defaults )[ names::music_channel ] = 0; #endif if ( !default_weight_and_delay_ ) { weight_ = syn_spec->known( names::weight ) ? ConnParameter::create( ( *syn_spec )[ names::weight ], kernel().vp_manager.get_num_threads() ) : ConnParameter::create( ( *syn_defaults )[ names::weight ], kernel().vp_manager.get_num_threads() ); register_parameters_requiring_skipping_( *weight_ ); delay_ = syn_spec->known( names::delay ) ? ConnParameter::create( ( *syn_spec )[ names::delay ], kernel().vp_manager.get_num_threads() ) : ConnParameter::create( ( *syn_defaults )[ names::delay ], kernel().vp_manager.get_num_threads() ); } else if ( default_weight_ ) { delay_ = syn_spec->known( names::delay ) ? ConnParameter::create( ( *syn_spec )[ names::delay ], kernel().vp_manager.get_num_threads() ) : ConnParameter::create( ( *syn_defaults )[ names::delay ], kernel().vp_manager.get_num_threads() ); } register_parameters_requiring_skipping_( *delay_ ); // Structural plasticity parameters // Check if both pre and post synaptic element are provided if ( syn_spec->known( names::pre_synaptic_element ) && syn_spec->known( names::post_synaptic_element ) ) { pre_synaptic_element_name = getValue< std::string >( syn_spec, names::pre_synaptic_element ); post_synaptic_element_name = getValue< std::string >( syn_spec, names::post_synaptic_element ); } else { if ( syn_spec->known( names::pre_synaptic_element ) || syn_spec->known( names::post_synaptic_element ) ) { throw BadProperty( "In order to use structural plasticity, both a pre and post synaptic " "element must be specified" ); } pre_synaptic_element_name = ""; post_synaptic_element_name = ""; } // synapse-specific parameters // TODO: Can we create this set once and for all? // Should not be done as static initialization, since // that might conflict with static initialization of // Name system. std::set< Name > skip_set; skip_set.insert( names::weight ); skip_set.insert( names::delay ); skip_set.insert( Name( "min_delay" ) ); skip_set.insert( Name( "max_delay" ) ); skip_set.insert( Name( "num_connections" ) ); skip_set.insert( Name( "num_connectors" ) ); skip_set.insert( Name( "property_object" ) ); skip_set.insert( Name( "synapsemodel" ) ); for ( Dictionary::const_iterator default_it = syn_defaults->begin(); default_it != syn_defaults->end(); ++default_it ) { const Name param_name = default_it->first; if ( skip_set.find( param_name ) != skip_set.end() ) continue; // weight, delay or not-settable parameter if ( syn_spec->known( param_name ) ) { synapse_params_[ param_name ] = ConnParameter::create( ( *syn_spec )[ param_name ], kernel().vp_manager.get_num_threads() ); register_parameters_requiring_skipping_( *synapse_params_[ param_name ] ); } } // Now create dictionary with dummy values that we will use // to pass settings to the synapses created. We create it here // once to avoid re-creating the object over and over again. if ( synapse_params_.size() > 0 ) { for ( index t = 0; t < kernel().vp_manager.get_num_threads(); ++t ) { param_dicts_.push_back( new Dictionary() ); for ( ConnParameterMap::const_iterator it = synapse_params_.begin(); it != synapse_params_.end(); ++it ) { if ( it->first == names::receptor_type || it->first == names::music_channel || it->first == names::synapse_label ) ( *param_dicts_[ t ] )[ it->first ] = Token( new IntegerDatum( 0 ) ); else ( *param_dicts_[ t ] )[ it->first ] = Token( new DoubleDatum( 0.0 ) ); } } } // If symmetric_ is requested call reset on all parameters in order // to check if all parameters support symmetric connections if ( symmetric_ ) { if ( weight_ ) { weight_->reset(); } if ( delay_ ) { delay_->reset(); } for ( ConnParameterMap::const_iterator it = synapse_params_.begin(); it != synapse_params_.end(); ++it ) { it->second->reset(); } } }
double iaf_psc_alpha_multisynapse::Parameters_::set( const DictionaryDatum& d ) { // if E_L_ is changed, we need to adjust all variables defined relative to // E_L_ const double ELold = E_L_; updateValue< double >( d, names::E_L, E_L_ ); const double delta_EL = E_L_ - ELold; if ( updateValue< double >( d, names::V_reset, V_reset_ ) ) { V_reset_ -= E_L_; } else { V_reset_ -= delta_EL; } if ( updateValue< double >( d, names::V_th, Theta_ ) ) { Theta_ -= E_L_; } else { Theta_ -= delta_EL; } if ( updateValue< double >( d, names::V_min, LowerBound_ ) ) { LowerBound_ -= E_L_; } else { LowerBound_ -= delta_EL; } updateValue< double >( d, names::I_e, I_e_ ); updateValue< double >( d, names::C_m, C_ ); updateValue< double >( d, names::tau_m, Tau_ ); updateValue< double >( d, names::t_ref, refractory_time_ ); if ( C_ <= 0 ) { throw BadProperty( "Capacitance must be strictly positive." ); } if ( Tau_ <= 0. ) { throw BadProperty( "Membrane time constant must be strictly positive." ); } const size_t old_n_receptors = this->n_receptors_(); if ( updateValue< std::vector< double > >( d, "tau_syn", tau_syn_ ) ) { if ( this->n_receptors_() != old_n_receptors && has_connections_ == true ) { throw BadProperty( "The neuron has connections, therefore the number of ports cannot be " "reduced." ); } for ( size_t i = 0; i < tau_syn_.size(); ++i ) { if ( tau_syn_[ i ] <= 0 ) { throw BadProperty( "All synaptic time constants must be strictly positive." ); } } } if ( refractory_time_ < 0. ) { throw BadProperty( "Refractory time must not be negative." ); } if ( V_reset_ >= Theta_ ) { throw BadProperty( "Reset potential must be smaller than threshold." ); } return delta_EL; }
inline void nest::ConnBuilder::check_synapse_params_( std::string syn_name, const DictionaryDatum& syn_spec ) { // throw error if weight is specified with static_synapse_hom_w if ( syn_name == "static_synapse_hom_w" ) { if ( syn_spec->known( names::weight ) ) throw BadProperty( "Weight cannot be specified since it needs to be equal " "for all connections when static_synapse_hom_w is used." ); return; } // throw error if n or a are set in quantal_stp_synapse, Connect cannot handle // them since they are integer if ( syn_name == "quantal_stp_synapse" ) { if ( syn_spec->known( names::n ) ) throw NotImplemented( "Connect doesn't support the setting of parameter " "n in quantal_stp_synapse. Use SetDefaults() or CopyModel()." ); if ( syn_spec->known( names::a ) ) throw NotImplemented( "Connect doesn't support the setting of parameter " "a in quantal_stp_synapse. Use SetDefaults() or CopyModel()." ); return; } // print warning if delay is specified outside cont_delay_synapse if ( syn_name == "cont_delay_synapse" ) { if ( syn_spec->known( names::delay ) ) LOG( M_WARNING, "Connect", "The delay will be rounded to the next multiple of the time step. " "To use a more precise time delay it needs to be defined within " "the synapse, e.g. with CopyModel()." ); return; } // throw error if no volume transmitter is defined or parameters are specified // that need to be introduced via CopyModel or SetDefaults if ( syn_name == "stdp_dopamine_synapse" ) { if ( syn_spec->known( "vt" ) ) throw NotImplemented( "Connect doesn't support the direct specification of the " "volume transmitter of stdp_dopamine_synapse in syn_spec." "Use SetDefaults() or CopyModel()." ); // setting of parameter c and n not thread save if ( kernel().vp_manager.get_num_threads() > 1 ) { if ( syn_spec->known( names::c ) ) throw NotImplemented( "For multi-threading Connect doesn't support the setting " "of parameter c in stdp_dopamine_synapse. " "Use SetDefaults() or CopyModel()." ); if ( syn_spec->known( names::n ) ) throw NotImplemented( "For multi-threading Connect doesn't support the setting " "of parameter n in stdp_dopamine_synapse. " "Use SetDefaults() or CopyModel()." ); } std::string param_arr[] = { "A_minus", "A_plus", "Wmax", "Wmin", "b", "tau_c", "tau_n", "tau_plus" }; std::vector< std::string > param_vec( param_arr, param_arr + 8 ); for ( std::vector< std::string >::iterator it = param_vec.begin(); it != param_vec.end(); it++ ) { if ( syn_spec->known( *it ) ) throw NotImplemented( "Connect doesn't support the setting of parameter " + *it + " in stdp_dopamine_synapse. Use SetDefaults() or CopyModel()." ); } return; } }
inline void nest::ConnBuilder::single_connect_( index sgid, Node& target, thread target_thread, librandom::RngPtr& rng ) { if ( param_dicts_.empty() ) // indicates we have no synapse params { if ( default_weight_and_delay_ ) kernel().connection_manager.connect( sgid, &target, target_thread, synapse_model_ ); else if ( default_weight_ ) kernel().connection_manager.connect( sgid, &target, target_thread, synapse_model_, delay_->value_double( target_thread, rng ) ); else { double delay = delay_->value_double( target_thread, rng ); double weight = weight_->value_double( target_thread, rng ); kernel().connection_manager.connect( sgid, &target, target_thread, synapse_model_, delay, weight ); } } else { assert( kernel().vp_manager.get_num_threads() == param_dicts_.size() ); for ( ConnParameterMap::const_iterator it = synapse_params_.begin(); it != synapse_params_.end(); ++it ) { if ( it->first == names::receptor_type || it->first == names::music_channel || it->first == names::synapse_label ) { try { // change value of dictionary entry without allocating new datum IntegerDatum* id = static_cast< IntegerDatum* >( ( ( *param_dicts_[ target_thread ] )[ it->first ] ).datum() ); ( *id ) = it->second->value_int( target_thread, rng ); } catch ( KernelException& e ) { if ( it->first == names::receptor_type ) { throw BadProperty( "Receptor type must be of type integer." ); } else if ( it->first == names::music_channel ) { throw BadProperty( "Music channel type must be of type integer." ); } else if ( it->first == names::synapse_label ) { throw BadProperty( "Synapse label must be of type integer." ); } } } else { // change value of dictionary entry without allocating new datum DoubleDatum* dd = static_cast< DoubleDatum* >( ( ( *param_dicts_[ target_thread ] )[ it->first ] ).datum() ); ( *dd ) = it->second->value_double( target_thread, rng ); } } if ( default_weight_and_delay_ ) kernel().connection_manager.connect( sgid, &target, target_thread, synapse_model_, param_dicts_[ target_thread ] ); else if ( default_weight_ ) kernel().connection_manager.connect( sgid, &target, target_thread, synapse_model_, param_dicts_[ target_thread ], delay_->value_double( target_thread, rng ) ); else { double delay = delay_->value_double( target_thread, rng ); double weight = weight_->value_double( target_thread, rng ); kernel().connection_manager.connect( sgid, &target, target_thread, synapse_model_, param_dicts_[ target_thread ], delay, weight ); } } }
MaskDatum TopologyModule::create_mask( const Token& t ) { // t can be either an existing MaskDatum, or a Dictionary containing // mask parameters MaskDatum* maskd = dynamic_cast< MaskDatum* >( t.datum() ); if ( maskd ) { return *maskd; } else { DictionaryDatum* dd = dynamic_cast< DictionaryDatum* >( t.datum() ); if ( dd == 0 ) { throw BadProperty( "Mask must be masktype or dictionary." ); } // The dictionary should contain one key which is the name of the // mask type, and optionally the key 'anchor'. To find the unknown // mask type key, we must loop through all keys. The value for the // anchor key will be stored in the anchor_token variable. Token anchor_token; bool has_anchor = false; AbstractMask* mask = 0; for ( Dictionary::iterator dit = ( *dd )->begin(); dit != ( *dd )->end(); ++dit ) { if ( dit->first == names::anchor ) { anchor_token = dit->second; has_anchor = true; } else { if ( mask != 0 ) { // mask has already been defined throw BadProperty( "Mask definition dictionary contains extraneous items." ); } mask = create_mask( dit->first, getValue< DictionaryDatum >( dit->second ) ); } } if ( has_anchor ) { // The anchor may be an array of doubles (a spatial position), or a // dictionary containing the keys 'column' and 'row' (for grid // masks only) try { std::vector< double > anchor = getValue< std::vector< double > >( anchor_token ); AbstractMask* amask; switch ( anchor.size() ) { case 2: amask = new AnchoredMask< 2 >( dynamic_cast< Mask< 2 >& >( *mask ), anchor ); break; case 3: amask = new AnchoredMask< 3 >( dynamic_cast< Mask< 3 >& >( *mask ), anchor ); break; default: throw BadProperty( "Anchor must be 2- or 3-dimensional." ); } delete mask; mask = amask; } catch ( TypeMismatch& e ) { DictionaryDatum ad = getValue< DictionaryDatum >( anchor_token ); int dim = 2; int column = getValue< long >( ad, names::column ); int row = getValue< long >( ad, names::row ); int layer; if ( ad->known( names::layer ) ) { layer = getValue< long >( ad, names::layer ); dim = 3; } switch ( dim ) { case 2: try { GridMask< 2 >& grid_mask_2d = dynamic_cast< GridMask< 2 >& >( *mask ); grid_mask_2d.set_anchor( Position< 2, int >( column, row ) ); } catch ( std::bad_cast& e ) { throw BadProperty( "Mask must be 2-dimensional grid mask." ); } break; case 3: try { GridMask< 3 >& grid_mask_3d = dynamic_cast< GridMask< 3 >& >( *mask ); grid_mask_3d.set_anchor( Position< 3, int >( column, row, layer ) ); } catch ( std::bad_cast& e ) { throw BadProperty( "Mask must be 3-dimensional grid mask." ); } break; } } } return mask; } }
bool nest::correlospinmatrix_detector::Parameters_::set( const DictionaryDatum& d, const correlospinmatrix_detector& n ) { bool reset = false; double_t t; long_t N; if ( updateValue< long_t >( d, names::N_channels, N ) ) { if ( N < 1 ) { throw BadProperty( "/N_channels can only be larger than zero." ); } else { N_channels_ = N; reset = true; } } if ( updateValue< double_t >( d, names::delta_tau, t ) ) { delta_tau_ = Time::ms( t ); reset = true; if ( t < 0 ) { throw BadProperty( "/delta_tau must not be negative." ); } } if ( updateValue< double_t >( d, names::tau_max, t ) ) { tau_max_ = Time::ms( t ); reset = true; if ( t < 0 ) { throw BadProperty( "/tau_max must not be negative." ); } } if ( updateValue< double_t >( d, names::Tstart, t ) ) { Tstart_ = Time::ms( t ); reset = true; if ( t < 0 ) { throw BadProperty( "/Tstart must not be negative." ); } } if ( updateValue< double_t >( d, names::Tstop, t ) ) { Tstop_ = Time::ms( t ); reset = true; if ( t < 0 ) { throw BadProperty( "/Tstop must not be negative." ); } } if ( !delta_tau_.is_step() ) { throw StepMultipleRequired( n.get_name(), names::delta_tau, delta_tau_ ); } if ( !tau_max_.is_multiple_of( delta_tau_ ) ) { throw TimeMultipleRequired( n.get_name(), names::tau_max, tau_max_, names::delta_tau, delta_tau_ ); } return reset; }
double nest::iaf_psc_alpha_canon::Parameters_::set( const DictionaryDatum& d ) { // if E_L_ is changed, we need to adjust all variables defined relative to // E_L_ const double ELold = E_L_; updateValue< double >( d, names::E_L, E_L_ ); const double delta_EL = E_L_ - ELold; updateValue< double >( d, names::tau_m, tau_m_ ); updateValue< double >( d, names::tau_syn, tau_syn_ ); updateValue< double >( d, names::C_m, c_m_ ); updateValue< double >( d, names::t_ref, t_ref_ ); updateValue< double >( d, names::I_e, I_e_ ); if ( updateValue< double >( d, names::V_th, U_th_ ) ) { U_th_ -= E_L_; } else { U_th_ -= delta_EL; } if ( updateValue< double >( d, names::V_min, U_min_ ) ) { U_min_ -= E_L_; } else { U_min_ -= delta_EL; } if ( updateValue< double >( d, names::V_reset, U_reset_ ) ) { U_reset_ -= E_L_; } else { U_reset_ -= delta_EL; } long tmp; if ( updateValue< long >( d, names::Interpol_Order, tmp ) ) { if ( NO_INTERPOL <= tmp && tmp < END_INTERP_ORDER ) { Interpol_ = static_cast< interpOrder >( tmp ); } else { throw BadProperty( "Invalid interpolation order. " "Valid orders are 0, 1, 2, 3." ); } } if ( U_reset_ >= U_th_ ) { throw BadProperty( "Reset potential must be smaller than threshold." ); } if ( U_reset_ < U_min_ ) { throw BadProperty( "Reset potential must be greater equal minimum potential." ); } if ( c_m_ <= 0 ) { throw BadProperty( "Capacitance must be strictly positive." ); } if ( Time( Time::ms( t_ref_ ) ).get_steps() < 1 ) { throw BadProperty( "Refractory time must be at least one time step." ); } if ( tau_m_ <= 0 || tau_syn_ <= 0 ) { throw BadProperty( "All time constants must be strictly positive." ); } return delta_EL; }
index NodeManager::add_node( index mod, long n ) // no_p { assert( current_ != 0 ); assert( root_ != 0 ); if ( mod >= kernel().model_manager.get_num_node_models() ) { throw UnknownModelID( mod ); } if ( n < 1 ) { throw BadProperty(); } const thread n_threads = kernel().vp_manager.get_num_threads(); assert( n_threads > 0 ); const index min_gid = local_nodes_.get_max_gid() + 1; const index max_gid = min_gid + n; Model* model = kernel().model_manager.get_model( mod ); assert( model != 0 ); model->deprecation_warning( "Create" ); /* current_ points to the instance of the current subnet on thread 0. The following code makes subnet a pointer to the wrapper container containing the instances of the current subnet on all threads. */ const index subnet_gid = current_->get_gid(); Node* subnet_node = local_nodes_.get_node_by_gid( subnet_gid ); assert( subnet_node != 0 ); SiblingContainer* subnet_container = dynamic_cast< SiblingContainer* >( subnet_node ); assert( subnet_container != 0 ); assert( subnet_container->num_thread_siblings() == static_cast< size_t >( n_threads ) ); assert( subnet_container->get_thread_sibling( 0 ) == current_ ); if ( max_gid > local_nodes_.max_size() || max_gid < min_gid ) { LOG( M_ERROR, "NodeManager::add:node", "Requested number of nodes will overflow the memory." ); LOG( M_ERROR, "NodeManager::add:node", "No nodes were created." ); throw KernelException( "OutOfMemory" ); } kernel().modelrange_manager.add_range( mod, min_gid, max_gid - 1 ); if ( model->potential_global_receiver() and kernel().mpi_manager.get_num_rec_processes() > 0 ) { // In this branch we create nodes for global receivers const int n_per_process = n / kernel().mpi_manager.get_num_rec_processes(); const int n_per_thread = n_per_process / n_threads + 1; // We only need to reserve memory on the ranks on which we // actually create nodes. In this if-branch ---> Only on recording // processes if ( kernel().mpi_manager.get_rank() >= kernel().mpi_manager.get_num_sim_processes() ) { local_nodes_.reserve( std::ceil( static_cast< double >( max_gid ) / kernel().mpi_manager.get_num_sim_processes() ) ); for ( thread t = 0; t < n_threads; ++t ) { // Model::reserve() reserves memory for n ADDITIONAL nodes on thread t model->reserve_additional( t, n_per_thread ); } } for ( size_t gid = min_gid; gid < max_gid; ++gid ) { const thread vp = kernel().vp_manager.suggest_rec_vp( get_n_gsd() ); const thread t = kernel().vp_manager.vp_to_thread( vp ); if ( kernel().vp_manager.is_local_vp( vp ) ) { Node* newnode = model->allocate( t ); newnode->set_gid_( gid ); newnode->set_model_id( mod ); newnode->set_thread( t ); newnode->set_vp( vp ); newnode->set_has_proxies( true ); newnode->set_local_receiver( false ); local_nodes_.add_local_node( *newnode ); // put into local nodes list current_->add_node( newnode ); // and into current subnet, thread 0. } else { local_nodes_.add_remote_node( gid ); // ensures max_gid is correct current_->add_remote_node( gid, mod ); } increment_n_gsd(); } } else if ( model->has_proxies() ) { // In this branch we create nodes for all GIDs which are on a local thread const int n_per_process = n / kernel().mpi_manager.get_num_sim_processes(); const int n_per_thread = n_per_process / n_threads + 1; // We only need to reserve memory on the ranks on which we // actually create nodes. In this if-branch ---> Only on // simulation processes if ( kernel().mpi_manager.get_rank() < kernel().mpi_manager.get_num_sim_processes() ) { // TODO: This will work reasonably for round-robin. The extra 50 entries // are for subnets and devices. local_nodes_.reserve( std::ceil( static_cast< double >( max_gid ) / kernel().mpi_manager.get_num_sim_processes() ) + 50 ); for ( thread t = 0; t < n_threads; ++t ) { // Model::reserve() reserves memory for n ADDITIONAL nodes on thread t // reserves at least one entry on each thread, nobody knows why model->reserve_additional( t, n_per_thread ); } } size_t gid; if ( kernel().vp_manager.is_local_vp( kernel().vp_manager.suggest_vp( min_gid ) ) ) { gid = min_gid; } else { gid = next_local_gid_( min_gid ); } size_t next_lid = current_->global_size() + gid - min_gid; // The next loop will not visit every node, if more than one rank is // present. // Since we already know what range of gids will be created, we can tell the // current subnet the range and subsequent calls to // `current_->add_remote_node()` // become irrelevant. current_->add_gid_range( min_gid, max_gid - 1 ); // min_gid is first valid gid i should create, hence ask for the first local // gid after min_gid-1 while ( gid < max_gid ) { const thread vp = kernel().vp_manager.suggest_vp( gid ); const thread t = kernel().vp_manager.vp_to_thread( vp ); if ( kernel().vp_manager.is_local_vp( vp ) ) { Node* newnode = model->allocate( t ); newnode->set_gid_( gid ); newnode->set_model_id( mod ); newnode->set_thread( t ); newnode->set_vp( vp ); local_nodes_.add_local_node( *newnode ); // put into local nodes list current_->add_node( newnode ); // and into current subnet, thread 0. // lid setting is wrong, if a range is set, as the subnet already // assumes, // the nodes are available. newnode->set_lid_( next_lid ); const size_t next_gid = next_local_gid_( gid ); next_lid += next_gid - gid; gid = next_gid; } else { ++gid; // brutal fix, next_lid has been set in if-branch } } // if last gid is not on this process, we need to add it as a remote node if ( not kernel().vp_manager.is_local_vp( kernel().vp_manager.suggest_vp( max_gid - 1 ) ) ) { local_nodes_.add_remote_node( max_gid - 1 ); // ensures max_gid is correct current_->add_remote_node( max_gid - 1, mod ); } } else if ( not model->one_node_per_process() ) { // We allocate space for n containers which will hold the threads // sorted. We use SiblingContainers to store the instances for // each thread to exploit the very efficient memory allocation for // nodes. // // These containers are registered in the global nodes_ array to // provide access to the instances both for manipulation by SLI // functions and so that NodeManager::calibrate() can discover the // instances and register them for updating. // // The instances are also registered with the instance of the // current subnet for the thread to which the created instance // belongs. This is mainly important so that the subnet structure // is preserved on all VPs. Node enumeration is done on by the // registration with the per-thread instances. // // The wrapper container can be addressed under the GID assigned // to no-proxy node created. If this no-proxy node is NOT a // container (e.g. a device), then each instance can be retrieved // by giving the respective thread-id to get_node(). Instances of // SiblingContainers cannot be addressed individually. // // The allocation of the wrapper containers is spread over threads // to balance memory load. size_t container_per_thread = n / n_threads + 1; // since we create the n nodes on each thread, we reserve the full load. for ( thread t = 0; t < n_threads; ++t ) { model->reserve_additional( t, n ); siblingcontainer_model_->reserve_additional( t, container_per_thread ); static_cast< Subnet* >( subnet_container->get_thread_sibling( t ) ) ->reserve( n ); } // The following loop creates n nodes. For each node, a wrapper is created // and filled with one instance per thread, in total n * n_thread nodes in // n wrappers. local_nodes_.reserve( std::ceil( static_cast< double >( max_gid ) / kernel().mpi_manager.get_num_sim_processes() ) + 50 ); for ( index gid = min_gid; gid < max_gid; ++gid ) { thread thread_id = kernel().vp_manager.vp_to_thread( kernel().vp_manager.suggest_vp( gid ) ); // Create wrapper and register with nodes_ array. SiblingContainer* container = static_cast< SiblingContainer* >( siblingcontainer_model_->allocate( thread_id ) ); container->set_model_id( -1 ); // mark as pseudo-container wrapping replicas, see reset_network() container->reserve( n_threads ); // space for one instance per thread container->set_gid_( gid ); local_nodes_.add_local_node( *container ); // Generate one instance of desired model per thread for ( thread t = 0; t < n_threads; ++t ) { Node* newnode = model->allocate( t ); newnode->set_gid_( gid ); // all instances get the same global id. newnode->set_model_id( mod ); newnode->set_thread( t ); newnode->set_vp( kernel().vp_manager.thread_to_vp( t ) ); // Register instance with wrapper // container has one entry for each thread container->push_back( newnode ); // Register instance with per-thread instance of enclosing subnet. static_cast< Subnet* >( subnet_container->get_thread_sibling( t ) ) ->add_node( newnode ); } } } else { // no proxies and one node per process // this is used by MUSIC proxies // Per r9700, this case is only relevant for music_*_proxy models, // which have a single instance per MPI process. for ( index gid = min_gid; gid < max_gid; ++gid ) { Node* newnode = model->allocate( 0 ); newnode->set_gid_( gid ); newnode->set_model_id( mod ); newnode->set_thread( 0 ); newnode->set_vp( kernel().vp_manager.thread_to_vp( 0 ) ); // Register instance local_nodes_.add_local_node( *newnode ); // and into current subnet, thread 0. current_->add_node( newnode ); } } // set off-grid spike communication if necessary if ( model->is_off_grid() ) { kernel().event_delivery_manager.set_off_grid_communication( true ); LOG( M_INFO, "NodeManager::add_node", "Neuron models emitting precisely timed spikes exist: " "the kernel property off_grid_spiking has been set to true.\n\n" "NOTE: Mixing precise-spiking and normal neuron models may " "lead to inconsistent results." ); } return max_gid - 1; }
void nest::SimulationManager::set_status( const DictionaryDatum& d ) { // Create an instance of time converter here to capture the current // representation of time objects: TICS_PER_MS and TICS_PER_STEP // will be stored in time_converter. // This object can then be used to convert times in steps // (e.g. Connection::delay_) or tics to the new representation. // We pass this object to ConnectionManager::calibrate to update // all time objects in the connection system to the new representation. // MH 08-04-14 TimeConverter time_converter; double_t time; if ( updateValue< double_t >( d, "time", time ) ) { if ( time != 0.0 ) throw BadProperty( "The simulation time can only be set to 0.0." ); if ( clock_ > TimeZero ) { // reset only if time has passed LOG( M_WARNING, "SimulationManager::set_status", "Simulation time reset to t=0.0. Resetting the simulation time is not " "fully supported in NEST at present. Some spikes may be lost, and " "stimulating devices may behave unexpectedly. PLEASE REVIEW YOUR " "SIMULATION OUTPUT CAREFULLY!" ); clock_ = Time::step( 0 ); from_step_ = 0; slice_ = 0; // clear all old spikes kernel().event_delivery_manager.configure_spike_buffers(); } } updateValue< bool >( d, "print_time", print_time_ ); // tics_per_ms and resolution must come after local_num_thread / // total_num_threads because they might reset the network and the time // representation nest::double_t tics_per_ms = 0.0; bool tics_per_ms_updated = updateValue< nest::double_t >( d, "tics_per_ms", tics_per_ms ); double_t resd = 0.0; bool res_updated = updateValue< double_t >( d, "resolution", resd ); if ( tics_per_ms_updated || res_updated ) { if ( kernel().node_manager.size() > 1 ) // root always exists { LOG( M_ERROR, "SimulationManager::set_status", "Cannot change time representation after nodes have been created. " "Please call ResetKernel first." ); throw KernelException(); } else if ( has_been_simulated() ) // someone may have simulated empty network { LOG( M_ERROR, "SimulationManager::set_status", "Cannot change time representation after the network has been " "simulated. Please call ResetKernel first." ); throw KernelException(); } else if ( kernel().connection_manager.get_num_connections() != 0 ) { LOG( M_ERROR, "SimulationManager::set_status", "Cannot change time representation after connections have been " "created. Please call ResetKernel first." ); throw KernelException(); } else if ( res_updated && tics_per_ms_updated ) // only allow TICS_PER_MS to // be changed together with // resolution { if ( resd < 1.0 / tics_per_ms ) { LOG( M_ERROR, "SimulationManager::set_status", "Resolution must be greater than or equal to one tic. Value " "unchanged." ); throw KernelException(); } else { nest::Time::set_resolution( tics_per_ms, resd ); // adjust to new resolution clock_.calibrate(); // adjust delays in the connection system to new resolution kernel().connection_manager.calibrate( time_converter ); kernel().model_manager.calibrate( time_converter ); LOG( M_INFO, "SimulationManager::set_status", "tics per ms and resolution changed." ); // make sure that wfr communication interval is always greater or equal // to resolution if no wfr is used explicitly set wfr_comm_interval // to resolution because communication in every step is needed if ( wfr_comm_interval_ < Time::get_resolution().get_ms() || not use_wfr_ ) { wfr_comm_interval_ = Time::get_resolution().get_ms(); } } } else if ( res_updated ) // only resolution changed { if ( resd < Time::get_ms_per_tic() ) { LOG( M_ERROR, "SimulationManager::set_status", "Resolution must be greater than or equal to one tic. Value " "unchanged." ); throw KernelException(); } else { Time::set_resolution( resd ); clock_.calibrate(); // adjust to new resolution // adjust delays in the connection system to new resolution kernel().connection_manager.calibrate( time_converter ); kernel().model_manager.calibrate( time_converter ); LOG( M_INFO, "SimulationManager::set_status", "Temporal resolution changed." ); // make sure that wfr communication interval is always greater or equal // to resolution if no wfr is used explicitly set wfr_comm_interval // to resolution because communication in every step is needed if ( wfr_comm_interval_ < Time::get_resolution().get_ms() || not use_wfr_ ) { wfr_comm_interval_ = Time::get_resolution().get_ms(); } } } else { LOG( M_ERROR, "SimulationManager::set_status", "change of tics_per_step requires simultaneous specification of " "resolution." ); throw KernelException(); } } // The decision whether the waveform relaxation is used // must be set before nodes are created. // Important: wfr_comm_interval_ may change depending on use_wfr_ bool wfr; if ( updateValue< bool >( d, "use_wfr", wfr ) ) { if ( kernel().node_manager.size() > 1 ) { LOG( M_ERROR, "SimulationManager::set_status", "Cannot enable/disable usage of waveform relaxation after nodes have " "been created. Please call ResetKernel first." ); throw KernelException(); } else { use_wfr_ = wfr; // if no wfr is used explicitly set wfr_comm_interval to resolution // because communication in every step is needed if ( not use_wfr_ ) { wfr_comm_interval_ = Time::get_resolution().get_ms(); } } } // wfr_comm_interval_ can only be changed if use_wfr_ is true and before // connections are created. If use_wfr_ is false wfr_comm_interval_ is set to // the resolution whenever the resolution changes. double_t wfr_interval; if ( updateValue< double_t >( d, "wfr_comm_interval", wfr_interval ) ) { if ( not use_wfr_ ) { LOG( M_ERROR, "SimulationManager::set_status", "Cannot set waveform communication interval when usage of waveform " "relaxation is disabled. Set use_wfr to true first." ); throw KernelException(); } else if ( kernel().connection_manager.get_num_connections() != 0 ) { LOG( M_ERROR, "SimulationManager::set_status", "Cannot change waveform communication interval after connections have " "been created. Please call ResetKernel first." ); throw KernelException(); } else if ( wfr_interval < Time::get_resolution().get_ms() ) { LOG( M_ERROR, "SimulationManager::set_status", "Communication interval of the waveform relaxation must be greater or " "equal to the resolution of the simulation." ); throw KernelException(); } else { LOG( M_INFO, "SimulationManager::set_status", "Waveform communication interval changed successfully. " ); wfr_comm_interval_ = wfr_interval; } } // set the convergence tolerance for the waveform relaxation method double_t tol; if ( updateValue< double_t >( d, "wfr_tol", tol ) ) { if ( tol < 0.0 ) LOG( M_ERROR, "SimulationManager::set_status", "Tolerance must be zero or positive" ); else wfr_tol_ = tol; } // set the maximal number of iterations for the waveform relaxation method long max_iter; if ( updateValue< long >( d, "wfr_max_iterations", max_iter ) ) { if ( max_iter <= 0 ) LOG( M_ERROR, "SimulationManager::set_status", "Maximal number of iterations for the waveform relaxation must be " "positive. To disable waveform relaxation set use_wfr instead." ); else wfr_max_iterations_ = max_iter; } // set the interpolation order for the waveform relaxation method long interp_order; if ( updateValue< long >( d, "wfr_interpolation_order", interp_order ) ) { if ( ( interp_order < 0 ) || ( interp_order == 2 ) || ( interp_order > 3 ) ) LOG( M_ERROR, "SimulationManager::set_status", "Interpolation order must be 0, 1, or 3." ); else wfr_interpolation_order_ = interp_order; } }
void nest::spike_generator::Parameters_::set(const DictionaryDatum& d, State_& s, const Time& origin, const Time& now) { const bool flags_changed = updateValue<bool>(d, names::precise_times, precise_times_) || updateValue<bool>(d, "allow_offgrid_spikes", allow_offgrid_spikes_) || updateValue<bool>(d, "shift_now_spikes", shift_now_spikes_); if ( precise_times_ && ( allow_offgrid_spikes_ || shift_now_spikes_ ) ) throw BadProperty("Option precise_times cannot be set to true when either allow_offgrid_spikes " "or shift_now_spikes is set to true."); const bool updated_spike_times = d->known(names::spike_times); if ( flags_changed && !(updated_spike_times || spike_stamps_.empty()) ) throw BadProperty("Options can only be set together with spike times or if no " "spike times have been set."); if ( updated_spike_times ) { const std::vector<double_t> d_times = getValue<std::vector<double> >(d->lookup(names::spike_times)); const size_t n_spikes = d_times.size(); spike_stamps_.clear(); spike_stamps_.reserve(n_spikes); spike_offsets_.clear(); if ( precise_times_ ) spike_offsets_.reserve(n_spikes); // Check spike times for ordering and grid compatibility and insert them if ( !d_times.empty() ) { // handle first spike time, no predecessor to compare with std::vector<double_t>::const_iterator prev = d_times.begin(); assert_valid_spike_time_and_insert_(*prev, origin, now); // handle all remaining spike times, compare to predecessor for ( std::vector<double_t>::const_iterator next = prev + 1; next != d_times.end() ; ++next, ++prev ) if ( *prev > *next ) throw BadProperty("Spike times must be sorted in non-descending order."); else assert_valid_spike_time_and_insert_(*next, origin, now); } } // spike_weights can be the same size as spike_times, or can be of size 0 to // only use the spike_times array bool updated_spike_weights = d->known("spike_weights"); if (updated_spike_weights) { std::vector<double> spike_weights = getValue<std::vector<double> >(d->lookup("spike_weights")); if (spike_weights.empty()) spike_weights_.clear(); else { if (spike_weights.size() != spike_stamps_.size()) throw BadProperty("spike_weights must have the same number of elements as spike_times," " or 0 elements to clear the property."); spike_weights_.swap(spike_weights); } } // Set position to start if something changed if ( updated_spike_times || updated_spike_weights || d->known(names::origin) ) s.position_ = 0; }
void nest::iaf_tum_2000::calibrate() { B_.logger_.init(); const double h = Time::get_resolution().get_ms(); // numbering of state vaiables: i_0 = 0, i_syn_ = 1, V_m_ = 2 // commented out propagators: forward Euler // needed to exactly reproduce Tsodyks network // these P are independent V_.P11ex_ = std::exp( -h / P_.tau_ex_ ); // P11ex_ = 1.0-h/tau_ex_; V_.P11in_ = std::exp( -h / P_.tau_in_ ); // P11in_ = 1.0-h/tau_in_; V_.P22_ = std::exp( -h / P_.Tau_ ); // P22_ = 1.0-h/Tau_; // these are determined according to a numeric stability criterion V_.P21ex_ = propagator_32( P_.tau_ex_, P_.Tau_, P_.C_, h ); V_.P21in_ = propagator_32( P_.tau_in_, P_.Tau_, P_.C_, h ); // P21ex_ = h/C_; // P21in_ = h/C_; V_.P20_ = P_.Tau_ / P_.C_ * ( 1.0 - V_.P22_ ); // P20_ = h/C_; // tau_ref_abs_ and tau_ref_tot_ specify the length of the corresponding // refractory periods as doubles in ms. The grid based iaf_tum_2000 can // only handle refractory periods that are integer multiples of the // computation step size (h). To ensure consistency with the overall // simulation scheme such conversion should be carried out via objects of // class nest::Time. The conversion requires 2 steps: // 1. A time object r is constructed, defining representation of // tau_ref_{abs,tot} in tics. This representation is then converted // to computation time steps again by a strategy defined by class // nest::Time. // 2. The refractory time in units of steps is read out get_steps(), a // member function of class nest::Time. // // Choosing a tau_ref_{abs,tot} that is not an integer multiple of the // computation time step h will lead to accurate (up to the resolution h) // and self- consistent results. However, a neuron model capable of // operating with real valued spike time may exhibit a different // effective refractory time. V_.RefractoryCountsAbs_ = Time( Time::ms( P_.tau_ref_abs_ ) ).get_steps(); V_.RefractoryCountsTot_ = Time( Time::ms( P_.tau_ref_tot_ ) ).get_steps(); if ( V_.RefractoryCountsAbs_ < 1 ) { throw BadProperty( "Absolute refractory time must be at least one time step." ); } if ( V_.RefractoryCountsTot_ < 1 ) { throw BadProperty( "Total refractory time must be at least one time step." ); } }