summaryrefslogtreecommitdiff
path: root/doc/source/reference/arrays.nditer.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/source/reference/arrays.nditer.rst')
-rw-r--r--doc/source/reference/arrays.nditer.rst181
1 files changed, 22 insertions, 159 deletions
diff --git a/doc/source/reference/arrays.nditer.rst b/doc/source/reference/arrays.nditer.rst
index 7dab09a71..2db12a408 100644
--- a/doc/source/reference/arrays.nditer.rst
+++ b/doc/source/reference/arrays.nditer.rst
@@ -1,5 +1,9 @@
.. currentmodule:: numpy
+.. for doctests
+ The last section on Cython is 'included' at the end of this file. The tests
+ for that section are disabled.
+
.. _arrays.nditer:
*********************
@@ -218,21 +222,21 @@ produce identical results to the ones in the previous section.
>>> it = np.nditer(a, flags=['f_index'])
>>> while not it.finished:
... print("%d <%d>" % (it[0], it.index), end=' ')
- ... it.iternext()
+ ... is_not_finished = it.iternext()
...
0 <0> 1 <2> 2 <4> 3 <1> 4 <3> 5 <5>
>>> it = np.nditer(a, flags=['multi_index'])
>>> while not it.finished:
... print("%d <%s>" % (it[0], it.multi_index), end=' ')
- ... it.iternext()
+ ... is_not_finished = it.iternext()
...
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)>
>>> with np.nditer(a, flags=['multi_index'], op_flags=['writeonly']) as it:
... while not it.finished:
... it[0] = it.multi_index[1] - it.multi_index[0]
- ... it.iternext()
+ ... is_not_finished = it.iternext()
...
>>> a
array([[ 0, 1, 2],
@@ -316,12 +320,13 @@ specified as an iterator flag.
... op_dtypes=['complex128']):
... print(np.sqrt(x), end=' ')
...
- 1.73205080757j 1.41421356237j 1j 0j (1+0j) (1.41421356237+0j)
+ 1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j)
>>> for x in np.nditer(a, flags=['buffered'], op_dtypes=['complex128']):
... print(np.sqrt(x), end=' ')
...
- 1.73205080757j 1.41421356237j 1j 0j (1+0j) (1.41421356237+0j)
+ 1.7320508075688772j 1.4142135623730951j 1j 0j (1+0j) (1.4142135623730951+0j)
+
The iterator uses NumPy's casting rules to determine whether a specific
conversion is permitted. By default, it enforces 'safe' casting. This means,
@@ -405,8 +410,8 @@ which includes the input shapes to help diagnose the problem.
... print("%d:%d" % (x,y), end=' ')
...
Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: operands could not be broadcast together with shapes (2) (2,3)
+ ...
+ ValueError: operands could not be broadcast together with shapes (2,) (2,3)
Iterator-Allocated Output Arrays
--------------------------------
@@ -482,9 +487,9 @@ reasons.
>>> square(np.arange(6).reshape(2,3), out=b)
Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "<stdin>", line 4, in square
- ValueError: non-broadcastable output operand with shape (3) doesn't match the broadcast shape (2,3)
+ ...
+ ValueError: non-broadcastable output operand with shape (3,) doesn't
+ match the broadcast shape (2,3)
Outer Product Iteration
-----------------------
@@ -550,7 +555,7 @@ For a simple example, consider taking the sum of all elements in an array.
>>> a = np.arange(24).reshape(2,3,4)
>>> b = np.array(0)
- >>> with np.nditer([a, b], flags=['reduce_ok', 'external_loop'],
+ >>> with np.nditer([a, b], flags=['reduce_ok'],
... op_flags=[['readonly'], ['readwrite']]) as it:
... for x,y in it:
... y[...] += x
@@ -568,7 +573,7 @@ sums along the last axis of `a`.
.. admonition:: Example
>>> a = np.arange(24).reshape(2,3,4)
- >>> it = np.nditer([a, None], flags=['reduce_ok', 'external_loop'],
+ >>> it = np.nditer([a, None], flags=['reduce_ok'],
... op_flags=[['readonly'], ['readwrite', 'allocate']],
... op_axes=[None, [0,1,-1]])
>>> with it:
@@ -602,7 +607,7 @@ buffering.
.. admonition:: Example
>>> a = np.arange(24).reshape(2,3,4)
- >>> it = np.nditer([a, None], flags=['reduce_ok', 'external_loop',
+ >>> it = np.nditer([a, None], flags=['reduce_ok',
... 'buffered', 'delay_bufalloc'],
... op_flags=[['readonly'], ['readwrite', 'allocate']],
... op_axes=[None, [0,1,-1]])
@@ -617,150 +622,8 @@ buffering.
array([[ 6, 22, 38],
[54, 70, 86]])
-Putting the Inner Loop in Cython
-================================
-
-Those who want really good performance out of their low level operations
-should strongly consider directly using the iteration API provided
-in C, but for those who are not comfortable with C or C++, Cython
-is a good middle ground with reasonable performance tradeoffs. For
-the :class:`nditer` object, this means letting the iterator take care
-of broadcasting, dtype conversion, and buffering, while giving the inner
-loop to Cython.
-
-For our example, we'll create a sum of squares function. To start,
-let's implement this function in straightforward Python. We want to
-support an 'axis' parameter similar to the numpy :func:`sum` function,
-so we will need to construct a list for the `op_axes` parameter.
-Here's how this looks.
-
-.. admonition:: Example
-
- >>> def axis_to_axeslist(axis, ndim):
- ... if axis is None:
- ... return [-1] * ndim
- ... else:
- ... if type(axis) is not tuple:
- ... axis = (axis,)
- ... axeslist = [1] * ndim
- ... for i in axis:
- ... axeslist[i] = -1
- ... ax = 0
- ... for i in range(ndim):
- ... if axeslist[i] != -1:
- ... axeslist[i] = ax
- ... ax += 1
- ... return axeslist
- ...
- >>> def sum_squares_py(arr, axis=None, out=None):
- ... axeslist = axis_to_axeslist(axis, arr.ndim)
- ... it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop',
- ... 'buffered', 'delay_bufalloc'],
- ... op_flags=[['readonly'], ['readwrite', 'allocate']],
- ... op_axes=[None, axeslist],
- ... op_dtypes=['float64', 'float64'])
- ... with it:
- ... it.operands[1][...] = 0
- ... it.reset()
- ... for x, y in it:
- ... y[...] += x*x
- ... return it.operands[1]
- ...
- >>> a = np.arange(6).reshape(2,3)
- >>> sum_squares_py(a)
- array(55.0)
- >>> sum_squares_py(a, axis=-1)
- array([ 5., 50.])
-
-To Cython-ize this function, we replace the inner loop (y[...] += x*x) with
-Cython code that's specialized for the float64 dtype. With the
-'external_loop' flag enabled, the arrays provided to the inner loop will
-always be one-dimensional, so very little checking needs to be done.
-
-Here's the listing of sum_squares.pyx::
-
- import numpy as np
- cimport numpy as np
- cimport cython
-
- def axis_to_axeslist(axis, ndim):
- if axis is None:
- return [-1] * ndim
- else:
- if type(axis) is not tuple:
- axis = (axis,)
- axeslist = [1] * ndim
- for i in axis:
- axeslist[i] = -1
- ax = 0
- for i in range(ndim):
- if axeslist[i] != -1:
- axeslist[i] = ax
- ax += 1
- return axeslist
-
- @cython.boundscheck(False)
- def sum_squares_cy(arr, axis=None, out=None):
- cdef np.ndarray[double] x
- cdef np.ndarray[double] y
- cdef int size
- cdef double value
-
- axeslist = axis_to_axeslist(axis, arr.ndim)
- it = np.nditer([arr, out], flags=['reduce_ok', 'external_loop',
- 'buffered', 'delay_bufalloc'],
- op_flags=[['readonly'], ['readwrite', 'allocate']],
- op_axes=[None, axeslist],
- op_dtypes=['float64', 'float64'])
- with it:
- it.operands[1][...] = 0
- it.reset()
- for xarr, yarr in it:
- x = xarr
- y = yarr
- size = x.shape[0]
- for i in range(size):
- value = x[i]
- y[i] = y[i] + value * value
- return it.operands[1]
-
-On this machine, building the .pyx file into a module looked like the
-following, but you may have to find some Cython tutorials to tell you
-the specifics for your system configuration.::
-
- $ cython sum_squares.pyx
- $ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -I/usr/include/python2.7 -fno-strict-aliasing -o sum_squares.so sum_squares.c
-
-Running this from the Python interpreter produces the same answers
-as our native Python/NumPy code did.
-
-.. admonition:: Example
-
- >>> from sum_squares import sum_squares_cy
- >>> a = np.arange(6).reshape(2,3)
- >>> sum_squares_cy(a)
- array(55.0)
- >>> sum_squares_cy(a, axis=-1)
- array([ 5., 50.])
-
-Doing a little timing in IPython shows that the reduced overhead and
-memory allocation of the Cython inner loop is providing a very nice
-speedup over both the straightforward Python code and an expression
-using NumPy's built-in sum function.::
-
- >>> a = np.random.rand(1000,1000)
-
- >>> timeit sum_squares_py(a, axis=-1)
- 10 loops, best of 3: 37.1 ms per loop
-
- >>> timeit np.sum(a*a, axis=-1)
- 10 loops, best of 3: 20.9 ms per loop
-
- >>> timeit sum_squares_cy(a, axis=-1)
- 100 loops, best of 3: 11.8 ms per loop
-
- >>> np.all(sum_squares_cy(a, axis=-1) == np.sum(a*a, axis=-1))
- True
+.. for doctests
+ Include Cython section separately. Those tests are skipped entirely via an
+ entry in RST_SKIPLIST
- >>> np.all(sum_squares_py(a, axis=-1) == np.sum(a*a, axis=-1))
- True
+.. include:: arrays.nditer.cython.rst