Re: Do special functions need to have all kwargs/attributes/methods of ufuncs?
Ralf Gommers <[email protected]> Thu, 21 Sep 2023 08:22:41 +0200
| Newsgroups | gmane.comp.python.scientific.devel |
|---|---|
| Message-ID | <CABL7CQgdc6BMw2yGY44iWFyj4WuR9DhFnR-P1Q4+semAG3D8Dw@mail.gmail.com> |
On Wed, Sep 20, 2023 at 10:33 PM Albert Steppi <[email protected]> wrote: > It should be straightforward if a little tedious to add support for the > ufunc attributes like `nin`, `nout`, etc. and ufunc methods like `reduce`, > `accumulate`, etc. (for numpy and raising an error for backends that don't > support these methods), if not doing so would require a difficult > deprecation cycle. I think making something like isinstance(special.ndtr, > np.ufunc) work would require some pretty ugly hackery, so I'd prefer to at > least break this. > Agreed. Ufuncs are nice, but can also be constraining. E.g., if we'd like to add a keyword to a function that isn't supported by ufuncs, we have to be able to change the return type. We reserve the right to move functions to being ufuncs, and we should also reserve the right to move them away from it. The current status is this: >>> import numpy as np >>> from scipy import special >>> ufuncs = [] >>> non_ufuncs = [] >>> for fname in special.__all__: ... func = getattr(special, fname) ... if isinstance(func, np.ufunc): ... ufuncs.append(func) ... else: ... non_ufuncs.append(func) ... >>> len(ufuncs) 232 >>> len(non_ufuncs) 119 It's not only functions that don't accept arrays in the non-ufunc list. E.g., `logsumexp`, `softmax`, `lambertw`, `multigammaln` and `sinc` aren't ufuncs (and none have the warning Robert mentions). The docs are a bit incomplete and misleading - we cannot promise that all functions that accept arrays are or will be ufuncs, it would for example prevent implementing any function in pure Python. I believe functions in special being ufuncs yes or no is mostly leaking of implementation details - using the ufunc machinery was nice and performant, but quite a few of the methods don't really make sense as Albert points out. There's probably more such leakage - for example, `sinc` has array-function dispatching support. The return type situation is pretty messy right now, and wholly unintentional: >>> special.softmax(d) # not a ufunc, so returns numpy.ndarray array([0.18242552, 0.81757448]) >>> special.j1(d) # is a ufunc, so returns a dask array dask.array<j1, shape=(2,), dtype=float64, chunksize=(2,), chunktype=numpy.ndarray> >>> special.sinc(d) # not a ufunc, but has array-function support -> dask array dask.array<sinc, shape=(2,), dtype=float64, chunksize=(2,), chunktype=numpy.ndarray> >>> special.airy([1, 2.5]) # is a ufunc, accepts array-like's, returns numpy.ndarray (array([0.13529242, 0.01572592]), array([-0.15914744, -0.02625088]), array([1.20742359, 6.48166074] ), array([0.93243593, 9.42142332])) >>> special.airy(d) # is a ufunc but breaks *because* it's a ufunc for dask arrays ... TypeError: operand type(s) all returned NotImplemented from __array_ufunc__(<ufunc 'airy'>, '__call __', dask.array<array, shape=(2,), dtype=float64, chunksize=(2,), chunktype=numpy.ndarray>): 'Array ' This kind of inconsistency with return types for arrays from other array libraries is one of the key things that the current effort is attempting to fix. > I can't see a serious use for `reduce`, `accumulate`, and `reduceat` for > special functions, but `outer` and `at` could be useful in some cases. > Based on the documentation Robert Kern linked to, I think it would probably > be best to support all ufunc attributes and methods. > > Albert > > On Tue, Sep 19, 2023 at 12:30 PM Robert Kern <[email protected]> > wrote: > >> On Tue, Sep 19, 2023 at 12:00 PM Matt Haberland <[email protected]> >> wrote: >> >>> Yes, the documentation mentions that they are "technically" ufuncs. My >>> comment was about documentation of the features of ufuncs - the >>> documentation seems to intentionally hide all ufunc parameters from the >>> signature. Please see >>> https://github.com/scipy/scipy/pull/19023#issuecomment-1711949107 >>> for further context. >>> >> >> More out of concision and deduplication than anything else because those >> parameters are documented in `ufunc` itself. >> >> ... >> >> That's as much the official docs for this object as what appears on >> docs.scipy.org. >> >> These are ufuncs. They should be ufuncs regardless of the environment >> variable. If you want to change them from being ufuncs to just being >> elementwise functions, go through a big deprecation where they are *never* >> ufuncs regardless of environment variable (i.e. plain functions with the >> docs.scipy.org signatures and just use the ufunc implementations >> underneath when the Array API is `numpy`). But I suspect one can also >> implement a ufunc-like override object that passes through all attribute >> access and ufunc-only keyword calls to the ufunc object and calls the >> override elementwise function only when it fits. >> > This would be a useful thing to do either way I think, because it would make the API more uniform. I'd be inclined to keep only the few methods that are actually useful, and not the whole API surface of ufuncs. The not so useful methods and attributes can be deprecated first in a wrapper. Finally, a wrapper would also help fix the issues with using `inspect.signature`, `inspect.isroutine` & co, and attributes like `__module__`. Cheers, Ralf _______________________________________________ SciPy-Dev mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/scipy-dev.python.org/ Member address: [email protected]