So I guess what makes this tricky is that:

- We want the behavior to the same for multiple-element arrays,
single-element arrays, zero-dimensional arrays, and scalars -- the
shape of the data shouldn't affect the semantics of **

- We also want the numpy scalar behavior to match the Python scalar behavior

- For Python scalars, int ** (positive int) returns an int, but int **
(negative int) returns a float.

- For arrays, int ** (positive int) and int ** (negative int) _have_
to return the same type, because in general output types are always a
function of the input types and *can't* look at the specific values
involved, and in specific because if you do array([2, 3]) ** array([2,
-2]) you can't return an array where the first element is int and the
second is float.

Given these immutable and contradictory constraints, the last bad
option IMHO would be that we make int ** (negative int) an error in
all cases, and the error message can suggest that instead of writing

    np.array(2) ** -2

they should instead write

    np.array(2) ** -2.0

(And similarly for np.int64(2) ** -2 versus np.int64(2) ** -2.0.)

Definitely annoying, but all the other options seem even more
inconsistent and confusing, and likely to encourage the writing of
subtly buggy code...

(I especially have in mind numpy's habit of silently switching between
scalars and zero-dimensional arrays -- so it's easy to write code that
you think handles arbitrary array dimensions, and it even passes all
your tests, but then it fails when someone passes in a different shape
data and triggers some scalar/array inconsistency. E.g. if we make **
-2 work for scalars but not arrays, then this code:

def f(arr):
    return np.sum(arr, axis=0) ** -2

works as expected for 1-d input, tests pass, everyone's happy... but
as soon as you try to pass in higher dimensional integer input it will
fail.)

-n

On Thu, May 19, 2016 at 2:37 PM, Charles R Harris
<charlesr.har...@gmail.com> wrote:
> Hi All,
>
> There are currently several pull requests apropos integer arrays/scalars to
> integer powers and, because the area is messy and involves tradeoffs, I'd
> like to see some discussion here on the list before proceeding.
>
> Scalars in 1.10
>
> In [1]: 1 ** -1
> Out[1]: 1.0
>
> In [2]: int16(1) ** -1
> Out[2]: 1
>
> In [3]: int32(1) ** -1
> Out[3]: 1
>
> In [4]: int64(1) ** -1
> Out[4]: 1.0
>
> In [5]: 2 ** -1
> Out[5]: 0.5
>
> In [6]: int16(2) ** -1
> Out[6]: 0
>
> In [7]: int32(2) ** -1
> Out[7]: 0
>
> In [8]: int64(2) ** -1
> Out[8]: 0.5
>
> In [9]: 0 ** -1
> ---------------------------------------------------------------------------
> ZeroDivisionError                         Traceback (most recent call last)
> <ipython-input-9-fd405d6cf4bc> in <module>()
> ----> 1 0 ** -1
>
> ZeroDivisionError: 0.0 cannot be raised to a negative power
>
> In [10]: int16(0) ** -1
> /home/charris/.local/bin/ipython:1: RuntimeWarning: divide by zero
> encountered in power
>   #!/usr/bin/python
> /home/charris/.local/bin/ipython:1: RuntimeWarning: invalid value
> encountered in power
>   #!/usr/bin/python
> Out[10]: -9223372036854775808
>
> In [11]: int32(0) ** -1
> Out[11]: -9223372036854775808
>
> In [12]: int64(0) ** -1
> /home/charris/.local/bin/ipython:1: RuntimeWarning: divide by zero
> encountered in long_scalars
>   #!/usr/bin/python
> Out[12]: inf
>
> Proposed
>
> for non-zero numbers the return type should be float.
> for zero numbers a zero division error should be raised.
>
>
> Scalar Arrays in 1.10
>
> In [1]: array(1, dtype=int16) ** -1
> Out[1]: 1
>
> In [2]: array(1, dtype=int32) ** -1
> Out[2]: 1
>
> In [3]: array(1, dtype=int64) ** -1
> Out[3]: 1
>
> In [4]: array(2, dtype=int16) ** -1
> Out[4]: 0
>
> In [5]: array(2, dtype=int32) ** -1
> Out[5]: 0
>
> In [6]: array(2, dtype=int64) ** -1
> Out[6]: 0
>
> In [7]: array(0, dtype=int16) ** -1
> /home/charris/.local/bin/ipython:1: RuntimeWarning: divide by zero
> encountered in power
>   #!/usr/bin/python
> /home/charris/.local/bin/ipython:1: RuntimeWarning: invalid value
> encountered in power
>   #!/usr/bin/python
> Out[7]: -9223372036854775808
>
> In [8]: array(0, dtype=int32) ** -1
> Out[8]: -9223372036854775808
>
> In [9]: array(0, dtype=int64) ** -1
> Out[9]: -9223372036854775808
>
> In [10]: type(array(1, dtype=int64) ** -1)
> Out[10]: numpy.int64
>
> In [11]: type(array(1, dtype=int32) ** -1)
> Out[11]: numpy.int64
>
> In [12]: type(array(1, dtype=int16) ** -1)
> Out[12]: numpy.int64
>
> Note that the return type is always int64 in all these cases. However, type
> is preserved in non-scalar arrays, although the value of int16 is not
> compatible with int32 and int64 for zero division.
>
> In [22]: array([0]*2, dtype=int16) ** -1
> Out[22]: array([0, 0], dtype=int16)
>
> In [23]: array([0]*2, dtype=int32) ** -1
> Out[23]: array([-2147483648, -2147483648], dtype=int32)
>
> In [24]: array([0]*2, dtype=int64) ** -1
> Out[24]: array([-9223372036854775808, -9223372036854775808])
>
> Proposed:
>
> Raise an ZeroDivisionError for zero division, that is, in the ufunc.
> Scalar arrays to return scalar arrays
>
>
> Thoughts?
>
> Chuck
>
>
>
> _______________________________________________
> NumPy-Discussion mailing list
> NumPy-Discussion@scipy.org
> https://mail.scipy.org/mailman/listinfo/numpy-discussion
>



-- 
Nathaniel J. Smith -- https://vorpus.org
_______________________________________________
NumPy-Discussion mailing list
NumPy-Discussion@scipy.org
https://mail.scipy.org/mailman/listinfo/numpy-discussion

Reply via email to