-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
REV: 8daf144 and following. Strides must be clean to be contiguous #2735
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
Maybe there should be some a better explanation when exactly the flags are set. Possibly noting that it is still better not to rely on clean strides and that relying on it may brake (and most of the code that this broke already has problems) espeacially for older numpy if the arrays size is 0 or 1. The C-Api docu is generated from source? I can write a bit in a few days. |
A vote from the scikit-learn: without this PR, scikit-learn is fairly broken under numpy master. This PR fixes it. 👍 for merge on our side! |
@seberg The c-api isn't automatically generated, you need to write something in the appropriate |
Ok, thanks I think I will look at writing a little documentation later. However if that is the only reason to keep this open, I will just do a seperate PR for it. |
That seems appropriate. I'm not sure what is holding this up. |
I am not sure, I guess it just needs a bit review. Or is there any discussion needed for the actual remaining changes? I mean it may sound weird that a size 1 array can be non contiguous and size 0 arrays are stricter, but that is consistent with allowing third party software to rely on what they already rely on (even if I don't like it). Also the old behaviour was a bit odd with allowing only one of the two flags being set (unless ndim < 2) which is removed. |
I agree that it sounds a bit weird. To some extent, I think we are being driven by unwarranted assumptions in user code. @GaelVaroquaux Gael, what would it take to fix scikits-learn? I know that you have a lot of working sets out in the field and don't want to invalidate them, but is it possible to fix things up so that some time in the future we can finish rationalizing the behavior of these flags> |
A size 1 array can and should be both C and F-contiguous. |
@stefanv OK, I can change it to that I really don't care. The deal is, people complained about scipy and sk-learn (and probably others out there) being broken. And in reality all of these are already broken for corner cases because of rules like "A size 1 array should be (always) both C and F-contiguous". From my point of view, the change here "fixes" the misuse in other code, because I thought it nonsense to support people relying on clean strides for most cases but not for all cases. Now of course I will gladly change it back, then I have more reason to say that these are all bugs in scipy and sk-learn, because without this change, they are, they just rarely surface. I do think that this needs to be changed back (it just breaks too much suddenly), but if numpy should or should not make these abuses at least work consistently for the moment, you guys tell me. |
@seberg I feel that we should try to be consistent with the mental model of what contiguous means; I'm not sure why scikit-learn breaks, unless they depend on broken behavior inside of numpy? Perhaps I should investigate more closely; perhaps you have already? |
@stefanv to be honest I am a bit sick of this... There are two models (the first is the case in current master):
All things break in current master rely on 2. Now numpy 1.7., etc. actually provide 2. in most cases. If your mental model is 1. then yes, they all rely on broken behaviour in numpy. Now in numpy 1.7. this is not even consistently possible (for some rarer cases namely size 1 and 0 arrays), in current master you cannot rely on 2. in many more cases (so things break). This PR basically makes numpy consistently broken if your model is 1. but thus "fixes" the other packages. The fixes are all very easy, all you need to do is if you use the fact that an array is contiguous do not use |
@stefanv I believe current scipy also breaks without this code. I'm inclined to merge this fix, but I would also like sk-learn and scipy to fix things up so we can do things properly at some point in the future. Now, precisely how we get to that desired end point is a bit hazy. Suggestions on that point welcome. |
That's probably true.
I am not sure: I didn't write the code that is breaking. I had a quick
+1, we'll have a look and figure out what we can easily fix. |
The python community doesn't pay enough attention to detail regarding array shapes, types, and corner cases imho. +1 for anything that would improve the situation. |
What might mitigate this problem a little would be if we clean the strides (also with the buffer protocol, I think that is done already like that) when a contiguous array is requested. I do not know if Cython, etc. use If cython, etc. use the latter method, that should actually solve all existing problems in scipy and sk-learn. It might be a little confusing though and will certainly not solve all occurrences of this usage out there. However I actually think that it would be consistent to do it like that, and maybe its "good enough" to avoid massive problems out there and still make the flags more consistent to what most expect. |
Actually it seems to me that cython uses the buffer protocol, which should probably return cleaned up strides in any case. I actually like the above idea, even if there is still a risk of breaking something out there. Does anyone have an idea how likely that should be? |
I agree with @charris... this is a mess and we really want to get rid of it (especially this nonsense where we need heuristics to guess whether people want their row vectors to be C-contiguous or F-contiguous based on the contiguity of the arrays they came from). But the fact that it has broken all the major C-API-using packages we've looked at so far suggests that we're not going to get away with just changing it in one fell swoop :-(. (I don't much like any of the partial fixes, since none of them allow a row vector to be flagged as both C- and F-contiguous simultaneously, and that's the most common situation that requires nasty hard-to-test-and-maintain heuristics.) Here's one possible migration path, very conservative:
Hmm. This wouldn't be so bad if it were just a matter of having to remember for a few releases that, say, @seberg: You found a bunch of these problems in sklearn with a "quick look" -- did it give you any ideas for how we can find more of these problems semi-automatically? Anyone else have any ideas on heuristics we could use to issue warnings at appropriate points without drowning the user in warnings? |
I actually now think that Cython code (and with that sk-learn) is in a way OK, because Cython seems to use the buffer interface and that probably should (and easily can) clean up strides of the returned buffer instead of just setting it to the original strides. Numpy could ensure clean strides for I have started a bit on hacking that the buffer interface returns cleaned up strides and also numpy tidies up strides when explicitly asked for it (note that ie. @njsmith I just grepped for |
@njsmith I'm just trying to clarify this in my mind: a row-vector that comes from slicing a C-contiguous array must be F-contiguous False and C-contiguous False, correct? |
Its a work in progress, but https://github.com/seberg/numpy/compare/clean_requested_strides fixes those issues I know in scipy and sk-learn (by cleaning up strides when buffer/array is specifically requested as contiguous), though I did not test the buffer interface change yet. This doesn't clean anything in the python API yet though, etc. Just to note that change would leave the flags as they are in master. |
@seberg on gh-2949 asked my opinion of the strides stuff. I agree that in an ideal world, program shouldn't use the strides without checking them for dimensions of size 1. But I think that having a simple deterministic behavior is important. I also think that such change have far implication that are hard to see. Just the number of code broken by this is an example. So I think that if there is such change, it should be done for NumPy 2.0. People don't want to re-validate all code they did to check that. Also, I think we should make it easy for people to detect problem in there code with this. For example, by being able to compile NumPy that use MAX_SSIZE_T value for those strides will probably make the program crash. What ever convention is decided, I think that those arrays MUST have the align/c contiguous/f contiguous flag don't check the strides in those dimensions. i.e. This can be done by having as strides some value that make those flags computed correctly as in the past or that we update the computation of those flags to check for the new convention. So here the list of possible value that can be affected as strides to dimensions of 1:
Here, the "ptr_in2+=in2.strides[0]" will automatically reset the ptr_in2 at the right place. So there isn't any special cases for broadcasting in this cases. This cause less special case codes, simplify the code and make it faster then having condition in the loop. This convention is only good for serial code. As we go to parallel architecture, I don't think it is wise to do this. The code can pre arrange the strides it use to do this.
Also, making 1d array c and f contiguous at the same time or neither (as for 1sized array) would simplifies implementation. As conclusion, I think we shouldn't break other people code by change to strides until NumPy 2.0. Those are hard to find and debug. I consider the numpy strides for broadcating dimensions as part of the NumPy API as this is something needed when we do performance code. People that write c code with NumPy do it for only one reason: performance! So even if it wasn't writen formally somewhere, I think we should consider this part of the NumPy interface and deal with this as like that: warn people, allow them to identify problem and later do changes that break code. This discussion seam splited. If I missed important part of it, can you point them to me for reading? In all cases, thank all for your hard work. I know that improving a library without breaking stuff is not trivial. |
@seberg: as a way forward --- would it be simplest to first completely revert all the commits in gh-2694 and get that merged ASAP? After that, the "good" parts could go in a new PR (once we have a clearer idea what 3rd party code actually expects). I think it's not good to keep the master branch in a "known to be sort of wonky" state waiting for a decision on how to fix it or which parts to revert. |
Well... I honestly was not aware that this caused much trouble out there (I did not know about those cython memoryview issues). To be honest, this PR basically just reverts to the old behaviour. So no, I personally think it would be better to just review this and then merge it if it should be done fast. That was once the intention anyway. (even if after that I thought that just fixing the strides for requested contiguous may be a better option to push forward, which fails to "fix" np.ndarray[...] cython misuses) Thanks for the comment nouize I like the point about the stride value being 0 or such, definitely something to keep in mind. |
@seberg: indeed, my point was more of a organizational one (I didn't take a close look at the code changes, except that the full diff is not that big) --- a full revert is easier to understand and review, as it doesn't really involve making decisions, and it removes the unnecessary pressure to do something fast. But I'm just "cheering" from the sidelines here, I trust your judgment :) |
Well, here's a not great but still maybe better than the alternatives
It doesn't make me happy but all the alternatives I can think of seem worse.
|
Not an option, IMHO. Think about the side effects and race-conditions of |
There are no race-conditions; this is something the user would have to set (I suppose some libraries/installs might go to extreme lengths to mess with Also, there are no libraries that need the new behaviour: there are only So I think these particular objections are not an issue, though there may On Sat, Feb 2, 2013 at 9:49 AM, Gael Varoquaux notifications@github.comwrote:
|
I disagree. By creating this situation, you are opening the door to a |
I'm not sure how to respond to this. We have a mess now and we're trying to On Sat, Feb 2, 2013 at 10:46 AM, Gael Varoquaux notifications@github.comwrote:
|
Well, I don't agree with your explaination. My experience with software I don't have a better suggestion. I might say: suck it up and induce at Ideally, the change of behavior should happen in numpy 2.0, and the I understand that you want to clean the mess, but the mess is mostly in |
Okay, it sounds like we actually agree much more than we disagree :-). As I
I don't feel strongly about either of these points, so great, some Before taking the time to get into that debate in detail now, though, I'd On Sat, Feb 2, 2013 at 11:03 AM, Gael Varoquaux notifications@github.comwrote:
|
Yes, absolutely. I do think that your overall plan is good. I was just |
Well, this would be very simple... Though I do not know how to do such flagging (I guess a macro, but then the environment flag, etc...). In any case, it would be basically just this code with the flags setting changes staying in optionally (the other changes are just things that would get unnecessary but doesn't hurt either). In any case, I somewhat think it is a decent option (hoping that with cython changing its logic there is not much out there that gets broken), but I really have no idea about how much headache it might cause out there, so I don't have a real opinion... |
I like the idea of an env variable to help people debug there code. Don't forget, few people compile numpy and compiling it with optimized blas isn't trivial. So forcing people to do it is wrong I think. But I agree that we probably need to swap the behavior in NumPy 2.0. @GaelVaroquaux, do you also object of having an env variable in the 1.* series to enable the new behavior to allow easy testing of libraries with the new behavior? Also, I don't think people will try to mess with that as you told. They don't have any insentive to enable the new behavior in release version, just when they debug/develop. |
Ok, just so that I know where we stand here. First there is an agreement that it is worth to make this change in the long term? Numpy may then start defaulting to it during beta test phase in a while to hopefully make devs aware if they may have problems. That would certainly be only after the cython issues get fixed though, as before that it would trigger too often without the actual developer having any influence. Both compile time or runtime variable seem fine to me, but while I can code the rest, I would need a hint on how to implement either one to get that variable or whatever. |
@seberg Can this be closed now? |
I wish. No this is still a problem and must be fixed in this or some other form before releasing 1.8... |
@seberg: no-one seems to have objected to that summary of things, so yes, I guess we have agreement! |
@seberg: Now that we're gearing up for 1.8, what's the status on this? Do you think you'll have time to implement the switch logic discussed above soon? |
@njsmith, I think I can get it ready. Was it a compile time flag? Basically need to figure out where to add that flag/global variable, after that it is just mixing a bit of this PR with the other... shouldn't be all that huge amount of work. |
Closing this. For anyone who is interested, gh-3162 is the new pull request, which supersedes this. It is basically this pull request but introducing a flag to keep (plus additional debug help) the current master behaviour. |
This reverts those parts of PR gh-2694 that were directly related to the change in flag logic, since it seems that too much code relies at least on mostly clean strides. See also the discussion there.
Some changes remain:
itemsize=8
,strides=(16,)
andshape=(0,)
is not contiguous). This is mostly so that its really always true that if C-Contiguous thenstrides[-1] == itemsize
, and I can't see it hurting, and it "fixes" a corner case in scikits-learn.Though I would consider 2 and 3 fixes.
On a side note, I think there should be code to clean up strides a little (better). This applies to:
Oh, I think the
ISONESEGMENT
should at least a bit more accurately check forSIZE < 1
, theNDIM
check is superfluous anyway.