Re: [Numpy-discussion] Proposal to accept NEP 49: Data allocation strategies

2021-05-11 Thread Eric Wieser
> Yes, sorry, had been a while since I had looked it up:
>
> https://docs.python.org/3/c-api/memory.html#c.PyMemAllocatorEx

That `PyMemAllocatorEx` looks almost exactly like one of the two variants I
was proposing. Is there a reason for wanting to define our own structure vs
just using that one?
I think the NEP should at least offer a brief comparison to that structure,
even if we ultimately end up not using it.

> That all looks like it can be customized in theory. But I am not sure
> that it is practical, except for hooking and calling the previous one.

Is chaining allocators not likely something we want to support too? For
instance, an allocator that is used for large arrays, but falls back to the
previous one for small arrays?

> I have to say it feels a bit
> like exposing things publicly, that are really mainly used internally,
> but not sure...  Presumably Python uses the `ctx` for something though.

I'd argue `ctx` / `baton` / `user_data` arguments are an essential part of
any C callback API.
I can't find any particularly good reference for this right now, but I have
been bitten multiple times by C APIs that forget to add this argument.

>  If someone wants a different strategy (i.e. different alignment) they
create a new policy

The crux of the problem here is that without very nasty hacks, C and C++ do
not allow new functions to be created at runtime.
This makes it very awkward to write a parameterizable allocator. If you
want to create two aligned allocators with different alignments, and you
don't have a `ctx` argument to plumb through that alignment information,
you're forced to write the entire thing twice.

> I guess the C++ similarity may be a reason, but I am not familiar with
that.

Similarity isn't the only motivation - I was considering compatibility.
Consider a user who's already written a shiny stateful C++ allocator, and
wants to use it with numpy.
I've made a gist at
https://gist.github.com/eric-wieser/6d0fde53fc1ba7a2fa4ac208467f2ae5 which
demonstrates how to hook an arbitrary C++ allocator into this new numpy
allocator API, that compares both the NEP version and the version with an
added `ctx` argument.
The NEP version has a bug that is very hard to fix without duplicating the
entire `numpy_handler_from_cpp_allocator` function.

If compatibility with C++ seems too much of a stretch, the NEP API is not
even compatible with `PyMemAllocatorEx`.

> But right now the proposal says this is static, and I honestly don't
> see much reason for it to be freeable?  The current use-cases `cupy` or
> `pnumpy` don't not seem to need it.

I don't know much about either of these use cases, so the following is
speculative.
In cupy, presumably the application is to tie allocation to a specific GPU
device.
Presumably then, somewhere in the python code there is a handle to a GPU
object, through which the allocators operate.
If that handle is stored in the allocator, and the allocator is freeable,
then it is possible to write code that automatically releases the GPU
handle after the allocator has been restored to the default and the last
array using it is cleaned up.

If that cupy use-case seems somwhat plausible, then I think we should go
with the PyObject approach.
If it doesn't seem plausible, then I think the `ctx` approach is
acceptable, and we should consider declaring our struct
```struct { PyMemAllocatorEx allocator; char const *name; }``` to reuse the
existing python API unless there's a reason not to.

Eric




On Tue, 11 May 2021 at 04:58, Matti Picus  wrote:

> On 10/5/21 8:43 pm, Sebastian Berg wrote:
>
> > But right now the proposal says this is static, and I honestly don't
> > see much reason for it to be freeable?
>
>
> I think this is the crux of the issue. The current design is for a
> singly-allocated struct to be passed around since it is just an
> aggregate of functions. If someone wants a different strategy (i.e.
> different alignment) they create a new policy: there are no additional
> parameters or data associated with the struct. I don't really see an ask
> from possible users for anything more, and so would prefer to remain
> with the simplest possible design. If the need arises in the future for
> additional data, which is doubtful, I am confident we can expand this as
> needed, and do not want to burden the current design with unneeded
> optional features.
>
>
> It would be nice to hear from some actual users if they need the
> flexibility.
>
>
> In any case I would like to resolve this quickly and get it into the
> next release, so if Eric is adamant that the advanced design is needed I
> will accept his proposal, since that seems easier than any of the
> alternatives so far.
>
>
> Matti
>
> ___
> NumPy-Discussion mailing list
> NumPy-Discussion@python.org
> https://mail.python.org/mailman/listinfo/numpy-discussion
>
___
NumPy-Discussion mailing list
NumPy-Discussion@python.org
https:/

Re: [Numpy-discussion] Proposal to accept NEP 49: Data allocation strategies

