Repository navigation
MAINT/ENH: refactor np.random.x to use np.random.Generator - #5357
Conversation
|
Hello @tupui! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:
Comment last updated at 2021-06-04 18:14:35 UTC |
|
@jni I started to work on this. I did a first pass at everything. I pushed so that this can get feedbacks and also to see the CI. |
|
I see that in a couple of places a new argument was added: it seems we were not very consistent in naming the argument |
Oh yes sorry i forgot to mention that. I can make them keyword only. Let me know the name of the keyword I should use. |
|
@tupui Gah, looks like something went wrong with a rebase...? |
|
Yes, will fix it. I had master which was in front of the changes I wanted to get from the NumPy bump PR. |
| noise = np.random.random(noisy_image.shape) | ||
| rng = np.random.default_rng() | ||
| noise = rng.random(noisy_image.shape) |
There was a problem hiding this comment.
This is going to take a long time to undo in my brain... 😅
There was a problem hiding this comment.
Haha totally, I also still need to check NumPy's doc from time to time because I am using the wrong methods.
| >>> np.random.seed(seed=1234) | ||
| >>> data += np.random.normal(size=data.shape) | ||
| >>> rng = np.random.default_rng() | ||
| >>> data += rng.normal(size=data.shape) |
There was a problem hiding this comment.
Can we perhaps continue setting the seed here (and below) so that the doctest can still be run? In some cases it's unclear whether things are suitably tested without the doctest...!
There was a problem hiding this comment.
Do you have a CI job for the doctests?
We had some discussions for SciPy (and still are having some on Astropy) and we came up with a monkeypatching of the line >>> rng = np.random.default_rng() to include a seed. To me this is no practical but it works. Here it does not seems that you have much examples which would really need a seed. Hence I would advice not to use any. And if there are things that are tested in the examples which are not in the tests, then we should simply move these to tests. In the end it's double the work for you maintainers to maintain 2 test suites otherwise.
There was a problem hiding this comment.
Yes, the doctests are configured run by default. imho the double effort is in the other direction — if we add tests in addition to doctests (which no longer become doctests), then we have to maintain them in two places.
There was a problem hiding this comment.
I was more thinking about moving than duplicating. IMO doctests should only serve to check that examples are still aligned with the implementation.
Anyway, what I can propose is to either put a seed when really necessary and comment with a DO NOT COPY. So that you could still use them for testing. For the seed itself, I would not use the same for all examples and I would get it using np.random.SeedSequence. Would that be ok?
There was a problem hiding this comment.
Yup, that all sounds fine! Sometimes an example usage is close enough to what a test would be that you can kill two birds with one stone — and we do this liberally at skimage. 😊 😬
There was a problem hiding this comment.
@hmaarrfk we had this discussion. So in the example you noted, I missed to do this. Let me know what you choose so I modify it accordingly if needed.
Co-authored-by: Juan Nunez-Iglesias <[email protected]>
|
I fixed the tests. I just have the following issues. For the first 2, I guess I need to update the files? For the last 2 I am not sure why this crashes. Can you help with that @jni? I will have a look at the examples now. _______________________________ test_3d_vs_fiji ________________________________
img_f = io.imread(fetch("data/_blobs_3d_fiji_skeleton.tif"))
___________________________ test_unsupervised_wiener ___________________________
path = fetch('restoration/tests/camera_unsup.npy')
________________________________ test_localvar _________________________________
passing but no stable with different seeds
_________________________________ test_speckle _________________________________
scikit-image/skimage/util/tests/test_random_noise.py:143: AssertionError
_________________________________ test_poisson _________________________________
scikit-image/skimage/util/tests/test_random_noise.py:154: AssertionError |
this is necessary due to change in the random noise from the new generator
I fixed the test_speckle and test_poisson and regenerated the data for |
|
I think the only failing tests now are the ones related to the change in the sha256 for the 3 files that were updated. In some of the CI logs these are being downloaded from the for example: so it is downloading the old version of the file, but comparing it to the sha256 for the new one I assume this will just work once we merge this branch. |
| seed : int, optional | ||
| Seed to initialize the random number generator. | ||
| If `None`, a random seed from the operating system is used. | ||
| seed : {None, int, `numpy.random.Generator`}, optional |
There was a problem hiding this comment.
This is a new feature and should be highlighted. I Think we things like .. versionadded::
There was a problem hiding this comment.
Indeed, also not clear what to choose as per #5359. And probably I should add it as keyword argument only with ..., *, seed. What should I put for the version?
There was a problem hiding this comment.
Yes, please make any added parameters keyword-only.
Use 0.19 for the versionadded statement.
There was a problem hiding this comment.
I don't think we reached any conclusion in #5359 on the preferred name, but I am personally leaning toward random_state. Unless there are objections, we could use that for any newly added parameter here. I wouldn't rename any of the pre-existing parameters in this PR. Let's leave that to a potential follow-up.
There was a problem hiding this comment.
To be clear, in this case I did not add the parameter but just the documentation and called behind default_rng instead of RandomState. But there are other places where I added the parameter. Correct me if I am wrong, but I should just add versionadded info to the new places but not here.
|
I think the only other task aside from the versionadded and keyword-only stuff mentioned above is adding something about this to To help avoid conflicts you may need to merge master to this branch first since a lot of entries left over from the 0.18 release were recently removed from that file in #5367. |
|
@grlee77 I think this is ready on my side. I addressed the points you listed and merged master. |
| New Features | ||
| ------------ | ||
|
|
||
| - Added a new keyword only parameter ``random_state`` to |
There was a problem hiding this comment.
| - Added a new keyword only parameter ``random_state`` to | |
| - Add a new keyword-only parameter ``random_state`` to |
| Seed to initialize the random number generator. | ||
| If `None`, a random seed from the operating system is used. | ||
| seed : {None, int, `numpy.random.Generator`}, optional | ||
| If `seed` is None the `numpy.random.Generator` singleton is used. |
There was a problem hiding this comment.
| If `seed` is None the `numpy.random.Generator` singleton is used. | |
| If `seed` is None, the `numpy.random.Generator` singleton is used. |
| Seed to initialize the random number generator. | ||
| If `None`, a random seed from the operating system is used. | ||
| random_seed : {None, int, `numpy.random.Generator`}, optional | ||
| If `random_seed` is None the `numpy.random.Generator` singleton is |
There was a problem hiding this comment.
| If `random_seed` is None the `numpy.random.Generator` singleton is | |
| If `random_seed` is None, the `numpy.random.Generator` singleton is |
| around keypoints. | ||
| sample_seed : int, optional | ||
| sample_seed : {None, int, `numpy.random.Generator`}, optional | ||
| If `sample_seed` is None the `numpy.random.Generator` singleton is |
There was a problem hiding this comment.
| If `sample_seed` is None the `numpy.random.Generator` singleton is | |
| If `sample_seed` is None, the `numpy.random.Generator` singleton is |
| If None, the random number generator is the RandomState instance used | ||
| by `np.random`. The random state is used when generating a set of | ||
| random_state : {None, int, `numpy.random.Generator`}, optional | ||
| If `random_state` is None the `numpy.random.Generator` singleton is |
There was a problem hiding this comment.
| If `random_state` is None the `numpy.random.Generator` singleton is | |
| If `random_state` is None, the `numpy.random.Generator` singleton is |
| def test_3d_vs_fiji(): | ||
| # generate an image with blobs and compate its skeleton to | ||
| # the skeleton generated by FIJI | ||
| # the skeleton generated by FIJI (Plugins>Skeleton->Skeletonize) |
There was a problem hiding this comment.
| # the skeleton generated by FIJI (Plugins>Skeleton->Skeletonize) | |
| # the skeleton generated by FIJI (Plugins > Skeleton > Skeletonize) |
or "(Plugins>Skeleton>Skeletonize)" or "(Plugins->Skeleton->Skeletonize)"
| True by default. If true, pixel values of the result above 1 or | ||
| under -1 are thresholded for skimage pipeline compatibility. | ||
| random_state : {None, int, `numpy.random.Generator`}, optional | ||
| If `random_state` is None the `numpy.random.Generator` singleton is |
There was a problem hiding this comment.
| If `random_state` is None the `numpy.random.Generator` singleton is | |
| If `random_state` is None, the `numpy.random.Generator` singleton is |
| Whether to return the full segmentation hierarchy tree and distances. | ||
| random_seed : int | ||
| random_seed : {None, int, `numpy.random.Generator`}, optional | ||
| If `random_seed` is None the `numpy.random.Generator` singleton |
There was a problem hiding this comment.
| If `random_seed` is None the `numpy.random.Generator` singleton | |
| If `random_seed` is None, the `numpy.random.Generator` singleton |
| Angles at which to compute the transform, in radians. | ||
| seed : int, optional | ||
| seed : {None, int, `numpy.random.Generator`, optional} | ||
| If `seed` is None the `numpy.random.Generator` singleton is used. |
There was a problem hiding this comment.
| If `seed` is None the `numpy.random.Generator` singleton is used. | |
| If `seed` is None, the `numpy.random.Generator` singleton is used. |
| seed : int, optional | ||
| If provided, this will set the random seed before generating noise, | ||
| seed : {None, int, `numpy.random.Generator`}, optional | ||
| If `seed` is None the `numpy.random.Generator` singleton is |
There was a problem hiding this comment.
| If `seed` is None the `numpy.random.Generator` singleton is | |
| If `seed` is None, the `numpy.random.Generator` singleton is |
|
The conflicts are related to other float32 PRs of mine that were merged, so I can help resolve them here. |
|
One issue that came up in resolving conflicts is related to the following lines in unsupervised_wiener: rng = np.random.default_rng(random_state) _rand1 = rng.standard_normal(data_spectrum.shape)
_rand1 = _rand1.astype(float_type, copy=False)
_rand2 = rng.standard_normal(data_spectrum.shape)
_rand2 = _rand2.astype(float_type, copy=False)
excursion = np.sqrt(0.5 / precision) * (_rand1 + 1j * _rand2)Here it should be more efficient to pass a _rand1 = rng.standard_normal(data_spectrum.shape, dtype=float_type)
_rand2 = rng.standard_normal(data_spectrum.shape, dtype=float_type)
excursion = np.sqrt(0.5 / precision) * (_rand1 + 1j * _rand2)However, this results in a different stream of random values for float32 vs. float64, which breaks comparison to the existing float64 reference data in Here I used |
|
Thanks @grlee77 for updating the PR. |
@grlee77 ok.
Can we do this (in a subsequent PR) by parametrizing |
This failure is for the same reason (the data files used in the tests had to be updated since the random values generated within |
|
Thanks everyone for your help! Great community 😃 |

Description
Closes #5342
Remove usage of
np.random.RandomStatein code and examples in favour ofnp.random.Generator. Only kept in tests as this is still the recommended way.Also this change all direct calls to
np.random.xto use anp.random.Generator(also recommended).For reviewers
later.
__init__.py.doc/release/release_dev.rst.