[Numpy-discussion] Value based promotion and user DTypes
ralf.gommers at gmail.com
Tue Jan 26 00:11:46 EST 2021
On Tue, Jan 26, 2021 at 2:01 AM Sebastian Berg <sebastian at sipsolutions.net>
> Hi all,
> does anyone have a thought about how user DTypes (i.e. DTypes not
> currently part of NumPy) should interact with the "value based
> promotion" logic we currently have?
> For now I can just do anything, and we will find out later. And I will
> have to do something for now, basically with the hope that it all turns
> out all-right.
> But there are multiple options for both what to offer to user DTypes
> and where we want to move (I am using `bfloat16` as a potential DType
> 1. The "weak" dtype option (this is what JAX does), where:
> np.array(, dtype=bfloat16) + 4.
> returns a bfloat16, because 4. is "lower" than all floating
> point types.
> In this scheme the user defined `bfloat16` knows that the input
> is a Python float, but it does not know its value (if an
> overflow occurs during conversion, it could warn or error but
> not upcast). For example `np.array(, dtype=uint4) + 2**5`
> will try `uint4(2**5)` assuming it works.
> NumPy is different `2.**300` would ensure the result is a `float64`.
> If a DType does not make use of this, it would get the behaviour
> of option 2.
> 2. The "default" DType option: np.array(, dtype=bfloat16) + 4. is
> always the same as `bfloat16 + float64 -> float64`.
> 3. Use whatever NumPy considers the "smallest appropriate dtype".
> This will not always work correctly for unsigned integers, and for
> floats this would be float16, which doesn't help with bfloat16.
> 4. Try to expose the actual value. (I do not want to do this, but it
> is probably a plausible extension with most other options, since
> the other options can be the "default".)
> Within these options, there is one more difficulty. NumPy currently
> applies the same logic for:
> np.array(, dtype=bfloat16) + np.array(4., dtype=np.float64)
> which in my opinion is wrong (the second array is typed). We do have
> the same issue with deciding what to do in the future for NumPy itself.
> Right now I feel that new (user) DTypes should live in the future
> (whatever that future is).
I agree. And I have a preference for option 1. Option 2 is too greedy in
upcasting, the value-based casting is problematic in multiple ways (e.g.,
hard for Numba because output dtype cannot be predicted from input dtypes),
and option 4 is hard to understand a rationale for (maybe so the user dtype
itself can implement option 3?).
> I have said previously, that we could distinguish this for universal
> functions. But calls like `np.asarray(4.)` are common, and they would
> lose the information that `4.` was originally a Python float.
Hopefully the future will have way fewer asarray calls in it. Rejecting
scalar input to functions would be nice. This is what most other
array/tensor libraries do.
> So, recently, I was considering that a better option may be to limit
> this to math Python operators: +, -, /, **, ...
This discussion may be relevant:
> Those are the places where it may make a difference to write:
> arr + 4. vs. arr + bfloat16(4.)
> int8_arr + 1 vs. int8_arr + np.int8(1)
> arr += 4. (in-place may be the most significant use-case)
> np.add(int8_arr, 1) vs. np.add(int8_arr, np.int8(1))
> is maybe less significant. On the other hand, it would add a subtle
> difference between operators vs. direct ufunc calls...
> In general, it may not matter: We can choose option 1 (which the
> bfloat16 does not have to use), and modify it if we ever change the
> logic in NumPy itself. Basically, I will probably pick option 1 for
> now and press on, and we can reconsider later. And hope that it does
> not make things even more complicated than it is now.
> Or maybe better just limit it completely to always use the default for
> user DTypes?
I'm not sure I understand why you like option 1 but want to give
user-defined dtypes the choice of opting out of it. Upcasting will rarely
make sense for user-defined dtypes anyway.
> But I would be interested if the "limit to Python operators" is
> something we should aim for here. This does make a small difference,
> because user DTypes could "live" in the future if we have an idea of
> how that future may look like.
A future with:
- no array scalars
- 0-D arrays have the same casting rules as >=1-D arrays
- no value-based casting
would be quite nice. For "same kind" casting like
Mixed-kind casting isn't specified there, because it's too different
between libraries. The JAX design (
https://jax.readthedocs.io/en/latest/type_promotion.html) seems sensible
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the NumPy-Discussion