Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added inplace_increment function #326

Closed
wants to merge 27 commits into from

7 participants

John Salvatier Nathaniel J. Smith Travis E. Oliphant Charles Harris Don't Add Me To Your Organization a.k.a The Travis Bot Bradley M. Froehle Henry Gomersall
John Salvatier

I've added an inplace_increment function which allows you to increment an array inplace using advanced indexing. The key feature is that repeated indexes will repeatedly increment the array whereas

a[idx] +=1

will not do this. More discussion here and here.

I've included, documentation, a couple of tests and supported multiple datatypes.

I'm new to doing Python functions in C so a good once over of the C code is a good idea. I'm also new to submitting code for numpy, so I've probably made some mistakes somewhere.

Nathaniel J. Smith
Owner

I have mixed feelings about this. The functionality is often-requested and clearly useful, but the implementation has a number of problems. The basic features numpy provides are indexing and generic operations over generic types. This code uses numpy's indexing, but ignores the generic operation and type support, so it only supports one operation (addition) and a fixed set of types. All that would be fine if it were just implementation limitations that could be fixed later, but the API is not flexible enough to support other operations -- a "proper" version of this functionality would probably be a method on ufuncs, not a standalone function. So the API is kind of a dead-end as well.

I don't want to make the perfect the enemy of the good, but on balance I think there's enough wrong with this that I'm -0.5 on merging it and making it a permanent part of the numpy API. The best compromise might be to extract the core function and release it as a tiny standalone library? It looks like it only uses public numpy API functions, so it should be easy to do. That way people who want the feature have an easy way to get it until numpy grows a cleaner solution.

John Salvatier

I'd be in favor of a stand alone library (it was my original intention to put this into a separate package) except that the MapIter interface is unfortunately not public; mapping.c has a comment that explains:

/*

  • The mapiter object must be created new each time. It does not work
  • to bind to a new array, and continue. *
  • This was the orginal intention, but currently that does not work.
  • Do not expose the MapIter_Type to Python. *
  • It's not very useful anyway, since mapiter(indexobj); mapiter.bind(a);
  • mapiter is equivalent to a[indexobj].flat but the latter gets to use
  • slice syntax. */

If MapIter could be exposed, then I'd be happy to make this a function in a separate package. I don't know whether it would be difficult to fix the reuse issue. I think Mark Wiebe was the person who implemented MapIter.

I'll look into making this a function of binary ufuncs like reduce, but I suspect that's still above my skill level.

John Salvatier

Actually looking at the implementation of PyUFunc_ReduceWrapper, it doesn't look like it would be too difficult to do something similar for this. However, I'm having a hard time picturing how the added generality would be used. Can you think of some useful cases where this would be used on ufuncs besides add? I can imagine it being useful on prod and pow, but I can't think of any concrete uses.

Nathaniel J. Smith
Owner

I don't have any concrete cases in mind for using this with prod and pow, but I bet someone will show up with one sooner or later... automatically supporting all numpy dtypes is the more useful part, I think.

If MapIter isn't exposed then that'll be a hurdle, though, because ufuncs (which live in the "umath" library) don't have access to the "multiarray" library internal functions. @mwiebe, is there any reasonable way to do ndarray indexing via the public C API?

Travis E. Oliphant
Owner

While in an ideal world, perhaps this functionality would be on ufuncs. However, that is actually quite a bit of work. The fancy-indexing feature is just not exposed to the ufunc machinery. Thus, I think that currently this patch does a pretty good job as is. I don't think it would be too difficult to add a verison for mul and pow using a similar API at some point.

numpy/core/bscript
@@ -428,6 +428,7 @@ def pre_build(context):
"src/multiarray/arraytypes.c.src",
"src/multiarray/nditer_templ.c.src",
"src/multiarray/lowlevel_strided_loops.c.src",
+ "src/multiarray/mapping.c.src",
Travis E. Oliphant Owner

Indentation problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/core/setup.py
@@ -688,6 +688,7 @@ def generate_multiarray_templated_sources(ext, build_dir):
join(local_dir, subpath, 'arraytypes.c.src'),
join(local_dir, subpath, 'nditer_templ.c.src'),
join(local_dir, subpath, 'lowlevel_strided_loops.c.src'),
+ join(local_dir, subpath, 'mapping.c.src'),
Travis E. Oliphant Owner

