Skip to content

BUG: ndarray.conjugate broken for custom dtypes (unlike np.conjugate) #9003

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

Closed
wants to merge 2 commits into from

Conversation

longjon
Copy link
Contributor

@longjon longjon commented Apr 27, 2017

In #4730, @andyjost noted that ndarray.conjugate was broken for arrays of objects. This was fixed in #4887 by @juliantaylor by extending the conditions guarding a no-op fall-through to exclude both complex- and object-typed arrays. Unfortunately this solution is not complete, because custom dtypes may be conjugateable without being objects. This patch simply removes the fall-through, which actually makes the behaviors of the np.conjugate ufunc and ndarray.conjugate the same as documented.

This will be slower if ndarray.conjugate is used (needlessly) on real data; but then, it's now the same speed as np.conjugate. (Is numpy exploiting the specific no-op behavior of ndarray.conjugate anywhere?)

This is especially relevant to @moble's https://github.com/moble/quaternion library, which will silently no-op when using ndarray.conjugate on non-zero-dim arrays.

@moble
Copy link
Contributor

moble commented May 2, 2017

As explained here, I can verify that this patch does fix the problem in the quaternion library. Thanks!

@eric-wieser
Copy link
Member

eric-wieser commented May 2, 2017

Note that this "breaks" the following:

  • np.array('hello world').conjugate()
  • np.array('2016', dtype='M').conjugate()
  • np.array((1, 2), dtype=[('a', int), ('b',int)]).conjugate()

It also means that x.conjugate() is x is now false for x = np.array(1), where previously this was true

@eric-wieser
Copy link
Member

To make this not break (bad) old code, I think the test should become:

PyArray_ISNUMBER(self) || PyArray_ISOBJECT(self) || PyArray_ISUSERDEF(self)

and then the else branch should have a DEPRECATE call warning that in future ndarray.conjugate will be the same as np.conjugate and error on those inputs

@moble
Copy link
Contributor

moble commented May 2, 2017

