Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix "numpy scalar * array-like == performance horror"#3501

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
charris merged 1 commit intonumpy:masterfrominducer:master
Jul 9, 2013
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletionsdoc/release/1.8.0-notes.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,6 +85,19 @@ IO compatibility with large files

Large NPZ files >2GB can be loaded on 64-bit systems.

Binary operations with non-arrays as second argument
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Binary operations of the form ``<array-or-subclass> * <non-array-subclass>``
where ``<non-array-subclass>`` declares an ``__array_priority__`` higher than
that of ``<array-or-subclass>`` will now unconditionally return
*NotImplemented*, giving ``<non-array-subclass>`` a chance to handle the
operation. Previously, `NotImplemented` would only be returned if
``<non-array-subclass>`` actually implemented the reversed operation, and after
a (potentially expensive) array conversion of ``<non-array-subclass>`` had been
attempted. (`bug <https://github.com/numpy/numpy/issues/3375>`_, `pull request
<https://github.com/numpy/numpy/pull/3501>`_)


New Features
============
Expand Down
20 changes: 20 additions & 0 deletionsnumpy/core/src/multiarray/number.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -209,6 +209,26 @@ PyArray_GenericBinaryFunction(PyArrayObject *m1, PyObject *m2, PyObject *op)
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}

if (!PyArray_Check(m2)) {
/*
* Catch priority inversion and punt, but only if it's guaranteed
* that we were called through m1 and the other guy is not an array
* at all. Note that some arrays need to pass through here even
* with priorities inverted, for example: float(17) * np.matrix(...)
Copy link
Member

Choose a reason for hiding this comment

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

float(17) isn't an array so I guess there's a mistake in the comment?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Nope, that's correct.float(17) * np.matrix(...) first ends up innp.matrix.__rmul__, which callsnp.dot, which callsPyArray_MatrixProduct2, which castsfloat(17) into an array scalar an then calls this place. :)

*
* See also:
* - https://github.com/numpy/numpy/issues/3502
* - https://github.com/numpy/numpy/issues/3503
*/
double m1_prio = PyArray_GetPriority(m1, NPY_SCALAR_PRIORITY);
double m2_prio = PyArray_GetPriority(m2, NPY_SCALAR_PRIORITY);
if (m1_prio < m2_prio) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
}

return PyObject_CallFunction(op, "OO", m1, m2);
}

Expand Down
30 changes: 30 additions & 0 deletionsnumpy/core/tests/test_ufunc.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -834,5 +834,35 @@ def test_custom_ufunc(self):
dtype=rational);
assert_equal(result, expected);

def test_custom_array_like(self):
class MyThing(object):
__array_priority__ = 1000

rmul_count = 0
getitem_count = 0

def __init__(self, shape):
self.shape = shape

def __len__(self):
return self.shape[0]

def __getitem__(self, i):
MyThing.getitem_count += 1
if not isinstance(i, tuple):
i = (i,)
if len(i) > len(self.shape):
raise IndexError("boo")

return MyThing(self.shape[len(i):])

def __rmul__(self, other):
MyThing.rmul_count += 1
return self

np.float64(5)*MyThing((3, 3))
assert_(MyThing.rmul_count == 1, MyThing.rmul_count)
assert_(MyThing.getitem_count <= 2, MyThing.getitem_count)

if __name__ == "__main__":
run_module_suite()

[8]ページ先頭

©2009-2025 Movatter.jp