-
Notifications
You must be signed in to change notification settings - Fork 49
Add global random number generator; support in shooting pt selectors and pathmovers #998
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
Add global random number generator; support in shooting pt selectors and pathmovers #998
Conversation
This is fine in principle: my view is that we should follow NEP 29 with the exception of our until-2.0 support for Python 2.7 (and that the NEP 29 rule for NumPy should also apply to other dependencies.) NumPy 1.16 support could therefore be dropped as of 13 Jan 2021; see drop table in that link. However, it looks like NumPy 1.17 was not released for Python 2.7, so we need an approach compatible with Python 2.7. That said, fixing the test is probably better than pinning the seed. That test takes a trajectory with 5 possible shooting points, and considers two of them: frame A has about 1/3 the selection probability of frame B. The test assumes that after 100 picks, frame B will have been picked more frequently than frame A. However, apparently the odds of that going the other way is enough that we've noticed multiple failures. Maybe the better approach is to increase the number of picks, or enhance the gap in probability. This test is supposed to be an integration test; if the random number generator changed in some way that it was no longer using the probabilities we provide, we would want the test to be flaky -- that would be a sign of a problem. BTW, I'm +1 on moving random number generators to instance variables, although I think I'd tend to prefer defaulting to a global RNG (to simplify seeding for a whole simulation in the future), rather than a separate RNG instance for each OPS object instance. Maybe create the RNG instance as |
That seems fair, I increased the number of picks to
I will rewrite this PR with a single rng instance and add some compatability code for python 2.7 as well |
|
I updated the title to better reflect what I understand this will contain; better for release notes. Please correct if the current title doesn't represent the intent of the PR. |
| def __init__(self): | ||
| StorableNamedObject.__init__(self) | ||
|
|
||
| self._rng = default_rng() |
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 has to be underscored for the safe/load cycle to work
| @@ -0,0 +1,24 @@ | |||
| import numpy as np | |||
| import random | |||
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.
added the random import here to (hopefully) make the import less confusing for python2.7
Codecov Report
@@ Coverage Diff @@
## master #998 +/- ##
==========================================
+ Coverage 80.62% 81.20% +0.58%
==========================================
Files 138 139 +1
Lines 14695 15130 +435
==========================================
+ Hits 11848 12287 +439
+ Misses 2847 2843 -4
Continue to review full report at Codecov.
|
| @@ -0,0 +1,25 @@ | |||
| from __future__ import absolute_import | |||
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 is needed as python 2.7 does not understand that I don't want to import this file whenever I say import random in this file...
without this line:
Python 2.7.15 | packaged by conda-forge | (default, Mar 5 2020, 14:56:06)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from openpathsampling.random import random
>>> random.random
<module 'openpathsampling.random' from 'openpathsampling/random.py'>
>>> random.random.random
<module 'openpathsampling.random' from 'openpathsampling/random.py'>
>>> random.random.random.random
<module 'openpathsampling.random' from 'openpathsampling/random.py'>with this line:
Python 2.7.15 | packaged by conda-forge | (default, Mar 5 2020, 14:56:06)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from openpathsampling.random import random
>>> random.random
<built-in method random of Random object at 0x555a53ee5080>|
@dwhswenson Finally figured out what was wrong with python While hunting this down I also added the |
dwhswenson
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.
Most changes very minor. One point I'm a little confused about in the implications of making default_rng a function, instead of just importing an instance. That's worth some additional discussion.
| @@ -1,4 +1,4 @@ | |||
| import random | |||
| from openpathsampling.random import random | |||
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 take it this is the necessary way of getting to stdlib random? (Long term -- being before 2.0 -- everything in OPS should be using paths.default_rng)
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, just to make name-space clashes less likely
| rng = np.random.default_rng() | ||
|
|
||
|
|
||
| def default_rng(): |
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 don't think this should be a function. Is there a specific reason it must be? Here are my reasons:
- A function implies to users that this is creating something.
- The behavior is specifically different from what
np.random.default_rng()does.
If you do your TestRandom.test_identities test with np.random.default_rng(), it will fail the is test.
Instead, either name rng as default_rng or DEFAULT_RNG (to really emphasize that it is constant). You can still import it into the main openpathsampling namespace.
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.
nvm. I think this has to be a function so that the underlying rng object can change, but newly created instances get the new rng. Nonetheless, please rename rng to something like DEFAULT_RNG.
If I understand correctly, in that case, setting the seed before a simulation starts would be something like paths.random.DEFAULT_RNG = np.random.default_rng(seed) before creating any objects. Is that right?
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.
Nonetheless, please rename rng to something like DEFAULT_RNG
Will do
If I understand correctly, in that case, setting the seed before a simulation starts would be something like paths.random.DEFAULT_RNG = np.random.default_rng(seed) before creating any objects. Is that right?
Yes, exactly (except that it has to go through paths.random.RandomState(seed) for np<1.17) .
Co-authored-by: David W.H. Swenson <dwhs@hyperblazer.net>
|
LGTM! |
This week my CI failed with:
This PR attaches a random number generator to the selectors that can be seeded during tests to prevent these flaky tests.
The current implementation requires
numpy>=1.17(that is when numpy swapped fromRandomStatetoGenerator) @dwhswenson please let me know if that is too strict of a requirement and if I should write some code to support the LegacyRandomState