gh-1021: Add decorator for np conversions#1137
Conversation
Signed-off-by: Pradyot Ranjan <99216956+pradyotRanjan@users.noreply.github.com>
| dxp = default_xp(xp.__name__) | ||
| return tuple(xp.asarray(arr) for arr in dxp.tril_indices(n, k=k, m=m)) | ||
|
|
||
| def numpy_fallback(func): |
There was a problem hiding this comment.
@lucascolley This could be more generalised for the standard. Currently, I feel the limitation is the inability to specify parameters such as dtype for xp.asarray.
@ntessore @paddyroddy Would love some feedback.
There was a problem hiding this comment.
cc @betatim, I wonder whether there is any overlap between this and scikit-learn/scikit-learn#34324 which would be nice to upstream into xpx.numpy.
There was a problem hiding this comment.
This is pretty much out of my purview, and @paddyroddy is on leave currently, so it might be a bit before he can answer.
In my opinion, which as I said doesn't carry much weight here, I don't think this is something where GLASS should have to innovate. We're in the cosmological simulations business, not the array business 🙂
There was a problem hiding this comment.
There isn't a lot of context in the PR description, what I figure from a quick browse: this PR adds a decorator that converts all "arrays" that are passed to a function to numpy arrays, then calls the wrapped function and converts any "arrays" in the output back to the original namespace.
I'm not sure how much the work in scikit-learn/scikit-learn#34324 is useful for others. It feels somewhat scikit-learn specific. The tricky (imho) part is deciding when to convert to numpy, which I guess is a library specific thing? At least in scikit-learn the decision to convert to numpy is not a simple "this function is hard to support". It depends on the global config, the specific constructor arguments of the class and what the type of the input array is. Once you know you want to convert to numpy it feels like you are "home free".
For this library it looks like most of the code of the decorator deals with finding arrays in the input arguments and such infrastructure. One thing I'm wondering is what to do if the inputs use different array namespaces or if one of the inputs is a torch array and another a list. But maybe these are things aren't an issue here? In scikit-learn we allow a mix of things :-/
Conclusion, I wonder if we can make a generic fallback decorator for xpx that doesn't get mega complex and at the same time supports most of the different behaviours users would want. If the answer is "yes we can" then I think it would be a useful tool (even if I don't think we'd use it in scikit-learn).
paddyroddy
left a comment
There was a problem hiding this comment.
This seems like a really nice approach and is the kind of thing I was thinking of. Long-term I definitely wouldn't want the code in this repo as it is complex, and our current approach would be easier for contributors to understand. Do we think we've captured all edge cases here? I assume the decorator is smart enough to detect which variables need to be converted and which don't?
| # This function is difficult to port to the Array API so for now we work | ||
| # in NumPy and ultimately convert back at the end of it. |
There was a problem hiding this comment.
We would need to remove this comment too
| dxp = default_xp(xp.__name__) | ||
| return tuple(xp.asarray(arr) for arr in dxp.tril_indices(n, k=k, m=m)) | ||
|
|
||
| def numpy_fallback(func): |
There was a problem hiding this comment.
Some typing and a docstring would be good here
There was a problem hiding this comment.
Thanks for the review. I've added some in the latest commit.
Signed-off-by: Pradyot Ranjan <99216956+pradyotRanjan@users.noreply.github.com>
…/prady0t/glass into add-decorator-for-np-conversions
prady0t
left a comment
There was a problem hiding this comment.
Currently, the state of the decorator is:
- Goes through all the function parameters (args and kwargs) and collects those which have
__array_namespace__attribute. - If
xpis in kwargs, it uses its value. If value is none, then usedefault_xp().xpvalue overwrites parameter's__array_namespace__attribute value. - If no
xpparameter and no parameter has__array_namespace__attribute, returns the function as is. - Parameters are converted to numpy
- Depending on what the original function returns, the wrapper recursively converts (so that it can support tuple, list and dict) back to the
xp.
There are two places where we cannot use this decorator: pixwin and the query_strip function, as we are specifying dtype during conversion. Something that this decorator cannot do yet.
Signed-off-by: Pradyot Ranjan <99216956+pradyotRanjan@users.noreply.github.com>
| def collect_arrays(obj): | ||
| if hasattr(obj, "__array_namespace__"): | ||
| list_of_xp.append(obj) | ||
| elif isinstance(obj, dict): | ||
| for v in obj.values(): | ||
| collect_arrays(v) | ||
| elif isinstance(obj, Sequence) and not isinstance(obj, (str, bytes)): | ||
| for item in obj: | ||
| collect_arrays(item) |
There was a problem hiding this comment.
I've made the decorator even smarter! Now, it's able to extract xp if the given arguments are a collection (or Sequence) of xp. Similarly, NumPy conversions are also being done recursively. This is helpful in the places commented below.
| return healpy.alm2map( | ||
| alms, | ||
| nside, | ||
| inplace=inplace, | ||
| lmax=lmax, | ||
| pixwin=pixwin, | ||
| pol=pol, |
| return healpy.map2alm( | ||
| maps, | ||
| datapath=_get_healpy_datapath(), | ||
| lmax=lmax, | ||
| pol=pol, | ||
| use_pixel_weights=use_pixel_weights, |
| theta, phi = healpix.randang( | ||
| nside, | ||
| np.asarray(ipix), | ||
| ipix, |
| return xp.asarray( | ||
| healpy.Rotator(coord=self.coord).rotate_map_pixel(np.asarray(m)), | ||
| ) | ||
| return healpy.Rotator(coord=self.coord).rotate_map_pixel(m) |
Signed-off-by: Pradyot Ranjan <99216956+pradyotRanjan@users.noreply.github.com>
Description
Addresses #1021
Checks