I can verify that using the original code with

    if (PyArray_ISCOMPLEX(self) || PyArray_ISOBJECT(self)) {

replaced by

    if (PyArray_ISNUMBER(self) || PyArray_ISOBJECT(self) || PyArray_ISUSERDEF(self)) {

also solves my problem.

@longjon longjon force-pushed the always-conjugate branch 2 times, most recently from 8260ac9 to 8254690 Compare May 2, 2017 20:06
@longjon
Copy link
Contributor Author

longjon commented May 2, 2017

Sounds good, I've updated the patch as suggested.

@eric-wieser
Copy link
Member

Can you split this into a BUG commit and a DEP commit? It's nice to have deprecation commits stand alone.

Also needs:

  • a test in test_deprecations.py
  • a release note entry about the deprecation
  • a release note entry about int.conjugate() no longer returning a view

But otherwise looks good

@longjon longjon force-pushed the always-conjugate branch 2 times, most recently from aa240fd to 76ab732 Compare May 3, 2017 00:23
@longjon
Copy link
Contributor Author

longjon commented May 3, 2017

Okay, I've done as you say. Let me know if you'd prefer separate TST and DOC commits instead of putting those changes in the DEP commit.

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.

All looks good to me. My only concern is whether no longer returning a view is an ok change - It'd be nice if another maintainer could weigh in.

Obviously there's a consistency benefit here, but there's also a performance cost.

@@ -100,6 +102,14 @@ invokes``ndarray.__getslice__`` (e.g. through ``super(...).__getslice__``) will
now issue a DeprecationWarning - ``.__getitem__(slice(start, end))`` should be
used instead.

``ndarray.conjugate`` no longer returns a view of real data
-----------------------------------------------------------
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick - belongs in previous commit, but I don't think that's worth your time to fix

@homu
Copy link
Contributor

homu commented May 3, 2017

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

@longjon longjon force-pushed the always-conjugate branch from 76ab732 to 3898ef2 Compare May 3, 2017 17:38
@longjon
Copy link
Contributor Author

longjon commented May 3, 2017

Rebased and moved release notes into correct commits.

Re: performance cost, I count five conjugations in this repo (outside tests) which are guarded by a complex check, and one which is not at https://github.com/numpy/numpy/blob/master/numpy/lib/function_base.py#L3087 in cov. That last one seems pretty important to me -- cov may be called on big arrays where making an extra copy would be bad news.

I could change this so that a view is still returned when PyArray_ISNUMBER && !PyArray_ISCOMPLEX, otherwise a new array is returned when PyArray_ISCOMPLEX || PyArray_ISOBJECT || PyArray_ISUSERDEF, otherwise a view is returned and we hit a deprecation warning. It'd still be a shame that np.conjugate behaves differently, but fixing that seems much deeper...

@longjon longjon force-pushed the always-conjugate branch from 3898ef2 to cd90a1b Compare May 3, 2017 18:23
@longjon
Copy link
Contributor Author

longjon commented May 3, 2017

Since I find the cov issue sufficiently worrisome, I've gone ahead and implemented the above; now the first commit simply deprecates non-numeric ndarray.conjugate, and the second extends conjugation to types with PyArray_ISUSERDEF.

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.

Very thorough patch - thanks! Just some minor style nits, and this is good to merge

@@ -1173,7 +1173,8 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o
NPY_NO_EXPORT PyObject *
PyArray_Conjugate(PyArrayObject *self, PyArrayObject *out)
{
if (PyArray_ISCOMPLEX(self) || PyArray_ISOBJECT(self)) {
if (PyArray_ISCOMPLEX(self) || PyArray_ISOBJECT(self) ||
PyArray_ISUSERDEF(self)) {
Copy link
Member

Choose a reason for hiding this comment

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

Convention would be to indent this twice, (doc\C_STYLE_GUIDE.rst.txt)

if (!PyArray_ISNUMBER(self)) {
if (DEPRECATE("attempting to conjugate non-numeric dtype; "
"this will error in the future to match the behavior "
"of np.conjugate") < 0) {
Copy link
Member

Choose a reason for hiding this comment

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

Indentation is weird here - should probably either be column aligned, or 8 spaces - currently, it's neither

@longjon longjon force-pushed the always-conjugate branch from cd90a1b to 16a27f8 Compare May 3, 2017 23:21
@longjon
Copy link
Contributor Author

longjon commented May 3, 2017

Indentation updated.

@@ -1186,6 +1186,13 @@ PyArray_Conjugate(PyArrayObject *self, PyArrayObject *out)
}
else {
PyArrayObject *ret;
if (!PyArray_ISNUMBER(self)) {
if (DEPRECATE("attempting to conjugate non-numeric dtype; this "
Copy link
Member

@eric-wieser eric-wieser May 3, 2017

Choose a reason for hiding this comment

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

One last thing - this needs a comment like NumPy 1.13, 2017-05-04 by the deprecation. Sorry for not spotting that in the last review

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment added, following the format of other such comments.

longjon added 2 commits May 3, 2017 16:57
types

This behavior is contrary to np.conjugate (which will error on
non-numeric types), even though documentation claims they are identical.
This deprecation favors failing fast.
This is especially relevant to moble's numpy quaternion library, which
will silently fail to conjugate when using ndarray.conjugate.
@longjon longjon force-pushed the always-conjugate branch from 16a27f8 to d425733 Compare May 3, 2017 23:57
@eric-wieser
Copy link
Member

eric-wieser commented May 4, 2017

Something screwy seems to be going on with github here, so I'm gonna try closing and reopening

image

(no test marked by the second commit, "view changes" button is a dead link. Also the "new changes" text won't go away)

@eric-wieser eric-wieser closed this May 4, 2017
@eric-wieser eric-wieser reopened this May 4, 2017
@eric-wieser
Copy link
Member

eric-wieser commented May 4, 2017

@charris: Any idea why travis is only testing the first commit (d425733) in this PR?

@longjon
Copy link
Contributor Author

longjon commented May 4, 2017

Travis is correctly testing the final commit (see, e.g., https://github.com/longjon/numpy/commits/d4257335fc515aff9cda64d57cd3ec72841b9720/numpy/core/src/multiarray/calculation.c). GitHub lists commits in chronological rather than topological order, because GitHub is designed around a concatenative rather than a rewriting workflow. (The other UI issues just sound broken though!)

@homu
Copy link
Contributor

homu commented May 6, 2017

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

@eric-wieser
Copy link
Member

eric-wieser commented May 6, 2017

In the interest of leaving this around as a testcase for the support thread I started with github about the UI, can I get you to make a new PR for rebasing this?

@eric-wieser
Copy link
Member

Thanks @longjon

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.

5 participants