-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Add dtypes for random number generation #6869
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
Couple of questions:
|
It is too good to be true. If you call e.g. |
I actually didn't get a segmentation fault with that exact method call, but I definitely broke something behind the scenes with my changes using a method call similar to the one you proposed. Back to the drawing board. As an aside, I had a lot of issues using fused types because I'm trying to recast the return value ( |
I think the idea would be to make a function that did something like: ctypedef fused integer_type:
char
short
int
long
long long
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
cdef fill_randint_array(integer_type[:] array_data, npy_intp length, long long lo, long long diff):
cdef npy_intp i;
with self.lock, nogil:
for i from 0 <= i < length:
array_data[i] = lo + <long long>rk_interval(diff, self. internal_state) You would then probably have to call it with something like: fill_randint_arr(&array.reshape(-1)[0], ...) to trigger the right function call. Has something akin to this failed for you? |
I tried something akin to that, but the problem is that Cython considers the elements of |
The |
I'm not sure I quite follow there on the |
Also, I'm not sure I follow your comment about the proper declaration of |
Yes; what I meant is that |
It just occurred to me, how can we guarantee though that |
You have just allocated the array, so it is contiguous, and the reshape will not trigger a copy. It's unnecessary, but if you want to be extra careful, rather than calling |
Here is what I currently have:
Cython (FYI: verson |
Accidentally pushed my current and incomplete work to my fork. Luckily it is all in that new commit "FIXUP", so not all is ruined. However, I'm puzzled by the fact that AppVeyor said that my code passed, even though both builds failed to compile. Is there a bug somewhere in the AppVeyor script? |
appveyor failures are currently ignored, because there are other failing tests present in master. |
In spite of the Appveyor issue, Travis is unhappy because Cython is unhappy with that |
When things with Cython get to this point of figuring out these arcane details, I'm always strongly tempted to send it to hell and write things in C directly. That would be an entirely different kind of PITA, so lets give it one more chance... You can also get Cython to efficiently index your array, by typing the contents of the ndarray with a special buffer syntax. This is tricky, because to properly do that, you need to know the C type, and if you already knew it, you could do away with the extra function call and do the casting directly. Here and here are a couple of SO questions dealing with similar issues. I think, but you will need to figure out if it really works, that in your case the easiest would be to move the cimport cython
@cython.boundscheck(False)
cdef object randint_array(rk_state *state, ndarray[npy_integer, ndim=1] flat_array,
long long lo, long long diff, object lock):
cdef npy_intp i;
cdef npy_intp length = flat_array.shape[0]
with lock, nogil:
for i from 0 <= i < length:
flat_array[i] = lo + <long long>rk_interval(diff, state) You would then simply call this function as: return randint_array(self.internal_state, array.reshape(-1), length, lo, diff, self.lock) Good luck! |
I just tried that and no luck unfortunately. The problem is that the I thought about what you said about writing it in C. I was wondering, how does |
Does it also fail if As for the C version of this, it uses a gigantic |
I think I tried something similar to that, and then Cython said it could not find a suitable method. I believe this is what you mean (see below), correct?
|
@jaimefrio : I came up with an entirely different implementation for the array just now (see commit history). It seems to work (did some random |
You would need to test it, but I think that is going to be Python slow... I remembered that there is an example in ctypedef fused npy_integers:
char
short
int
long
long long
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
cdef fill_array(rk_state *internal_state, object lock, long lo,
unsigned long diff,
npy_integers[::1] flat_array,
npy_intp length):
cdef npy_intp i
with lock, nogil:
for i from 0 <= i < length:
flat_array[i] = lo + <long>rk_interval(diff, internal_state)
def get_fill_function(ndarray[npy_integers] array):
return <int>fill_array[npy_integers]
ctypedef void (*fill_array_t)(rk_state*, object, long, unsigned long,
void*, npy_intp) And then inside the cdef fill_array_t fill_function
...
array = <ndarray>np.empty(size, dtype)
fill_function = <fill_array_t><void*><int>get_fill_function(array.take([0]))
length = PyArray_SIZE(array)
fill_function(self.internal_state, self.lock, lo, diff,
PyArray_DATA(array), length)
return array I personally think this approach is ludicrous, especially those magic castings to get the function pointer, and would not like to see something like this creeping into numpy, but would like to hear from others. I think the proper way of doing this would be to have a small C module, probably using NumPy's templating syntax (look for a |
Ah, yes, speed is also an issue here (the slow-down is noticeable already for a 4096 * 4096 array). It seems like "simple" solutions just won't cut it here, will they? 😄 I think both options are worth considering (especially if they give flexibility without compromising speed), though I am not entirely sure how to appropriately set up the |
I gave your I gave the other implementation some thought, and if I'm not mistaken from your description, it seems like we are disguising the giant From the PR description, attaching a |
I believe I added it because my C compiler was raising a warning telling me to put it there to "speed things up". |
The stated reason to use Cython in a situation like this is that it improves maintainability, because many people are not comfortable with C, but don't mind Cython. I don't think that really applies here:
If I did not make it clear enough, I don't like this approach. Not at all. ;-) But if others think that it is not that bad, I won't fight back too hard. |
I think this is the wrong place to start. Note that the Mersenne twister used by numpy produces 32bit random integers stored in long. Hence in @rkern Thoughts? |
Small correction, the bit stream depends on the difference between lo and high. For differences <= 0xffffffff, the random streams are the same and greater differences are only supported on platforms with long > 32 bits. So there is no compatibility problem between platforms. |
@gfyoung I think adding a dtype may be more than needed. But in any case, I think you do want efficient integer streams from mtrand, perhaps in different ranges. Note that pulling off anything but longs from the state can lead to alignment problems, so everything needs to be constructed from longs. But these things should all be new, just leave the current functions as they are so there are no compatibility questions. |
Note, I think |
@charris : Sorry, I really don't follow what you just said. Could you clarify? How does this relate to |
I think you should forget about dtypes for a while and study the functions in |
@charris |
@rkern, oops, meant |
@charris : Have you looked at #6824? |
You are trying to solve what is essentially the same missing feature with three separate PRs. @charris's comments are relevant to the underlying feature if not the one part you are implementing in this PR. In particular, he is suggesting that this particular prong of your attack on this problem is probably not going to be fruitful. |
@charris Yes, I think adding |
Alright: I think there is a discrepancy between what I am understanding about random integer generation and what you guys are understanding about it. The way I see these PR's, I see them as three separate issues:
Now, you guys are of the thought that there is some fundamental issue with random number generation that somehow all of my PR's are not addressing. Let me restate again: I don't really understand what you guys are talking about. Thus, I would appreciate (for the sake of my own understanding and for properly working on these PR's instead of barking up the wrong tree for another week) if someone could lay out what exactly is going on and address these questions
I would love to help improve this issue with random number generation but being told something doesn't work without much explanation makes it difficult. Thanks. |
I haven't had the bandwidth to follow this in detail, was just trying to respond quickly since I was pinged, but I think my comment above is in the correct thread, since it's suggesting one possible strategy for handling smaller-width dtypes in randint etc. |
@njsmith: See my comment above. Also, judging from the numbers you suggested, isn't that suggestion tuned to the request in the email? |
@njsmith You need to fetch multiples of 32 bits in any case and discard the extra in order to keep the state of the rng sane. If you have internal buffers that are not cleared when the rng is reseeded the stream is not reproducible. |
I'm sorry, no idea what you folks are saying :-) My suggestion is that one crude but nonetheless possibly attractive strategy for implementing
It's not quite as efficient as all the fused-type style approaches, but with appropriate choice of BUFSIZE it might do pretty well, and is massively simpler. (It comes even closer to performance parity if we add support for |
@njsmith Of course you can cast using a buffer, that is a detail. But from my point of view we need to be able to produce 64 bit random integers on platforms with 32 bit longs. Hence, first we need I think the important thing at this point is to decide what we want and lay out the project. |
Oh sure, we need to sort out the basic int32/int64 generation routines too (I thought that was more #6824 territory but like I said I haven't followed this whole discussion closely). I was narrowly commenting on the discussion in this thread that was struggling to find a good strategy for handling narrower dtypes without adding a bunch of nasty templated code. |
☔ The latest upstream changes (presumably #6885) made this pull request unmergeable. Please resolve the merge conflicts. |
@homu is a bot, but I'm sure he appreciates being replied to. ;-) |
Whoops! Did not know that. 😄 Though the comment does seem relevant nonetheless given the discussion going on. |
Addresses request in #6790 to allow methods like
rand
andrandint
to generate numbers based ondtype
due to memory considerations.(Optional, But Recommended) Depends on #6824
If #6824 is merged in, the PR will be revised to make any newly created methods from that PR take in a
dtype
parameter.