- Notifications
You must be signed in to change notification settings - Fork 389
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?
Conversation
jessica-mitchell left a comment
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.
Thanks for the contribution!
I have a minor change for the docs, and a question if there is a good use case we can use an example for this generator?
| @FinnBurkhardt Would you be able to address the open issues within a few days? I also added two minor suggestions. Please merge master into your branch for this PR to ensure that the testsuite works properly. |
Thanks, I’ve applied the suggested changes. For examples, I’d have two candidates at hand:
Would one (or both) be useful for the docs? |
| @FinnBurkhardt if they are ready then we could take both. And you can add it to the doc/htmldoc/examples/index.rst page: It also needs to be added to the I can also help with this if it's confusing! |
jessica-mitchell left a comment
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.
Thanks @FinnBurkhardt looks good, just a few minor formatting suggestions
jessica-mitchell left a comment
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.
thanks!
| mean | ||
| The mean value :math:`\mu` to which the process reverts (pA). | ||
| | ||
| std |
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.
On line 66 of models/ou_noise_generator.cpp, this unit is given as pA/sqrt(s)
| struct Parameters_ | ||
| { | ||
| double mean_; //!< mean current, in pA | ||
| double std_; //!< standard deviation of current, in pA |
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.
same here
| { | ||
| double mean_; //!< mean current, in pA | ||
| double std_; //!< standard deviation of current, in pA | ||
| double tau_; //!< Standard frequency in Hz |
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.
unit here seems wrong
| | ||
| /** | ||
| * Number of targets. | ||
| * This is a hidden parameter; must be placed in parameters, |
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.
Just curious why not put it into Variables?
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 am wondering the same thing, but it is the same in noise_generator and several other generators. This comment may be very old, from the time when we still had a (non-working) ResetNetwork command. But changing that should be done for all pertaining generators. I have created #3617 for follow-up and suggest we keep it as is here.
clinssen left a comment
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.
Thanks for the PR! I just have some small comments/questions.
heplesser left a comment
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.
Thanks for the revisions. I noticed a few more details and added one principal questions concerning the return from periods of inactivity.
| public: | ||
| ou_noise_generator(); | ||
| ou_noise_generator( const ou_noise_generator& ); | ||
| |
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.
| double noise_amp_; //!< | ||
| double tau_inv_; //!< |
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.
Comment texts are missing here. I find tau_inv_ misleading (would expect value 1/tau from the name), while it is 1-exp(-h/tau). Can you find a better name? Further, if I understand the code right, it is only used in in the combination P_.mean_ * V_.tau_inv_, so why not store that value in a variable mean_decayed_ (or whatever would be a good name)?
| | ||
| /** | ||
| * Number of targets. | ||
| * This is a hidden parameter; must be placed in parameters, |
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 am wondering the same thing, but it is the same in noise_generator and several other generators. This comment may be very old, from the time when we still had a (non-working) ResetNetwork command. But changing that should be done for all pertaining generators. I have created #3617 for follow-up and suggest we keep it as is here.
| const double h = Time::get_resolution().get_ms(); | ||
| const double t = kernel().simulation_manager.get_time().get_ms(); | ||
| | ||
| // scale Hz to ms |
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.
This comment seems to make no sense here.
| 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; |
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 would simplify this a bit
| 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_ ); |
| } | ||
| | ||
| // >= in case we woke from inactivity | ||
| if ( now >= B_.next_step_ ) |
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?
| You can use a :doc:`multimeter <multimeter>` to record the average current sent to all targets for each time step if | ||
| simulating on a single thread; multiple MPI processes with one thread each also work. In this case, the recording | ||
| interval of the multimeter should be equal to the `dt` of the generator to avoid aliasing effects. In multi-threaded | ||
| mode, recording of noise currents is prohibited for technical reasons. |
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 did not see that prohibition in the code.
| @@ -0,0 +1,155 @@ | |||
| # -*- coding: utf-8 -*- | |||
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.
This file should be directly in pytests. The sli2py_* directories are only for tests ported from SLI.
Add Ornstein–Uhlenbeck Noise Generator
This pull request adds a new
ou_noise_generatordevice and corresponding pytest-based tests. The device generates temporally correlated current following an Ornstein–Uhlenbeck (OU). The tests verify both parameter handling and statistical properties.Model Implementation
Files added
models/ou_noise_generator.hmodels/ou_noise_generator.cpppytests/sli2py_stimulating/test_ou_noise_generator.pyProcess equation
The state variable (x) evolves as
where
($\mu$ ) (
meanparameter) is the long-term mean of the process,($\sigma$ ) (
stdparameter) its stationary standard deviation,($\tau$ ) (
tauparameter) is the time constant,dtis the internal integration step.Integration and output
dt.Tests (
test_ou_noise_generator.py)Parameter setting and defaults
set()andSetDefaults()both correctly configure all parameters (mean,std,tau,dt).Error conditions
dtnot divisible bynest.resolutionraisesStepMultipleRequired.Mean and variance
Autocorrelation
Cross-correlation