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
Added inplace_increment function #326
Conversation
…kind, it will copy, otherwise it will just do it inplace
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. |
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: /*
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. |
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. |
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? |
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. |
@@ -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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indentation problem.
I think I've fixed those formatting issues. |
inplace_binop add_inplace = NULL; | ||
int type_number = PyArray_TYPE(a); | ||
int i =0; | ||
while (type_numbers[i] >= 0 && addition_funcs[i] != NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong curly braces style for while and if.
|
||
/**begin repeat | ||
* #type = npy_complex64, npy_complex128# | ||
* #typen = NPY_COMPLEX64, NPY_COMPLEX128# |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for the reals above, what happens for the longer complex types?
…ind of complex now..." This reverts commit 684ac6a.
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 |
&inc)) { | ||
return NULL; | ||
} | ||
if (arg_a->ob_type != &PyArray_Type){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you meant to write if (!PyArray_Check(arg_a)) {
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I was looking for that function.
… two error checks
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: Of course there may be other options as well, but this approach doesn't seem too difficult and AFAICT everyone seems to like it. |
What would be necessary to do that?
|
Exposing an API for indexing would be a good start. Basically, expose the bound MapIter functions that you used (MapIter_Next, etc.) Then, this API could be used to add in-place indexing as a method to ufuncs more easily (re-using the inner loops of those ufuncs to do the actual calculation). -Travis On Jul 26, 2012, at 8:56 AM, john salvatier wrote:
|
Can the API be exposed as-is or do we need to solve the problem of On Thu, Jul 26, 2012 at 7:36 AM, Travis E. Oliphant <
|
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. |
I've made a separate pull request, for njsmith's recommendation to expose the MapIter API (#375). |
I've made a cleaned up pull request (#377). |
We decided not to move ahead with this pull request and instead just added API support: #377 |
refactor: Reduce vmlsl_* register usage
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.