Indentation problem again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/core/src/multiarray/mapping.c.src
((135 lines not shown))
+ if (type_number == type_numbers[i])
+ {
+ add_inplace = addition_funcs[i];
+ break;
+ }
+ i++ ;
+ }
+
+ if (add_inplace == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, "unsupported type for a");
+ return NULL;
+ }
+
+
+ //body
Travis E. Oliphant Owner

Do we support // -style comments in C in NumPy. Last I knew we did not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/core/src/multiarray/mapping.c.src
((138 lines not shown))
+ break;
+ }
+ i++ ;
+ }
+
+ if (add_inplace == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, "unsupported type for a");
+ return NULL;
+ }
+
+
+ //body
+ PyArrayMapIterObject * mit = (PyArrayMapIterObject *) PyArray_MapIterNew(index, 0, 1);
+ if (mit == NULL)
+ {
Travis E. Oliphant Owner

Indentation issues. Please put the if aligned with the previous line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/core/src/multiarray/mapping.c.src
((152 lines not shown))
+ if (mit == NULL)
+ {
+ goto fail;
+ }
+ PyArray_MapIterBind(mit, a);
+ if (map_increment(mit, inc, add_inplace) != 0)
+ {
+ goto fail;
+ }
+
+ //endbody
+
+ Py_DECREF(mit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
Travis E. Oliphant Owner

Align this line with the previous one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
John Salvatier

I think I've fixed those formatting issues.

numpy/core/src/multiarray/mapping.c.src
((114 lines not shown))
+
+ if (PyArray_NDIM(a) == 0) {
+ PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed.");
+ return NULL;
+ }
+
+ if (!PyArray_ISWRITEABLE(a)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "array is not writeable");
+ return NULL;
+ }
+
+ inplace_binop add_inplace = NULL;
+ int type_number = PyArray_TYPE(a);
+ int i =0;
+ while (type_numbers[i] >= 0 && addition_funcs[i] != NULL)
Charles Harris Owner
charris added a note

wrong curly braces style for while and if.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Charles Harris
Owner

Not quite, there are still lots of style issues. There is a C style guide in doc/C_STYLE_GUIDE.rst.txt. Also check that there are no hard tabs anywhere, some of the indents still look odd.

numpy/core/src/multiarray/mapping.c.src
@@ -2024,6 +2024,169 @@
}
+typedef void (*inplace_binop)(char *, char *);
+
+
+//is something like this already defined somewhere? where?
+/**begin repeat
+* #type = npy_int8, npy_int16, npy_int32, npy_int64, npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_float16, npy_float32, npy_float64#
+* #typen = NPY_INT8, NPY_INT16, NPY_INT32, NPY_INT64, NPY_UINT8, NPY_UINT16, NPY_UINT32, NPY_UINT64, NPY_FLOAT16, NPY_FLOAT32, NPY_FLOAT64#

Isn't this missing a few types? What happens for bit lengths bigger than 64? (forgive me if this should be apparent!)

I just chose the ones that I had seen used. How large should this go up to?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/core/src/multiarray/mapping.c.src
((5 lines not shown))
+
+
+//is something like this already defined somewhere? where?
+/**begin repeat
+* #type = npy_int8, npy_int16, npy_int32, npy_int64, npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_float16, npy_float32, npy_float64#
+* #typen = NPY_INT8, NPY_INT16, NPY_INT32, NPY_INT64, NPY_UINT8, NPY_UINT16, NPY_UINT32, NPY_UINT64, NPY_FLOAT16, NPY_FLOAT32, NPY_FLOAT64#
+*/
+static void @type@_inplace_add(char * l_op, char* r_op)
+{
+ ((@type@*)l_op)[0] = ((@type@*)l_op)[0] + ((@type@ *)r_op)[0];
+}
+/**end repeat**/
+
+/**begin repeat
+* #type = npy_complex64, npy_complex128#
+* #typen = NPY_COMPLEX64, NPY_COMPLEX128#

As for the reals above, what happens for the longer complex types?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request fails (merged 16603b5 into f2f0ac0).

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request fails (merged 6a80aba into f2f0ac0).

John Salvatier

I read through the C style document and went through and tried to make this patch conform.

I've also added support for all int,uint, float, complex datatype bit-width sizes that are listed at the bottom of here: http://docs.scipy.org/doc/numpy/reference/c-api.dtype.html

numpy/core/src/multiarray/mapping.c.src
((118 lines not shown))
+
+static PyObject *
+inplace_increment(PyObject *dummy, PyObject *args)
+{
+ PyObject *arg_a = NULL, *index=NULL, *inc=NULL;
+ PyArrayObject *a;
+ inplace_binop add_inplace = NULL;
+ int type_number = -1;
+ int i =0;
+ PyArrayMapIterObject * mit;
+
+ if (!PyArg_ParseTuple(args, "OOO", &arg_a, &index,
+ &inc)) {
+ return NULL;
+ }
+ if (arg_a->ob_type != &PyArray_Type){

I think you meant to write if (!PyArray_Check(arg_a)) {.

Thanks, I was looking for that function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/core/src/multiarray/mapping.c.src
@@ -2028,6 +2028,189 @@
}
+typedef void (*inplace_binop)(char *, char *);
+
+
+/*is something like this already defined somewhere? where?*/

