-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
ENH: expose bit_generator
and random C-API to cython
#15463
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
Conversation
Nothing other than what you say. The "first best" would be to ship a lib/so that could be linked to like npymath.lib/so, e.g., npyrandom.lib/so (or possibly to stick them in npymath if possible). |
the npymath parallel looks good. I will try that, thanks |
751d849
to
db01d81
Compare
Looks like a good and surprisingly simple solution. |
@rkern I don't understand the test failure in test_random.py 1585 in the threaded tests. on windows. Any thoughts? |
e9d27e7
to
af317e7
Compare
af317e7
to
f66326a
Compare
numpy/random/setup.py
Outdated
@@ -52,78 +54,87 @@ def generate_libraries(ext, build_dir): | |||
PCG64_DEFS = [] | |||
# One can force emulated 128-bit arithmetic if one wants. | |||
#PCG64_DEFS += [('PCG_FORCE_EMULATED_128BIT_MATH', '1')] | |||
depends = ['__init__.pxd', 'c_distributions.pxd', '_bit_generator.pxd'] |
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.
_bit_generator.pxd
existed in 1.18, but was not copied into the wheel. The use of depends
on line 137 should fix that.
apparently something in this PR broke windows. Maybe the npyrandom.lib is off somehow |
# Cast the pointer | ||
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name) | ||
randoms = np.empty(n, dtype=_dtype) | ||
if _dtype is np.float32: |
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.
if _dtype is np.float32: | |
if _dtype.type is np.float32: |
or
if _dtype is np.float32: | |
if _dtype == np.float32: |
As written this is always false. The former is more consistent with line 100.
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.
Got be careful here, the latter is correct. The former would have to also check _dtype.isnative
. Right now dtype=">f8"
(on little endian machines) is probably broken!
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.
However, I think I actually like the dtype.type is np.float32 and dtype.isnative
spelling I think.
EDIT: I guess here the isnative
check needs to be further up anyway though, maybe just create an issue for it as a separate thing.
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 meant as a test-run for something to replace the key = np.dtype(dtype).name
in _generator.pyx
and mtrand.pyx
so it would be good to get it right here.
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 think dtype == np.float32
is probably simplest, but make sure to update line 100 too.
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.
True dtype == ...
is shortest. May be worth to time it in case the type + isnative happens to be much faster (right now).
I timed it, and it:
In [2]: %timeit dt == np.float64
85 ns ± 5.03 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [3]: %timeit dt.type is np.float64
57.3 ns ± 0.108 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [4]: %timeit dt.type is np.float64 and dt.isnative
81.6 ns ± 0.697 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
so since the ==
includes the isnative check there is no speed difference between the spellings , so probably should use the ==
one. The only thing we could consider with respect to fixing the speed issue of dtype.name
check would be to add a fast-path for the very common dtype default.
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.
using the ==
form
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.
Still seems to be the ==
form here?
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.
fixing
"Dogfooding" our code is helpful. The use of the |
numpy/random/mtrand.pyx
Outdated
double r | ||
double q | ||
double fm | ||
int64_t m | ||
long m | ||
double p1 | ||
double xm | ||
double xl |
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.
cython doesn't seem to care what int
is used in the struct, it is later filled out by the compiler. But cleaning this up anyway for consistency.
#include "ziggurat_constants.h" | ||
#include "logfactorial.h" | ||
|
||
#if defined(_MSC_VER) && defined(_WIN64) | ||
#include <intrin.h> | ||
#include <intrin.h> | ||
#endif | ||
|
||
/* Inline generators for internal use */ |
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 is formatted with 2-space tabs instead of 4-space. Is now a good time to remedy that?
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 happy if you do that (just ping Kevin on it before merging?), should be its own pure STY PR though (and if possible cover other files in random if they exist?). Not sure how others feel about such fixups, but I tend to feel that not doing style cleanups is also not a solution...
+1 to move |
@r-devulap the reciprical strided test is failing here as well |
Hmm, did that test failure vanish? @mattip |
I reran the failing CI run, and it dissappeared. But see gh-15610 |
2fc146f
to
8c6a68b
Compare
CI is passing after the |
Added a release note, CI is still green. |
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 ping Matti. LGTM, the is
part should be fixed in the example but everything else we can do later I think, so lets keep things moving.
# Cast the pointer | ||
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name) | ||
randoms = np.empty(n, dtype=_dtype) | ||
if _dtype is np.float32: |
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.
Still seems to be the ==
form here?
install_dir='lib', | ||
build_info={ | ||
'include_dirs' : [], # empty list required for creating npyrandom.h | ||
'extra_compiler_args' : (['/GL-'] if is_msvc else []), |
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.
Would it make sense to comment on why global optimization is disabled? (and maybe that it is global optimization?)
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 copied it from numpy/core/setup.py
Sould the release notes in the same or different fragment include that |
Mentioned the |
@@ -106,7 +106,7 @@ def uniforms_ex(bit_generator, Py_ssize_t n, dtype=np.float64): | |||
# Cast the pointer | |||
rng = <bitgen_t *> PyCapsule_GetPointer(capsule, capsule_name) | |||
randoms = np.empty(n, dtype=_dtype) | |||
if _dtype is np.float32: | |||
if _dtype == np.float32: |
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.
Sorry, I did not look at it careful enough again right now. This is still incorrect for non-native byte order, would need to add an error branch and elif _dtype == np.float64
. On the up-side, you can just move the above raise TypeError('Unsupported dtype "%r"' % dtype)
in the else branch then, so that it is nicer anyway.
I can do that if you are getting tired of this :).
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.
done
bit_generator
and random C-API to cython
Thanks, I renamed it the title, hope that fits well enough (if not, can change it in the PR title at least, will squash merge). |
@mattip Hmm, I missed this for the 1.18.1 release (wrong milestone) and it may be a bit late to put it in the 1.18.3 release. It doesn't seem right to add new functionality so late in the release cycle. |
I think there is no need to rush is out since it is an enhancement and not a bug. 1.19 seems like a good choice.. |
@bashtage OK, let's go with that. |
@charris @bashtage Can we backport at least the |
We could try, the wheels code may have bitrotted, making things more difficult. Want to make a PR? |
xref gh-14778
As pointed out in the comment by @jamesthomasgriffin, we did not include a
pxd
file to expose the distribution functions documented in the random c-api. This PR adds ac_distributions.pxd
file that exposes them.A test is still needed that this all works as advertised. Could someone familiar with typical use please suggest one?Complicated test to build a cython c-extension module added.