Skip to content
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

BUG: Fix iteration over reversed subspaces in mapiter_@name@ #8284

Merged
merged 1 commit into from
Nov 22, 2016

Conversation

rainwoodman
Copy link
Contributor

This PR will fix 8264. It is a WIP.

@@ -1707,10 +1712,22 @@ mapiter_@name@(PyArrayMapIterObject *mit)
}

/*
* Resetting is slow, so skip if the subspace iteration has
* only a single inner loop.
* Resetting is slow, so skip if the subspace iteration is
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seberg I am not sure if the prediction works correctly for higher dimensional subspace.
Saying the fastest dimension has strides == +-itemsize, but the last-fastest dimension doesn't. Not sure if I can quickly come up with a test case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me try to explain nditer a bit, I think it will make things clear why it should work. I am not sure it might help but...

The advanced iterator will always do some "iteration", after each of these there is an inner loop optimization. This inner loop can be iterated trivially by a single stride. Now in principle the inner stride you need can change of course.
This is why there is the check that the innerloop is actually the complete iteration. If the innerloop spans the whole iteration there is no reason for the innermost stride to change later on (assuming no buffering, buffering might add weirdness). The only reason why this could happen is if the strided layout is different for different "subspaces/inner iteration positions", but that is not compatible to numpy's memory layout.
All in all, even if you change the iterator to do a different iteration order, these things should still work as far as I can tell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I get your point. It sounds like the subspace is compatible to 1d -- I mean .ravel() can returns a reference not a copy. Is there a name in numpy for this property? ravellable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, precisely. And no, I don't know a nice word for that property, since its not contiguous....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want to give it a name to properly refer to it. I see code appears to be dealing with this in numpy.reshape too.

@rainwoodman
Copy link
Contributor Author

Linking the PR to #8264

@@ -1724,6 +1741,10 @@ mapiter_@name@(PyArrayMapIterObject *mit)
NPY_AUXDATA_FREE(transferdata);
return -1;
}
if(subspace_ptrs[0] == self_ptr &&
subspace_ptrs[1] == mit->extra_op_ptrs[0]) {
skip <<= 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why two iterations without checking closer, but can't the bit shift overflow to 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the second part of your question: shifting stops after skip == 4 -- if skip started with 1. Otherwise skip started with 0 and shifting never do anything. So it won't overflow.

The first part of your question I don't really understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skip = 1(fit the offset), 2 (twice, fit the stride), 4(the linear model is fitted, use predictions from this point on)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still don't get it. Why stride? This is purely an offset in the subspace (from the point of view of the outer iteration), the inner stride is already known? In fact, even if they were different (and skip was 1) you could save the difference and just apply it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are right. I changed it to 1 and all tests passes too. The offset from selfptr and the first iteration is always a constant, and we can calculate it on the first iteration.

Copy link
Member

@seberg seberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. It seems that defining reset_offsets (or maybe we can even think of a new name) is pretty understandable. It would be cool if we can make the comments a bit clearer, I tried to suggest/drop some words, maybe you can think of something nicer too.

@@ -1632,7 +1632,8 @@ mapiter_@name@(PyArrayMapIterObject *mit)
char *subspace_baseptrs[2];
char **subspace_ptrs = mit->subspace_ptrs;
npy_intp *subspace_strides = mit->subspace_strides;
int skip = 0;
int is_reset_trivial = 1;
npy_intp reset_offsets[2];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be initialized to avoid compile time warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it proper numpy C to initialize with {}?

if (*counter != PyArray_SIZE(mit->subspace)) {
/*
* if the subspace iterator skips, we cannot avoid resetting
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remembered the word, its "trivially iterable" (at least within much of our code). Just trying to make it a bit clearer (only some thougths): If the subspace iterator is not trivially iterable (so not in a single stride), it has to be reset to the correct start point in every outer iteration. If it is trivially iterable we can avoid using it alltogether (the actual loop does nothing).

* because the internal iteration of each external iterations,
* share the same structure, if we are correct once we know
* future iterations we are always correct.
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for extra blank line. The comment is outdated with this version of the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

reset_offsets[0] = subspace_ptrs[0] - self_ptr;
reset_offsets[1] = subspace_ptrs[1] - mit->extra_op_ptrs[0];
/* use the faster adjustment */
is_reset_trivial ++;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I wouldn't put the space, though I guess it really does not matter, nor am I sure its part of any style guideline.

subspace_ptrs[0] = self_ptr;
subspace_ptrs[1] = mit->extra_op_ptrs[0];
/*
* will avoid resetting if the reset is trival.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly could try to expand this a little along the same lines as above, especially an example might help. If the inner/subspace iterator is trivially iterable, we still may need to calculate the correct starting point of the iteration. The offset is typically zero unless reversing the iteration is more efficient (originally a negative stride) in reversed order in which case this is the offset to the last item.

@@ -497,6 +497,14 @@ def test_indexing_array_weird_strides(self):
zind = np.zeros(4, dtype=np.intp)
assert_array_equal(x2[ind, zind], x2[ind.copy(), zind])

def test_indexing_array_weird_strides_8264(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not matter, but maybe negative strides explicitly is nicer ;).

@charris charris added this to the 1.12.0 release milestone Nov 20, 2016
@seberg
Copy link
Member

seberg commented Nov 21, 2016

Thanks, looks all good. If you can squash it, I will put this in.

@rainwoodman
Copy link
Contributor Author

squashed.

@charris
Copy link
Member

charris commented Nov 21, 2016

The summary line could be improved, something like

BUG: Fix iteration over subspaces in mapiter_@name@.

Then put the Fixes #8264 at the bottom of the commit message,

@charris
Copy link
Member

charris commented Nov 21, 2016

Also more explanation of what the problem was, e.g., visible failure. Currently is all "what" and no "why".

As stated in numpy#8264, before this patch numpy crashes when the
subspace of iterator has negative strides on the faster
resetting branch for trivially iterable subspaces in mapiter_@name@.

Noticing the offset between ptr and first item in subspace is
constant, we calculate the offset from the first iteration and use
it onwards.

Fixes numpy#8264
@rainwoodman
Copy link
Contributor Author

Indeed. Poorly written. How does it look now?

On Mon, Nov 21, 2016 at 12:31 PM, Charles Harris notifications@github.com
wrote:

Also more explanation of what the problem was, e.g., visible failure.
Currently is all what and no why.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#8284 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAIbTAmuNUEZ2gf-ehxnImJ3UyvFaL0Aks5rAf-RgaJpZM4K0py5
.

@charris charris changed the title WIP: fix #8264. BUG: Fix iteration over reversed subspaces in mapiter_@name@ Nov 22, 2016
@charris
Copy link
Member

charris commented Nov 22, 2016

LGTM. I'll let @seberg finish it up.

@seberg
Copy link
Member

seberg commented Nov 22, 2016

LGTM, thanks a lot @rainwoodman.

@seberg seberg merged commit ce6d3ff into numpy:master Nov 22, 2016
@charris charris removed this from the 1.12.0 release milestone Nov 22, 2016
@rainwoodman
Copy link
Contributor Author

my pleasure!

@rainwoodman rainwoodman deleted the fix-8264 branch November 22, 2016 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants