8000 ENH: Make it possible to NpyIter_RemoveAxis an empty dimension by seberg · Pull Request #3861 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Make it possible to NpyIter_RemoveAxis an empty dimension #3861

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

Merged
merged 3 commits into from
Apr 29, 2017

Conversation

seberg
Copy link
Member
@seberg seberg commented Oct 3, 2013

This is the followup to that "kludge" discussion. I did not really check much if there might be something going awefully wrong, though on the other hand it is hard to imagine something too bad.

There are certainly more kludges to remove here assuming that it works. So should check for those first.

< 8000 div data-view-component="true" class="TimelineItem pt-0">
NIT_ADVANCE_AXISDATA(axisdata, 1);
}
NIT_ITERSIZE(iter) = size;
}
Copy link
Member

Choose a reason for hiding this comment

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

Can we just use the second code path unconditionally? (With a comment noting that recomputing from scratch is safest because it's possible the removed axis was zero-sized.) No point in having multiple code paths where one will do.

@njsmith
Copy link
Member
njsmith commented Oct 3, 2013

Aside from that comment LGTM. Simplifies the code and makes it more general, nice.

@charris
Copy link
Member
charris commented Oct 15, 2013

ERROR: test_0_size (test_linalg.TestInv)


Traceback (most recent call last):

File "/home/travis/virtualenv/python2.6/lib/python2.6/site-packages/numpy/linalg/tests/test_linalg.py", line 293, in test_0_size

res = linalg.inv(a)

File "/home/travis/virtualenv/python2.6/lib/python2.6/site-packages/numpy/linalg/linalg.py", line 503, in inv

ainv = _umath_linalg.inv(a, signature=signature, extobj=extobj)

ValueError: On entry to ZGESV parameter number 4 had an illegal value

@charris
Copy link
Member
charris commented Oct 15, 2013

Ping Travis.

@charris charris closed this Oct 15, 2013
@charris charris reopened this Oct 15, 2013
@seberg
Copy link
Member Author
seberg commented Oct 15, 2013

Yeah, the test failure is real. I removed the special case, and whatever is linked locally can handle the empty operation in BLAS, but on travis it can't. So I have to plug some kind of special case(s) back in there. Not sure yet on which level.

@charris
Copy link
Member
charris commented Mar 24, 2014

@seberg Where is this PR after all the other changes in nditer?

@seberg
Copy link
Member Author
seberg commented Mar 25, 2014

It has some code overlap (gets simpler then it is currently), but feature wise it has nothing to do with the rest. Still would have to see which gufuncs support empty arrays and which don't though...

@seberg seberg force-pushed the nditer-remove-empty branch from c899f31 to 21af68e Compare August 31, 2014 19:35
@seberg
Copy link
Member Author
seberg commented Aug 31, 2014

This should be fine, but since lapack does not support empty arrays (at least mine apparently), all those functions would need to be looked at...

@charris
Copy link
Member
charris commented Jan 21, 2015

@seberg I've forgotten, if I ever knew, what this is about. Seems like a pretty small patch and if it fixes other problems I'd like it to go in.

@seberg
Copy link
Member Author
seberg commented Jan 21, 2015

Its about gufuncs not being able to handle empty arrays. unfortunatly, lapack doesn't handle, so that module needs to be carefully checked ebfore putting this in.

@seberg
Copy link
Member Author
seberg commented Jul 8, 2015

OK, I have updated this, so some of the things now accept 0-d such as determinant. Cholesky I am not sure, etc. should have a bit careful look over the test case and everything that was changed has a test case etc. (i.e. I am a bit worried about the linalg changes, less so the nditer ones).
I can stash things later, and it needs a release notes comment as well, since nditer is changed (and also linalg).

@homu
Copy link
Contributor
homu commented Jul 26, 2015

☔ The latest upstream changes (presumably #6115) made this pull request unmergeable. Please resolve the merge conflicts.

@charris
Copy link
Member
charris commented Aug 1, 2015

@seberg Could you rebase this? I'm going to branch 1.10 tomorrow, and if you think this is ready I'll put it in.

@seberg
Copy link
Member Author
seberg commented Aug 2, 2015

Probably is pretty much, but I am on vacation and do not have my compiter with me. But maybe it is better to wait for bew master anyway, in case I missed a function.

@seberg seberg force-pushed the nditer-remove-empty branch from 3e48ef9 to 8a093ab Compare September 28, 2015 13:32
@seberg
Copy link
Member Author
seberg commented Sep 28, 2015

Rebased this, but if someone has the patience to double check the linalg stuff, maybe would not hurt.

@homu
Copy link
Contributor
homu commented Jan 6, 2016

☔ The latest upstream changes (presumably #6905) made this pull request unmergeable. Please resolve the merge conflicts.

@homu
Copy link
Contributor 8000
homu commented Jan 23, 2016

☔ The latest upstream changes (presumably #7073) made this pull request unmergeable. Please resolve the merge conflicts.

@@ -1010,8 +991,6 @@ def eigvalsh(a, UPLO='L'):
_assertRankAtLeast2(a)
_assertNdSquareness(a)
t, result_t = _commonType(a)
if _isEmpty2d(a):
Copy link
Member Author

Choose a reason for hiding this comment

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

There was a bug here for complex value, since it missed the cast to real (I think in one other function as well).

Copy link
Member Author

Choose a reason for hiding this comment

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

@eric-wieser can you have a look if you have time. Since I don't know all your newer changes, and I guess it may be that the tests are not quite how they would be nice (dunno how well the rebase worked). Also this moves some of your fixups into the C-lapack wrappers.

Copy link
Member
@eric-wieser eric-wieser left a comment

Choose a reason for hiding this comment

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

Looks generally good, but a couple of comments:

  • A lot of lapack functions do support n == 0, but have an argument that we currently pass as n that we should be passing as max(n, 1) according to the docs
  • The test-suite already tests some empty arrays through the do method, so maybe the test_size_0 could be merged, if we really care (not sure we do). At any rate, I think that adding some dtype-changing tests to the main set of tests would be sensible. Perhaps for another PR though

gufunc = _umath_linalg.solve1
else:
if b.size == 0:
if (a.shape[-1] == 0 and b.shape[-2] == 0) or b.shape[-1] == 0:
a = a[:,:1].reshape(a.shape[:-1] + (1,))
Copy link
Member

Choose a reason for hiding this comment

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

This looks to me like it was wrong in the first place, and should have been a[...,:1]

LAPACK(@cblas_type@getrf)(&m, &m, (void *)src, &m, pivots, &info);
if (m != 0) {
/* Lapack (at least some versions) do not handle n == 0 */
LAPACK(@cblas_type@getrf)(&m, &m, (void *)src, &m, pivots, &info);
Copy link
Member

Choose a reason for hiding this comment

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

Which versions are we supporting? Even the oldest 3.0.0 version in lapack_lite we're using supports this, but the third m should be max(1,M) according to the documentation

Copy link
Member Author

Choose a reason for hiding this comment

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

Dunno, my guess was I had problems on my system, or, I just did not know if it was necessary or not. The max(1, m) thing may just be the reason too I suppose.

Copy link
Member Author

Choose a reason for hiding this comment

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

@eric-wieser is this the case for all of these functions? Or which one would need this kind of max handling? I will add a commit undoing it and see what happens for now, and we can still go back and forth if problems show up with some lapack or so.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, it worked here, but for none of the other cases, but I guess you expected that?

Copy link
Member Author

Choose a reason for hiding this comment

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

Though, I did not try to replace parameters with 1 instead of 0 there. Is it worth checking that out?

Copy link
Member

Choose a reason for hiding this comment

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

You'd need to look at the documentation for each lapacj function in turn, which can be found within f2c_[czds]_lapack.c

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmmm, ok, should do some time I guess, but don't have the nerve for it right now.

op += os_p;
}
op -= ob_p;
op += os_m;
Copy link
Member

Choose a reason for hiding this comment

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

indentation

}
END_OUTER_LOOP
return;
}
Copy link
Member
@eric-wieser eric-wieser Mar 26, 2017

Choose a reason for hiding this comment

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

Wouldn't a better fix be to just reassign the pointers, and run the below code?

Something like

if (dn == 0) {
     @typ@ ip_val = 0;
     @typ@ ip2_val = 0;
      ip1 = ip2 = &ip_val;
      // set strides to 0
      dn = 1
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice hack, didn't think about it much.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, no, the char *ip1=args[0], *ip2=args[1], *op=args[2]; line is important where it is, since the macro will manipulate args

@eric-wieser
Copy link
Member
eric-wieser commented Mar 26, 2017

though there are probably nicer ways to fix it).

Yeah, I would have thought you want the loop over n innermost, and then your problems would go away

Would not be smart to use it anyway

Never mind then.

@eric-wieser
Copy link
Member
eric-wieser commented Mar 26, 2017

Yeah, with the innermost loop over n, this just falls out:

BEGIN_OUTER_LOOP_3
    char *ip1=args[0], *ip2=args[1], *op=args[2];
    for (m = 0; m < dm; m++) {
        for (p = 0; p < dp; p++) {
            @typ@ op_val = 0;
            for (n = 0; n < dn; n++) {
                @typ@ val1 = (*(@typ@ *)ip1);
               
B37A
 @typ@ val2 = (*(@typ@ *)ip2);
                op_val += val1 * val2;

                ip1 += is1_n;
                ip2 += is2_n;
            }
            *(@typ@ *)op = op_val;

            ip1 -= ib1_n;
            ip2 -= ib2_n;
            ip2 += is2_p;
            op  +=  os_p;
        }

        ip2 -= ib2_p;
        op  -=  ob_p;
        ip1 += is1_m;
        op  +=  os_m;
    }
END_OUTER_LOOP

Perhaps this has performance/caching implications - I guess the old code iterated over p in the inner loop, which is the last of the input and output dimensions, so likely to be contiguous.

I guess an optimal implementation would look at the strides and do a different loop in each case. For instance, my implementation would be more efficient at computing X @ X.T

@seberg seberg force-pushed the nditer-remove-empty branch from 443ba37 to 6f75eb3 Compare March 26, 2017 14:15
@homu
Copy link
Contributor
homu commented Mar 27, 2017

☔ The latest upstream changes (presumably #8847) made this pull request unmergeable. Please resolve the merge conflicts.

@seberg seberg force-pushed the nditer-remove-empty branch from 6f75eb3 to fe3e16c Compare April 7, 2017 15:05
@eric-wieser
Copy link
Member

This didn't previously segfault, did it? Bad rebase?

@eric-wieser
Copy link
Member
eric-wieser commented Apr 22, 2017

One failure is in this test

Fatal Python error: GC object already tracked
Current thread 0x00007fb6e0f0e700 (most recent call first):
  File "/home/travis/build/numpy/numpy/venv-for-wheel/lib/python3.5/site-packages/numpy/core/tests/test_ufunc.py", line 694 in test_object_scalar_multiply

@@ -120,6 +140,8 @@ behaviour is recovered if ``axis=None`` (default).
``np.gradient`` now supports unevenly spaced data
------------------------------------------------
Users can now specify a not-constant spacing for data.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Users can now specify a not-constant spacing for data.
Copy link
Member

Choose a reason for hiding this comment

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

Bad merge

@@ -131,11 +153,13 @@ In particular ``np.gradient`` can now take:

This means that, e.g., it is now possible to do the following::

This means that, e.g., it is now possible to do the following:
Copy link
Member

Choose a reason for hiding this comment

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

Bad merge

@@ -227,6 +251,8 @@ More ``linalg`` operations now accept empty vectors and matrices
All of the following functions in ``np.linalg`` now work when given input
arrays with a 0 in the last two dimensions: ``det``, ``slogdet``, ``pinv``,
``eigvals``, ``eigvalsh``, ``eig``, ``eigh``.
arrays with a 0 in the last two dimensions: `det``, ``slogdet``, ``pinv``,
``eigvals``, ``eigvalsh``, ``eig``, ``eigh``, ``cholesky``.
Copy link
Member

Choose a reason for hiding this comment

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

Bad merge

Copy link
Member Author

Choose a reason for hiding this comment

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

Ooops, I am sorry, I am a bit overwhelmed with other things right now, not sure I will be checking on things regularly for a while :/.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, that should have fixed it up, when going back to the previous version of setting to zero, I got the pointer arithmetic wrong apparently.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok, now for real... ;)

@homu
Copy link
Contributor
homu commented Apr 26, 2017

☔ The latest upstream changes (presumably #8999) made this pull request unmergeable. Please resolve the merge conflicts.

The caller should take of not using pointers given in such a case
@seberg seberg force-pushed the nditer-remove-empty branch 2 times, most recently from 99ce747 to ca8b952 Compare April 29, 2017 16:30
seberg and others added 2 commits April 29, 2017 18:37
The necessary fixup on the C-side of linalg has been done already
(i.e. the gufuncs correctly work for these empty arrays).
This also enables cholesky decomposition and fixes a small bug in pinv
handling.

Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
The case where the result is not empty was missing the zeroing out
now that it is legal to use that input.
@seberg seberg force-pushed the nditer-remove-empty branch from ca8b952 to 1c4e0d4 Compare April 29, 2017 16:37
@eric-wieser
Copy link
Member

Nice job! Based on the silence on the waiting list, this looks pretty safe to merge?

@eric-wieser eric-wieser added this to the 1.13.0 release milestone Apr 29, 2017
@charris
Copy link
Member
charris commented Apr 29, 2017

I don't have a problem with this getting merged. Support for empty arrays is not complete and this is another step on the way,

@seberg
Copy link
Member Author
seberg commented Apr 29, 2017

OK; lets give it a shot.

@seberg seberg merged commit 8d01ee4 into numpy:master Apr 29, 2017
@seberg seberg deleted the nditer-remove-empty branch April 29, 2017 20:53
@eric-wieser
Copy link
Member
eric-wieser commented Apr 29, 2017

On the subject of empty array kludges - what is going on here? I can't see how the iteration can be empty, yet the results not be

@eric-wieser
Copy link
Member

Apparently not a single one of our tests hits that code

@seberg
Copy link
Member Author
seberg commented Apr 29, 2017

Sounds like it was planned to catch those empty array matrix multiplies for us, but the if is incorrect. Since this is not really a reduction.... I would tend to leave it as is after this now (i.e. let the inner loop worry about it) and remove the code block (assuming, that we are not missing something really silly).

@eric-wieser
Copy link
Member
eric-wieser commented Apr 29, 2017

Doesn't look like it's checking for the right thing - that's looking at the ... in (..., m, n) -> (..., n, o), right? So if size(...) == 0, the output is always empty?

@seberg
Copy link
Member Author
seberg commented Apr 30, 2017

Yeah.

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.

6 participants
0