2021-05-11 Thread Sebastian Berg
On Tue, 2021-05-11 at 09:54 +0100, Eric Wieser wrote:
> > Yes, sorry, had been a while since I had looked it up:
> > 
> > https://docs.python.org/3/c-api/memory.html#c.PyMemAllocatorEx
> 
> That `PyMemAllocatorEx` looks almost exactly like one of the two
> variants I
> was proposing. Is there a reason for wanting to define our own
> structure vs
> just using that one?
> I think the NEP should at least offer a brief comparison to that
> structure,
> even if we ultimately end up not using it.
> 
> > That all looks like it can be customized in theory. But I am not
> > sure
> > that it is practical, except for hooking and calling the previous
> > one.
> 
> Is chaining allocators not likely something we want to support too?
> For
> instance, an allocator that is used for large arrays, but falls back
> to the
> previous one for small arrays?
> 
> > I have to say it feels a bit
> > like exposing things publicly, that are really mainly used
> > internally,
> > but not sure...  Presumably Python uses the `ctx` for something
> > though.
> 
> I'd argue `ctx` / `baton` / `user_data` arguments are an essential
> part of
> any C callback API.
> I can't find any particularly good reference for this right now, but
> I have
> been bitten multiple times by C APIs that forget to add this
> argument.


Can't argue with that :).

I am personally still mostly a bit concerned that we have some way to
modify/extend in the future (even clunky seems fine).
Beyond that, I don't care all that much.  Passing a context feels right
to me, but neither do I know that we need it.


Using PyObject still feels a bit much, but I am also not opposed.  I
guess for future extension, we would have to subclass ourselves and/or
include an ABI version number (if just to avoid `PyObject_TypeCheck`
calls to figure out which ABI version we got).


Otherwise, either allocating the struct or including a version number
(or reserved space) in the struct/PyObject is probably good enough to
to ensure we have a path for modifying/extending the ABI.


I hope that the actual end-users can chip in and clear it up a bit...

Cheers,

Sebastian



> 
> >  If someone wants a different strategy (i.e. different alignment)
> > they
> create a new policy
> 
> The crux of the problem here is that without very nasty hacks, C and
> C++ do
> not allow new functions to be created at runtime.
> This makes it very awkward to write a parameterizable allocator. If
> you
> want to create two aligned allocators with different alignments, and
> you
> don't have a `ctx` argument to plumb through that alignment
> information,
> you're forced to write the entire thing twice.
> 
> > I guess the C++ similarity may be a reason, but I am not familiar
> > with
> that.
> 
> Similarity isn't the only motivation - I was considering
> compatibility.
> Consider a user who's already written a shiny stateful C++ allocator,
> and
> wants to use it with numpy.
> I've made a gist at
> https://gist.github.com/eric-wieser/6d0fde53fc1ba7a2fa4ac208467f2ae5 
> which
> demonstrates how to hook an arbitrary C++ allocator into this new
> numpy
> allocator API, that compares both the NEP version and the version
> with an
> added `ctx` argument.
> The NEP version has a bug that is very hard to fix without
> duplicating the
> entire `numpy_handler_from_cpp_allocator` function.
> 
> If compatibility with C++ seems too much of a stretch, the NEP API is
> not
> even compatible with `PyMemAllocatorEx`.
> 
> > But right now the proposal says this is static, and I honestly
> > don't
> > see much reason for it to be freeable?  The current use-cases
> > `cupy` or
> > `pnumpy` don't not seem to need it.
> 
> I don't know much about either of these use cases, so the following
> is
> speculative.
> In cupy, presumably the application is to tie allocation to a
> specific GPU
> device.
> Presumably then, somewhere in the python code there is a handle to a
> GPU
> object, through which the allocators operate.
> If that handle is stored in the allocator, and the allocator is
> freeable,
> then it is possible to write code that automatically releases the GPU
> handle after the allocator has been restored to the default and the
> last
> array using it is cleaned up.
> 
> If that cupy use-case seems somwhat plausible, then I think we should
> go
> with the PyObject approach.
> If it doesn't seem plausible, then I think the `ctx` approach is
> acceptable, and we should consider declaring our struct
> ```struct { PyMemAllocatorEx allocator; char const *name; }``` to
> reuse the
> existing python API unless there's a reason not to.
> 
> Eric
> 
> 
> 
> 
> On Tue, 11 May 2021 at 04:58, Matti Picus 
> wrote:
> 
> > On 10/5/21 8:43 pm, Sebastian Berg wrote:
> > 
> > > But right now the proposal says this is static, and I honestly
> > > don't
> > > see much reason for it to be freeable?
> > 
> > 
> > I think this is the crux of the issue. The current design is for a
> > singly-allocated struct to be passed around since it is just an
>

Re: [Numpy-discussion] NEP 42 status – Store quantity in a NumPy array and convert it :)

2021-05-11 Thread Sebastian Berg
On Thu, 2021-03-25 at 17:27 -0500, Sebastian Berg wrote:
> On Wed, 2021-03-17 at 17:12 -0500, Sebastian Berg wrote:
> > On Wed, 2021-03-17 at 07:56 -0500, Lee Johnston wrote:
> 
> 
> 
> > 3. In parallel, I will create a small "toy" DType based on that
> >    experimental API.  Probably in a separate repo (in the NumPy
> >    organization?).
> > 


As a small update to the experimental user DTypes.  The branches now
include the merge of the PR https://github.com/numpy/numpy/pull/18905
which implementes most of NEP 43 to refactor ufuncs and allow new user
DTypes for them.  (The PR does not cover reductions, so those are also
missing here.)

