-
Notifications
You must be signed in to change notification settings - Fork 390
Add Ornstein–Uhlenbeck noise generator #3541
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 18 commits
da3b29a
82b48c0
500c332
1f02f0b
8be0909
d583852
d33e09c
f8f0511
fc8fa54
8fe0262
f3b27c3
c0e77e7
e7aa371
079b240
7637ff0
a012822
791beed
19b4e8f
b2915f6
76c4f32
84d645f
c40f941
7cc0493
638f9d7
d0d36c7
df7abb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,377 @@ | ||||||||||||||||||||||
| /* | ||||||||||||||||||||||
| * ou_noise_generator.cpp | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * This file is part of NEST. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * Copyright (C) 2004 The NEST Initiative | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * NEST is free software: you can redistribute it and/or modify | ||||||||||||||||||||||
| * it under the terms of the GNU General Public License as published by | ||||||||||||||||||||||
| * the Free Software Foundation, either version 2 of the License, or | ||||||||||||||||||||||
| * (at your option) any later version. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * NEST is distributed in the hope that it will be useful, | ||||||||||||||||||||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||||||||||||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||||||||||||||||||
| * GNU General Public License for more details. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * You should have received a copy of the GNU General Public License | ||||||||||||||||||||||
| * along with NEST. If not, see <http://www.gnu.org/licenses/>. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #include "ou_noise_generator.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Includes from libnestutil: | ||||||||||||||||||||||
| #include "compose.hpp" | ||||||||||||||||||||||
| #include "dict_util.h" | ||||||||||||||||||||||
| #include "logging.h" | ||||||||||||||||||||||
| #include "numerics.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Includes from nestkernel: | ||||||||||||||||||||||
| #include "event_delivery_manager_impl.h" | ||||||||||||||||||||||
| #include "kernel_manager.h" | ||||||||||||||||||||||
| #include "nest_impl.h" | ||||||||||||||||||||||
| #include "universal_data_logger_impl.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Includes from sli: | ||||||||||||||||||||||
| #include "dict.h" | ||||||||||||||||||||||
| #include "dictutils.h" | ||||||||||||||||||||||
| #include "doubledatum.h" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| namespace nest | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| void | ||||||||||||||||||||||
| register_ou_noise_generator( const std::string& name ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| register_node_model< ou_noise_generator >( name ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| RecordablesMap< ou_noise_generator > ou_noise_generator::recordablesMap_; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| template <> | ||||||||||||||||||||||
| void | ||||||||||||||||||||||
| RecordablesMap< ou_noise_generator >::create() | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| insert_( Name( names::I ), &ou_noise_generator::get_I_avg_ ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* ---------------------------------------------------------------- | ||||||||||||||||||||||
| * Default constructors defining default parameter | ||||||||||||||||||||||
| * ---------------------------------------------------------------- */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::Parameters_::Parameters_() | ||||||||||||||||||||||
| : mean_( 0.0 ) // pA | ||||||||||||||||||||||
| , std_( 0.0 ) // pA / sqrt(s) | ||||||||||||||||||||||
| , tau_( 1.0 ) // ms | ||||||||||||||||||||||
| , dt_( get_default_dt() ) | ||||||||||||||||||||||
| , num_targets_( 0 ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::Parameters_::Parameters_( const Parameters_& p ) | ||||||||||||||||||||||
| : mean_( p.mean_ ) | ||||||||||||||||||||||
| , std_( p.std_ ) | ||||||||||||||||||||||
| , tau_( p.tau_ ) | ||||||||||||||||||||||
| , dt_( p.dt_ ) | ||||||||||||||||||||||
| , num_targets_( 0 ) // we do not copy connections | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| if ( dt_.is_step() ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| dt_.calibrate(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| else | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| dt_ = get_default_dt(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::Parameters_& | ||||||||||||||||||||||
| nest::ou_noise_generator::Parameters_::operator=( const Parameters_& p ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| if ( this == &p ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| return *this; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| mean_ = p.mean_; | ||||||||||||||||||||||
| std_ = p.std_; | ||||||||||||||||||||||
| tau_ = p.tau_; | ||||||||||||||||||||||
| dt_ = p.dt_; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return *this; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::State_::State_() | ||||||||||||||||||||||
| : I_avg_( 0.0 ) // pA | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::Buffers_::Buffers_( ou_noise_generator& n ) | ||||||||||||||||||||||
| : next_step_( 0 ) | ||||||||||||||||||||||
| , logger_( n ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::Buffers_::Buffers_( const Buffers_& b, ou_noise_generator& n ) | ||||||||||||||||||||||
| : next_step_( b.next_step_ ) | ||||||||||||||||||||||
| , logger_( n ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* ---------------------------------------------------------------- | ||||||||||||||||||||||
| * Parameter extraction and manipulation functions | ||||||||||||||||||||||
| * ---------------------------------------------------------------- */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| void | ||||||||||||||||||||||
| nest::ou_noise_generator::Parameters_::get( DictionaryDatum& d ) const | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| ( *d )[ names::mean ] = mean_; | ||||||||||||||||||||||
| ( *d )[ names::std ] = std_; | ||||||||||||||||||||||
| ( *d )[ names::dt ] = dt_.get_ms(); | ||||||||||||||||||||||
| ( *d )[ names::tau ] = tau_; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| void | ||||||||||||||||||||||
| nest::ou_noise_generator::State_::get( DictionaryDatum& d ) const | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| void | ||||||||||||||||||||||
| nest::ou_noise_generator::Parameters_::set( const DictionaryDatum& d, const ou_noise_generator& n, Node* node ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| updateValueParam< double >( d, names::mean, mean_, node ); | ||||||||||||||||||||||
| updateValueParam< double >( d, names::std, std_, node ); | ||||||||||||||||||||||
| updateValueParam< double >( d, names::tau, tau_, node ); | ||||||||||||||||||||||
| if ( tau_ <= 0 ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| throw BadProperty( "tau > 0 required." ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| double dt; | ||||||||||||||||||||||
| if ( updateValueParam< double >( d, names::dt, dt, node ) ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| dt_ = Time::ms( dt ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if ( std_ < 0 ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| throw BadProperty( "The standard deviation cannot be negative." ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if ( not dt_.is_step() ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| throw StepMultipleRequired( n.get_name(), names::dt, dt_ ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* ---------------------------------------------------------------- | ||||||||||||||||||||||
| * Default and copy constructor for node | ||||||||||||||||||||||
| * ---------------------------------------------------------------- */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::ou_noise_generator() | ||||||||||||||||||||||
| : StimulationDevice() | ||||||||||||||||||||||
| , P_() | ||||||||||||||||||||||
| , S_() | ||||||||||||||||||||||
| , B_( *this ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| recordablesMap_.create(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nest::ou_noise_generator::ou_noise_generator( const ou_noise_generator& n ) | ||||||||||||||||||||||
| : StimulationDevice( n ) | ||||||||||||||||||||||
| , P_( n.P_ ) | ||||||||||||||||||||||
| , S_( n.S_ ) | ||||||||||||||||||||||
| , B_( n.B_, *this ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /* ---------------------------------------------------------------- | ||||||||||||||||||||||
| * Node initialization functions | ||||||||||||||||||||||
| * ---------------------------------------------------------------- */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| void | ||||||||||||||||||||||
| nest::ou_noise_generator::init_state_() | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| StimulationDevice::init_state(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| void | ||||||||||||||||||||||
| nest::ou_noise_generator::init_buffers_() | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| StimulationDevice::init_buffers(); | ||||||||||||||||||||||
| B_.logger_.reset(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| B_.next_step_ = 0; | ||||||||||||||||||||||
| B_.amps_.clear(); | ||||||||||||||||||||||
| B_.amps_.resize( P_.num_targets_, 0.0 ); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| void | ||||||||||||||||||||||
| nest::ou_noise_generator::pre_run_hook() | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| B_.logger_.init(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| StimulationDevice::pre_run_hook(); | ||||||||||||||||||||||
| if ( P_.num_targets_ != B_.amps_.size() ) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| LOG( M_INFO, "ou_noise_generator::pre_run_hook()", "The number of targets has changed, drawing new amplitudes." ); | ||||||||||||||||||||||
| init_buffers_(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| V_.dt_steps_ = P_.dt_.get_steps(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const double h = Time::get_resolution().get_ms(); | ||||||||||||||||||||||
| const double t = kernel().simulation_manager.get_time().get_ms(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // scale Hz to ms | ||||||||||||||||||||||
|
||||||||||||||||||||||
| const double noise_amp = P_.std_ * std::sqrt( -1 * std::expm1( -2 * h / P_.tau_ ) ); | ||||||||||||||||||||||
| const double prop = std::exp( -1 * h / P_.tau_ ); | ||||||||||||||||||||||
| const double tau_inv = -std::expm1( -h / P_.tau_ ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| V_.noise_amp_ = noise_amp; | ||||||||||||||||||||||
| V_.prop_ = prop; | ||||||||||||||||||||||
| V_.tau_inv_ = tau_inv; | ||||||||||||||||||||||
|
||||||||||||||||||||||
| const double noise_amp = P_.std_ * std::sqrt( -1 * std::expm1( -2 * h / P_.tau_ ) ); | |
| const double prop = std::exp( -1 * h / P_.tau_ ); | |
| const double tau_inv = -std::expm1( -h / P_.tau_ ); | |
| V_.noise_amp_ = noise_amp; | |
| V_.prop_ = prop; | |
| V_.tau_inv_ = tau_inv; | |
| V_.noise_amp_ = P_.std_ * std::sqrt( -1 * std::expm1( -2 * h / P_.tau_ ) ); | |
| V_.prop_ = std::exp( -1 * h / P_.tau_ ); | |
| V_.tau_inv_ = -std::expm1( -h / P_.tau_ ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if the logic for handling inactivity is correct here. Consider a situation like the following:
ou = nest.Create('ou_noise_generator', params={'start': 200, 'stop': 500'})
for k in range(10):
ou.offset = k * 1000
nest.Simulate(1000)This runs a simulation in sections of 1000 ms and in each section the generator is on from 200 to 500 ms and off in between.
With the code as it is now, we freeze the OU process while the generator is inactive. But would it not make more sense to assume that the process runs permanently and we only send its values out between start and stop? Then, amp should always be updated and only I_amp should be kept to 0 and no events sent out while the generator is inactive. Or does this not matter? Or could on be more efficient and correct in probability by updating according to the time that has passed since the generator was last active?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I hadn’t considered that case before. I’ve now implemented a version where the OU process keeps evolving even while the generator is inactive.
I tested it by repeatedly turning the generator on and off and checking the correlation between the last value before the pause and the first after resuming.
Would you consider this a useful test to add to the unit tests?
I also believe it’s possible to compute the update in a single large step, based on the time elapsed since the last update, with some bookkeeping overhead. I'll try to implement that version to assess how much more efficient it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, "jumping" over that gap mathematically should help efficiency. And if you can include the correlation check across the gap as a test, that would be very nice. Then, to test the test, try to break it by going temporarily back to the old implementation in NEST and see if the test fails.
Uh oh!
There was an error while loading. Please reload this page.