Doubtful, because calling a function just to add two integers or floats together would be exceptionally slow. Instead I would expect that the function you generate here would perform the entire loop...

Edit: I now see PyArray_GetMap and PyArray_SetMap do something similar for the constructions a[b] and a[b] = c, but I must say I'm a bit surprised.

I hadn't understood why this was true before, but now I do. I've changed it the specialized functions to do the entire loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
numpy/core/src/multiarray/mapping.c.src
((128 lines not shown))
+
+ if (!PyArg_ParseTuple(args, "OOO", &arg_a, &index,
+ &inc)) {
+ return NULL;
+ }
+ if (arg_a->ob_type != &PyArray_Type){
+ PyErr_SetString(PyExc_ValueError, "needs an ndarray as first argument");
+ return NULL;
+ }
+ a = (PyArrayObject *) arg_a;
+
+ if (!PyArray_ISWRITEABLE(a)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "array is not writeable");
+ return NULL;
+ }

I think you meant:

if (PyArray_FailUnlessWriteable(a, "input/output array") < 0) {
    return NULL;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request fails (merged 73b36f4 into f2f0ac0).

Nathaniel J. Smith
Owner

It's great that you guys are interested in this, but I'm worried that you're spending a lot of time in ways that aren't really getting the patch closer to merging. The main obstacle is still the points that I raised. IMHO the best way forward would be to work out how to expose an API for doing indexing:
http://mail.scipy.org/pipermail/numpy-discussion/2012-July/063411.html

Of course there may be other options as well, but this approach doesn't seem too difficult and AFAICT everyone seems to like it.

John Salvatier
Travis E. Oliphant
Owner
John Salvatier
Nathaniel J. Smith
Owner

There's a script that runs over the source and generates the actual API proper. To mark a function to be exported, you just put a magic comment that says NUMPY_API in front of it. If you grep the source you'll find some examples to follow. Then you should also add it to the list in numpy/core/code_generators/numpy_api.py.

I'm not familiar enough with the details of the MapIterObject interface to comment off-the-cuff on whether it can be exposed as is. If no-one else speaks up, though, then you can always make a first hackish attempt and post it for review. (Note that Travis did have a suggestion about how the interface should look in that comment I posted up above, though.) I think the solution to the resetting problem is to just not expose any functionality that would let people run into the problem, though I don't know what the problem is in detail so I might be missing something.

John Salvatier

I've made a separate pull request, for njsmith's recommendation to expose the MapIter API (#375).

John Salvatier

I've made a cleaned up pull request (#377).

John Salvatier jsalvatier referenced this pull request in Theano/Theano
Closed

Full advanced Indexing support + gradient #1083

John Salvatier

We decided not to move ahead with this pull request and instead just added API support: #377

John Salvatier jsalvatier closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 25, 2012
  1. John Salvatier
  2. John Salvatier

    made index_increment return the array; if the input wasn't the right …

    jsalvatier authored
    …kind, it will copy, otherwise it will just do it inplace
Commits on Jun 26, 2012
  1. John Salvatier
  2. John Salvatier
  3. John Salvatier

    improved error handling

    jsalvatier authored
Commits on Jun 27, 2012
  1. John Salvatier
  2. John Salvatier
  3. John Salvatier

    added initial doc

    jsalvatier authored
  4. John Salvatier
  5. John Salvatier
  6. John Salvatier

    updated examples

    jsalvatier authored
  7. John Salvatier

    supported complex types

    jsalvatier authored
  8. John Salvatier

    added initial tests

    jsalvatier authored
  9. John Salvatier

    added more tests

    jsalvatier authored
  10. John Salvatier

    fixed tests mostly

    jsalvatier authored
  11. John Salvatier

    fixed boolean test

    jsalvatier authored
Commits on Jul 6, 2012
  1. John Salvatier

    added another example

    jsalvatier authored
Commits on Jul 18, 2012
  1. John Salvatier
Commits on Jul 23, 2012
  1. John Salvatier

    fixed style issues

    jsalvatier authored
  2. John Salvatier
  3. John Salvatier
  4. John Salvatier

    Revert "added more types and refactored typing generation; it seems k…

    jsalvatier authored
    …ind of complex now..."
    
    This reverts commit 684ac6a.
  5. John Salvatier

    forgot ;

    jsalvatier authored
  6. John Salvatier

    added other datatypes

    jsalvatier authored
  7. John Salvatier

    added check for non-array

    jsalvatier authored
Commits on Jul 25, 2012
  1. John Salvatier
Commits on Nov 6, 2012
  1. John Salvatier
This page is out of date. Refresh to see the latest.
53 numpy/add_newdocs.py
View
@@ -2231,6 +2231,59 @@ def luf(lamdaexpr, *args, **kwargs):
30
""")
+
+add_newdoc('numpy.core.multiarray', 'inplace_increment',
+ """
+ inplace_increment(a, index, inc)
+
+ Increments `a` inplace by `inc` in the locations specified by the index tuple
+ `index`. Supports advanced indexing. Repeated indexes are repeatedly
+ incremented.
+
+ Parameters
+ ----------
+ a : ndarray
+ Array to increment.
+ index : tuple of indexes
+ Supports advanced indexing.
+ inc : array_like
+ How much to increment `a`.
+
+ Returns
+ -------
+ output : None
+
+ Examples
+ --------
+ >>> a = arange(12).reshape((3,4)).astype(float64)
+ >>> index = ([1,1,2,0], [0,0,2,3])
+ >>> vals = [50,50, 30,16]
+ >>> a
+ array([[ 0., 1., 2., 3.],
+ [ 4., 5., 6., 7.],
+ [ 8., 9., 10., 11.]])
+ >>> inplace_increment(a, index, vals)
+ >>> a
+ array([[ 0., 1., 2., 19.],
+ [ 104., 5., 6., 7.],
+ [ 8., 9., 40., 11.]])
+
+ >>> x = zeros((3,3))
+ >>> x
+ array([[ 0., 0., 0.],
+ [ 0., 0., 0.],
+ [ 0., 0., 0.]])
+ >>> y = arange(3*3).reshape((3,3))
+ >>> y
+ array([[0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8]])
+ >>> inplace_increment(x, [0,1,1], y)
+ >>> x
+ array([[ 0., 1., 2.],
+ [ 9., 11., 13.],
+ [ 0., 0., 0.]])
+ """)
##############################################################################
3  numpy/core/bscript
View
@@ -427,6 +427,7 @@ def pre_build(context):
"src/multiarray/arraytypes.c.src",
"src/multiarray/nditer_templ.c.src",
"src/multiarray/lowlevel_strided_loops.c.src",
+ "src/multiarray/mapping.c.src",
"src/multiarray/einsum.c.src"]
bld(target="multiarray_templates", source=multiarray_templates)
if ENABLE_SEPARATE_COMPILATION:
@@ -449,7 +450,7 @@ def pre_build(context):
pjoin('src', 'multiarray', 'item_selection.c'),
pjoin('src', 'multiarray', 'iterators.c'),
pjoin('src', 'multiarray', 'lowlevel_strided_loops.c.src'),
- pjoin('src', 'multiarray', 'mapping.c'),
+ pjoin('src', 'multiarray', 'mapping.c.src'),
pjoin('src', 'multiarray', 'methods.c'),
pjoin('src', 'multiarray', 'multiarraymodule.c'),
pjoin('src', 'multiarray', 'nditer_pywrap.c'),
3  numpy/core/numeric.py
View
@@ -10,7 +10,7 @@
'alterdot', 'restoredot', 'roll', 'rollaxis', 'cross', 'tensordot',
'array2string', 'get_printoptions', 'set_printoptions',
'array_repr', 'array_str', 'set_string_function',
- 'little_endian', 'require',
+ 'little_endian', 'require', 'inplace_increment',
'fromiter', 'array_equal', 'array_equiv',
'indices', 'fromfunction', 'isclose',
'load', 'loads', 'isscalar', 'binary_repr', 'base_repr',
@@ -252,6 +252,7 @@ def extend_all(module):
compare_chararrays = multiarray.compare_chararrays
putmask = multiarray.putmask
einsum = multiarray.einsum
+inplace_increment = multiarray.inplace_increment
def asarray(a, dtype=None, order=None):
"""
3  numpy/core/setup.py
View
@@ -691,6 +691,7 @@ def generate_multiarray_templated_sources(ext, build_dir):
join(local_dir, subpath, 'arraytypes.c.src'),
join(local_dir, subpath, 'nditer_templ.c.src'),
join(local_dir, subpath, 'lowlevel_strided_loops.c.src'),
+ join(local_dir, subpath, 'mapping.c.src'),
join(local_dir, subpath, 'einsum.c.src')]
# numpy.distutils generate .c from .c.src in weird directories, we have
@@ -776,7 +777,7 @@ def generate_multiarray_templated_sources(ext, build_dir):
join('src', 'multiarray', 'item_selection.c'),
join('src', 'multiarray', 'iterators.c'),
join('src', 'multiarray', 'lowlevel_strided_loops.c.src'),
- join('src', 'multiarray', 'mapping.c'),
+ join('src', 'multiarray', 'mapping.c.src'),
join('src', 'multiarray', 'methods.c'),
join('src', 'multiarray', 'multiarraymodule.c'),
join('src', 'multiarray', 'nditer_templ.c.src'),
187 numpy/core/src/multiarray/mapping.c → numpy/core/src/multiarray/mapping.c.src
View
@@ -2056,6 +2056,193 @@ PyArray_MapIterArray(PyArrayObject * a, PyObject * index)
#undef SOBJ_TOOMANY
#undef SOBJ_LISTTUP
+typedef void (*inplace_map_binop)(PyArrayMapIterObject *, PyArrayIterObject *);
+
+/**begin repeat
+* #type = npy_int8, npy_int16, npy_int32, npy_int64, npy_int128, npy_int256,
+ npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_uint128, npy_uint256,
+ npy_float16, npy_float32, npy_float64, npy_float80, npy_float96, npy_float128, npy_float256#
+
+* #typen = NPY_INT8, NPY_INT16, NPY_INT32, NPY_INT64, NPY_INT128, NPY_INT256,
+ NPY_UINT8, NPY_UINT16, NPY_UINT32, NPY_UINT64, NPY_UINT128, NPY_UINT256,
+ NPY_FLOAT16, NPY_FLOAT32, NPY_FLOAT64, NPY_FLOAT80, NPY_FLOAT96, NPY_FLOAT128, NPY_FLOAT256#
+*/
+#if defined(@typen@)
+static void @type@_inplace_add(PyArrayMapIterObject *mit, PyArrayIterObject *it)
+{
+ int index = mit->size;
+ while (index--) {
+ ((@type@*)mit->dataptr)[0] = ((@type@*)mit->dataptr)[0] + ((@type@ *)it->dataptr)[0];
+
+ PyArray_MapIterNext(mit);
+ PyArray_ITER_NEXT(it);
+ }
+}
+#endif
+/**end repeat**/
+
+/**begin repeat
+* #type = npy_complex32, npy_complex64, npy_complex128, npy_complex160, npy_complex192, npy_complex512#
+* #typen = NPY_COMPLEX32, NPY_COMPLEX64, NPY_COMPLEX128, NPY_COMPLEX160, NPY_COMPLEX192, NPY_COMPLEX512#
+*/
+#if defined(@typen@)
+static void @type@_inplace_add(PyArrayMapIterObject *mit, PyArrayIterObject *it)
+{
+ int index = mit->size;
+ while (index--) {
+ ((@type@*)mit->dataptr)[0].real = ((@type@*)mit->dataptr)[0].real + ((@type@ *)it->dataptr)[0].real;
+ ((@type@*)mit->dataptr)[0].imag = ((@type@*)mit->dataptr)[0].imag + ((@type@ *)it->dataptr)[0].imag;
+
+ PyArray_MapIterNext(mit);
+ PyArray_ITER_NEXT(it);
+ }
+}
+#endif
+/**end repeat**/
+
+inplace_map_binop addition_funcs[] = {
+/**begin repeat
+* #type = npy_int8, npy_int16, npy_int32, npy_int64, npy_int128, npy_int256,
+ npy_uint8, npy_uint16, npy_uint32, npy_uint64, npy_uint128, npy_uint256,
+ npy_float16, npy_float32, npy_float64, npy_float80, npy_float96, npy_float128, npy_float256,
+ npy_complex32, npy_complex64, npy_complex128, npy_complex160, npy_complex192, npy_complex512#
+
+* #typen = NPY_INT8, NPY_INT16, NPY_INT32, NPY_INT64, NPY_INT128, NPY_INT256,
+ NPY_UINT8, NPY_UINT16, NPY_UINT32, NPY_UINT64, NPY_UINT128, NPY_UINT256,
+ NPY_FLOAT16, NPY_FLOAT32, NPY_FLOAT64, NPY_FLOAT80, NPY_FLOAT96, NPY_FLOAT128, NPY_FLOAT256,
+ NPY_COMPLEX32, NPY_COMPLEX64, NPY_COMPLEX128, NPY_COMPLEX160, NPY_COMPLEX192, NPY_COMPLEX512#
+*/
+#if defined(@typen@)
+@type@_inplace_add,
+#endif
+/**end repeat**/
+NULL};
+
+int type_numbers[] = {
+/**begin repeat
+* #typen = NPY_INT8, NPY_INT16, NPY_INT32, NPY_INT64, NPY_INT128, NPY_INT256,
+ NPY_UINT8, NPY_UINT16, NPY_UINT32, NPY_UINT64, NPY_UINT128, NPY_UINT256,
+ NPY_FLOAT16, NPY_FLOAT32, NPY_FLOAT64, NPY_FLOAT80, NPY_FLOAT96, NPY_FLOAT128, NPY_FLOAT256,
+ NPY_COMPLEX32, NPY_COMPLEX64, NPY_COMPLEX128, NPY_COMPLEX160, NPY_COMPLEX192, NPY_COMPLEX512#
+*/
+#if defined(@typen@)
+@typen@,
+#endif
+/**end repeat**/
+-1000};
+
+static int
+map_increment(PyArrayMapIterObject *mit, PyObject *op, inplace_map_binop add_inplace)
+{
+ PyArrayObject *arr = NULL;
+ PyArrayIterObject *it;
+ PyArray_Descr *descr;
+
+ if (mit->ait == NULL) {
+ return -1;
+ }
+ descr = PyArray_DESCR(mit->ait->ao);
+ Py_INCREF(descr);
+ arr = (PyArrayObject *)PyArray_FromAny(op, descr,
+ 0, 0, NPY_ARRAY_FORCECAST, NULL);
+ if (arr == NULL) {
+ return -1;
+ }
+
+ if ((mit->subspace != NULL) && (mit->consec)) {
+ if (mit->iteraxes[0] > 0) {
+ _swap_axes(mit, (PyArrayObject **)&arr, 0);
+ if (arr == NULL) {
+ return -1;
+ }
+ }
+ }
+
+ if ((it = (PyArrayIterObject *)\
+ PyArray_BroadcastToShape(arr, mit->dimensions, mit->nd)) == NULL) {
+ Py_DECREF(arr);
+
+ return -1;
+ }
+
+
+
+ PyArray_MapIterReset(mit);
+
+ (*add_inplace)(mit, it);
+
+ Py_DECREF(arr);
+ Py_DECREF(it);
+ return 0;
+}
+
+
+static PyObject *
+inplace_increment(PyObject *dummy, PyObject *args)
+{
+ PyObject *arg_a = NULL, *index=NULL, *inc=NULL;
+ PyArrayObject *a;
+ inplace_map_binop add_inplace = NULL;
+ int type_number = -1;
+ int i =0;
+ PyArrayMapIterObject * mit;
+
+ if (!PyArg_ParseTuple(args, "OOO", &arg_a, &index,
+ &inc)) {
+ return NULL;
+ }
+ if (!PyArray_Check(arg_a)) {
+ PyErr_SetString(PyExc_ValueError, "needs an ndarray as first argument");
+ return NULL;
+ }
+ a = (PyArrayObject *) arg_a;
+
+ if (PyArray_FailUnlessWriteable(a, "input/output array") < 0) {
+ return NULL;
+ }
+
+ if (PyArray_NDIM(a) == 0) {
+ PyErr_SetString(PyExc_IndexError, "0-d arrays can't be indexed.");
+ return NULL;
+ }
+ type_number = PyArray_TYPE(a);
+
+
+
+
+ while (type_numbers[i] >= 0 && addition_funcs[i] != NULL){
+ if (type_number == type_numbers[i]) {
+ add_inplace = addition_funcs[i];
+ break;
+ }
+ i++ ;
+ }
+
+ if (add_inplace == NULL) {
+ PyErr_SetString(PyExc_TypeError, "unsupported type for a");
+ return NULL;
+ }
+
+ mit = (PyArrayMapIterObject *) PyArray_MapIterNew(index, 0, 1);
+ if (mit == NULL) {
+ goto fail;
+ }
+ PyArray_MapIterBind(mit, a);
+ if (map_increment(mit, inc, add_inplace) != 0) {
+ goto fail;
+ }
+
+ Py_DECREF(mit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+
+fail:
+ Py_XDECREF(mit);
+
+ return NULL;
+}
+
static void
arraymapiter_dealloc(PyArrayMapIterObject *mit)
{
3  numpy/core/src/multiarray/mapping.h
View
@@ -59,4 +59,7 @@ PyArray_MapIterBind(PyArrayMapIterObject *, PyArrayObject *);
NPY_NO_EXPORT PyObject*
PyArray_MapIterNew(PyObject *, int, int);
+static PyObject *
+inplace_increment(PyObject *dummy, PyObject *args);
+
#endif
3  numpy/core/src/multiarray/multiarraymodule.c
View
@@ -3677,6 +3677,9 @@ static struct PyMethodDef array_module_methods[] = {
{"test_interrupt",
(PyCFunction)test_interrupt,
METH_VARARGS, NULL},
+ {"inplace_increment",
+ (PyCFunction)inplace_increment,
+ METH_VARARGS, NULL},
{NULL, NULL, 0, NULL} /* sentinel */
};
49 numpy/core/tests/test_inplace_increment.py
View
@@ -0,0 +1,49 @@
+import numpy as np
+from numpy.testing import *
+from numpy.testing.utils import WarningManager
+import warnings
+
+
+class TestInplaceIncrement(TestCase):
+ def test_dtypes(self):
+ def testdt(dt):
+ a = np.arange(12).reshape((3,4)).astype(dt)
+ index = ([1,1,2,0], [0,0,2,3])
+ inc = [50,50, 30,16]
+
+ np.inplace_increment(a, index, inc)
+
+ assert_array_almost_equal(a, np.array([[ 0, 1, 2, 19],
+ [ 104, 5, 6, 7],
+ [ 8, 9, 40, 11]]).astype(dt))
+
+ testdt(np.float)
+ testdt(np.int)
+ testdt(np.complex)
+
+ def test_slice(self):
+ a = np.arange(12).reshape((3,4)).astype(float)
+ index = (1, slice(None, None))
+ inc = [50,50, 30,16]
+
+ np.inplace_increment(a, index, inc)
+
+ assert_array_almost_equal(a, np.array([[ 0, 1, 2, 3],
+ [ 54, 55, 36, 23],
+ [ 8, 9, 10, 11]]).astype(float))
+
+ def test_boolean(self):
+ a = np.arange(12).reshape((3,4)).astype(float)
+ index = (1, np.array([ False, True, False, True], dtype = bool))
+ inc = [30,16]
+
+ np.inplace_increment(a, index, inc)
+
+ assert_array_almost_equal(a, np.array([[ 0, 1, 2, 3],
+ [ 4, 35, 6, 23],
+ [ 8, 9, 10, 11]]).astype(float))
+
+
+
+if __name__ == "__main__":
+ run_module_suite()
Something went wrong with that request. Please try again.