That means, the unit dtype's multiplication can be written as
`np.multiply(unit_arr, unit_arr)` or just `unit_arr * unit_arr`.

And after importing the right experimental module string comparison can
use `np.equal` directly.


With that, the most central parts of dtypes exists far enough to play
around.  (For units the simple re-use of existing math functions is
missing, though).

Cheers,

Sebastian


> 
> So this is started. What you need to do right now if you want to try
> is
> work of this branch in NumPy:
> 
>  
> https://github.com/numpy/numpy/compare/main...seberg:experimental-dtype-api
> 
> Install NumPy with `NPY_USE_NEW_CASTINGIMPL=1 python -mpip install .`
> or your favorite alternative.
> (The `NPY_USE_NEW_CASTINGIMPL=1` should be unnecessary very soon,
> working of a branch and not "main" will hopefully also be unnecessary
> soon.)
> 
> 
> Then fetch: https://github.com/seberg/experimental_user_dtypes
> and install it as well in the same environment.
> 
> 
> After that, you can jump through the hoop of setting:
> 
>     NUMPY_EXPERIMENTAL_DTYPE_API=1
> 
> And you can enjoy these type of examples (while expecting hard
> crashes
> when going too far beyond!):
> 
>     from experimental_user_dtypes import float64unit as u
>     import numpy as np
> 
>     F = np.array([u.Quantity(70., "Fahrenheit")])
>     C = F.astype(u.Float64UnitDType("Celsius"))
>     print(repr(C))
>     # array([21.15 °C], dtype='Float64UnitDType(degC)')
> 
>     m = np.array([u.Quantity(5., "m")])
>     m_squared = u.multiply(m, m)
>     print(repr(m_squared))
>     # array([25.0 m**2], dtype='Float64UnitDType(m**2)')
> 
>     # Or conversion to SI the long route:
>     pc = np.arange(5.,
> dtype="float64").view(u.Float64UnitDType("pc"))
>     pc.astype(pc.dtype.si())
>     # array([0.0 m, 3.085677580962325e+16 m, 6.17135516192465e+16 m,
>     #    9.257032742886974e+16 m, 1.23427103238493e+17 m],
>     #   dtype='Float64UnitDType(m)')
> 
> 
> Yes, the code has some horrible hacks around creating the DType, but
> the basic mechanism i.e. "functions you need to implement" are not
> expected to change lot.
> 
> Right now, it forces you to use and implement the scalar `u.Quantity`
> and the code sample uses it. But you can also do:
> 
>     np.arange(3.).view(u.Float64UnitDType("m"))
> 
> I do have plans to "not have a scalar" so the 0-D result would still
> be
> an array.  But that option doesn't exist yet (and right now the
> scalar
> is used for printing).
> 
> 
> (There is also a `string_equal` "ufunc-like" that works on "S"
> dtypes.)
> 
> Cheers,
> 
> Sebastian
> 
> 
> 
> PS: I need to figure out some details about how to create DTypes and
> DType instances with regards to our stable ABI.  The current
> "solution"
> is some weird subclassing hoops which are probably not good.
> 
> That is painful unfortunately and any ideas would be great :). 
> Unfortunately, it requires a grasp around the C-API and
> metaclassing...
> 
> 
> 
> > 
> > Anyone using the API, should expect bugs, crashes and changes for a
> > while.  But hopefully will only require small code modifications
> > when
> > the API becomes public.
> > 
> > My personal plan for a toy example is currently a "scaled integer".
> > E.g. a uint8 where you can set a range `[min_double, max_double]`
> > that
> > it maps to (which makes the DType "parametric").
> > We discussed some other examples, such as a "modernized" rational
> > DType, that could be nice as well, lets see...
> > 
> > Units would be a great experiment, but seem a bit complex to me (I
> > don't know units well though). So to keep it baby steps :) I would
> > aim
> > for doing the above and then we can experiment on Units together!
> > 
> > 
> > Since it came up:  I agree that a Python API would be great to
> > have.
> > It
> > is something I firmly kept on the back-burner...  It should not be
> > very
> > hard (if rudimentary), but unless it would help experiments a lot,
> > I
> > would tend to leave it on the back-burner for now.
> > 
> > Cheers,
> > 
> > Sebastian
> > 
> > 
> > [1]  Maybe a `uint8` storage that maps to evenly spaced values on a
> > parametric range `[double_min, double_max]`.  That seems like a
> > good
> > trade-off in complexity.
> > 
> > 
> > 
> > > On Tue, Mar 16, 2021 at 4:11 PM Sebastian Berg <
> > > sebast...@sipsolu

[Numpy-discussion] NumPy Community Meeting Wednesday

2021-05-11 Thread Sebastian Berg
Hi all,

There will be a NumPy Community meeting Wednesday Mai 12th at
20:00 UTC. Everyone is invited and encouraged to
join in and edit the work-in-progress meeting topics and notes at:

https://hackmd.io/76o-IxCjQX2mOXO_wwkcpg?both

Best wishes

Sebastian

___
NumPy-Discussion mailing list
NumPy-Discussion@python.org
https://mail.python.org/mailman/listinfo/numpy-discussion