Discussion:
[Numpy-discussion] Integers to integer powers
Charles R Harris
2016-05-19 21:37:05 UTC
Permalink
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
j***@gmail.com
2016-05-20 02:16:35 UTC
Permalink
Post by Charles R Harris
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])
- Raise an ZeroDivisionError for zero division, that is, in the ufunc.
- Scalar arrays to return scalar arrays
Thoughts?
Why does negative exponent not upcast to float like division?
sounds like python 2 to me

Josef
Post by Charles R Harris
Chuck
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
j***@gmail.com
2016-05-20 02:17:54 UTC
Permalink
On Thu, May 19, 2016 at 5:37 PM, Charles R Harris <
Post by Charles R Harris
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])
- Raise an ZeroDivisionError for zero division, that is, in the ufunc.
- Scalar arrays to return scalar arrays
Thoughts?
Why does negative exponent not upcast to float like division?
sounds like python 2 to me
from __future__ import negative_power

Josef
Josef
Post by Charles R Harris
Chuck
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
Nathaniel Smith
2016-05-20 03:30:40 UTC
Permalink
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
Post by Charles R Harris
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])
Raise an ZeroDivisionError for zero division, that is, in the ufunc.
Scalar arrays to return scalar arrays
Thoughts?
Chuck
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
--
Nathaniel J. Smith -- https://vorpus.org
Charles R Harris
2016-05-20 18:35:47 UTC
Permalink
Post by Nathaniel Smith
- 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 **
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.)
Hmm, the Alexandrian solution. The main difficulty with this solution that
this will likely to break working code. We could try it, or take the safe
route of raising a (Visible)DeprecationWarning. The other option is to
simply treat the negative power case uniformly as floor division and raise
an error on zero division, but the difference from Python power would be
highly confusing. I think I would vote for the second option with a
DeprecationWarning.

<snip>

Chuck
Charles R Harris
2016-05-20 18:48:29 UTC
Permalink
On Fri, May 20, 2016 at 12:35 PM, Charles R Harris <
Post by Charles R Harris
Post by Nathaniel Smith
- 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 **
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.)
Hmm, the Alexandrian solution. The main difficulty with this solution that
this will likely to break working code. We could try it, or take the safe
route of raising a (Visible)DeprecationWarning. The other option is to
simply treat the negative power case uniformly as floor division and raise
an error on zero division, but the difference from Python power would be
highly confusing. I think I would vote for the second option with a
DeprecationWarning.
I suspect that the different behavior of int64 on my system is due to
inheritance from Python 2.7 int

In [1]: isinstance(int64(1), int)
Out[1]: True

That different behavior is also carried over for Python 3.

Chuck
Nathaniel Smith
2016-05-20 19:15:55 UTC
Permalink
On Fri, May 20, 2016 at 11:35 AM, Charles R Harris
Post by Charles R Harris
Post by Nathaniel Smith
- 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 **
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.)
Hmm, the Alexandrian solution. The main difficulty with this solution that
this will likely to break working code. We could try it, or take the safe
route of raising a (Visible)DeprecationWarning.
Right, sorry, I was talking about the end goal -- there's a separate
question of how we get there. Pretty much any solution is going to
require some sort of deprecation cycle though I guess, and at least
the deprecate -> error transition is a lot easier than the working ->
working different transition.
Post by Charles R Harris
The other option is to
simply treat the negative power case uniformly as floor division and raise
an error on zero division, but the difference from Python power would be
highly confusing. I think I would vote for the second option with a
DeprecationWarning.
So "floor division" here would mean that k ** -n == 0 for all k and n
except for k == 1, right? In addition to the consistency issue, that
doesn't seem like a behavior that's very useful to anyone...

-n
--
Nathaniel J. Smith -- https://vorpus.org
Charles R Harris
2016-05-20 19:23:39 UTC
Permalink
Post by Nathaniel Smith
On Fri, May 20, 2016 at 11:35 AM, Charles R Harris
Post by Charles R Harris
Post by Nathaniel Smith
- 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 **
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.)
Hmm, the Alexandrian solution. The main difficulty with this solution
that
Post by Charles R Harris
this will likely to break working code. We could try it, or take the safe
route of raising a (Visible)DeprecationWarning.
Right, sorry, I was talking about the end goal -- there's a separate
question of how we get there. Pretty much any solution is going to
require some sort of deprecation cycle though I guess, and at least
the deprecate -> error transition is a lot easier than the working ->
working different transition.
Post by Charles R Harris
The other option is to
simply treat the negative power case uniformly as floor division and
raise
Post by Charles R Harris
an error on zero division, but the difference from Python power would be
highly confusing. I think I would vote for the second option with a
DeprecationWarning.
So "floor division" here would mean that k ** -n == 0 for all k and n
except for k == 1, right? In addition to the consistency issue, that
doesn't seem like a behavior that's very useful to anyone...
And -1 as well. The virtue is consistancy while deprecating. Or we could
just back out the current changes in master and throw in deprecation
warnings. That has the virtue of simplicity and not introducing possible
code breaks.

Chuck
j***@gmail.com
2016-05-20 19:44:52 UTC
Permalink
Post by Charles R Harris
Post by Nathaniel Smith
On Fri, May 20, 2016 at 11:35 AM, Charles R Harris
Post by Charles R Harris
Post by Nathaniel Smith
- 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 **
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.)
Hmm, the Alexandrian solution. The main difficulty with this solution
that
Post by Charles R Harris
this will likely to break working code. We could try it, or take the
safe
Post by Charles R Harris
route of raising a (Visible)DeprecationWarning.
Right, sorry, I was talking about the end goal -- there's a separate
question of how we get there. Pretty much any solution is going to
require some sort of deprecation cycle though I guess, and at least
the deprecate -> error transition is a lot easier than the working ->
working different transition.
Post by Charles R Harris
The other option is to
simply treat the negative power case uniformly as floor division and
raise
Post by Charles R Harris
an error on zero division, but the difference from Python power would be
highly confusing. I think I would vote for the second option with a
DeprecationWarning.
So "floor division" here would mean that k ** -n == 0 for all k and n
except for k == 1, right? In addition to the consistency issue, that
doesn't seem like a behavior that's very useful to anyone...
And -1 as well. The virtue is consistancy while deprecating. Or we could
just back out the current changes in master and throw in deprecation
warnings. That has the virtue of simplicity and not introducing possible
code breaks.
can numpy cast to float by default for power or **?

At least then we always get correct numbers.

Are there dominant usecases that require default return dtype int?
AFAICS, it's always possible to choose the return dtype in np.power.

Josef
Post by Charles R Harris
Chuck
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
Nathaniel Smith
2016-05-20 22:54:41 UTC
Permalink
On May 20, 2016 12:44 PM, <***@gmail.com> wrote:
[...]
Post by j***@gmail.com
can numpy cast to float by default for power or **?
Maybe? The question is whether there are any valid use cases for getting
Post by j***@gmail.com
np.array([1, 2, 3]) ** 2
array([1, 4, 9])

It's not 100% obvious to me but intuitively this seems like an operation
that we probably want to support? Especially since there's a reasonable
range of int64 values that can't be represented exactly as floats.

-n
j***@gmail.com
2016-05-21 00:11:20 UTC
Permalink
Post by Nathaniel Smith
[...]
Post by j***@gmail.com
can numpy cast to float by default for power or **?
Maybe? The question is whether there are any valid use cases for getting
Post by j***@gmail.com
np.array([1, 2, 3]) ** 2
array([1, 4, 9])
It's not 100% obvious to me but intuitively this seems like an operation
that we probably want to support? Especially since there's a reasonable
range of int64 values that can't be represented exactly as floats.
It would still be supported by the np.power function with dtype keyword for
users that want to strictly control the dtype.

The question is mainly for the operator ** which doesn't have options and
there I think it's more appropriate for users that want correct numbers but
not necessarily have or want to watch out for the dtype.


Related: Python 3.4 returns complex for (-1)**0.5 while numpy returns nan.
That's a similar case of upcasting if the result doesn't fit.

Longterm I think it would still be nice if numpy could do value dependent
type promotion. e.g. when a value is encountered that doesn't fit, then
upcast the result at the cost of possibly having to copy the existing
results.
In the current setting the user has to decide in advance what the necessary
maybe required dtype is supposed to be. (Of course I have no idea about the
technical problems or computational cost of this.)

(Julia dispatch seems to make it easier to construct new types. e.g. we
could have a flexible dtype that is free to upcast to whatever is required
for the calculation. just guessing)


practicality:
going from int to float is a common usecase and we would expect getting the
correct numbers 2**(-2) -> promote

complex is in most fields an unusual outcome for integer or float
calculations
(e.g. box-cox transformation for x>0 ) having suddenly complex numbers is
weird, getting nans is standard float response -> don't promote


I'm still largely in the python 2.x habit of adding a decimal points to
numbers for float or a redundant `* 1.` in my code to avoid integer
division or other weirdness. So, I never realized that ** in numpy doesn't
always promote to float which I kind of thought it did.
Maybe it's not yet time to drop all the decimal points or `* 1.` from the
code?


Josef
Post by Nathaniel Smith
-n
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
Alan Isaac
2016-05-20 20:22:05 UTC
Permalink
Post by Nathaniel Smith
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.)
Fwiw, Haskell has three exponentiation operators
because of such ambiguities. I don't use C, but
I think the contrasting decision there was to
always return a double, which has a clear attraction
since for any fixed-width integral type, most of the
possible input pairs overflow the type.

My core inclination would be to use (what I understand to be)
the C convention that integer exponentiation always produces
a double, but to support dtype-specific exponentiation with
a function. But this is just a user's perspective.

Cheers,
Alan Isaac
Warren Weckesser
2016-05-20 20:27:19 UTC
Permalink
Post by Alan Isaac
Post by Nathaniel Smith
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.)
Fwiw, Haskell has three exponentiation operators
because of such ambiguities. I don't use C, but
I think the contrasting decision there was to
always return a double, which has a clear attraction
since for any fixed-width integral type, most of the
possible input pairs overflow the type.
My core inclination would be to use (what I understand to be)
the C convention that integer exponentiation always produces
a double, but to support dtype-specific exponentiation with
a function.
C doesn't have an exponentiation operator. The C math library has pow,
powf and powl, which (like any C functions) are explicitly typed.

Warren


But this is just a user's perspective.
Post by Alan Isaac
Cheers,
Alan Isaac
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
j***@gmail.com
2016-05-20 20:35:05 UTC
Permalink
On Fri, May 20, 2016 at 4:27 PM, Warren Weckesser <
Post by Warren Weckesser
Post by Alan Isaac
Post by Nathaniel Smith
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.)
Fwiw, Haskell has three exponentiation operators
because of such ambiguities. I don't use C, but
I think the contrasting decision there was to
always return a double, which has a clear attraction
since for any fixed-width integral type, most of the
possible input pairs overflow the type.
My core inclination would be to use (what I understand to be)
the C convention that integer exponentiation always produces
a double, but to support dtype-specific exponentiation with
a function.
C doesn't have an exponentiation operator. The C math library has pow,
powf and powl, which (like any C functions) are explicitly typed.
Warren
But this is just a user's perspective.
Post by Alan Isaac
Cheers,
Alan Isaac
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
another question

uint are positive so the division problem doesn't show up. So that could
still handle a usecase for ints.

I'm getting stronger in favor of float because raising an exception (or
worse, nonsense) in half of the parameter spaces sounds ... (maybe kind of
silly)

Josef
Marten van Kerkwijk
2016-05-20 20:44:43 UTC
Permalink
Hi All,

As a final desired state, always returning float seems the best idea. It
seems quite similar to division in this regard, where integer division
works for some values, but not for all. This means not being quite
consistent with python, but as Nathan pointed out, one cannot have
value-dependent dtype's for arrays (and scalars should indeed behave the
same way).

If so, then like for division, in-place power should raise a `TypeError`.
​
Obviously, one could have a specific function for integers (like `//` for
division) for cases where it is really needed.

Now, how to get there... Maybe we can learn from division? At least, I
guess at some point `np.divide` became equivalent to `np.true_divide`?

All the best,

Marten
Alan Isaac
2016-05-20 21:33:20 UTC
Permalink
Yes, I was referring to `pow`,
but I had in mind the C++ version,
which is overloaded:
http://www.cplusplus.com/reference/cmath/pow/

Cheers,
Alan
C doesn't have an exponentiation operator. The C math library has pow, powf and powl, which (like any C functions) are explicitly typed.
Antoine Pitrou
2016-05-24 11:30:34 UTC
Permalink
On Thu, 19 May 2016 20:30:40 -0700
Post by Nathaniel Smith
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...
That sounds like a good compromise, indeed.

Regards

Antoine.
Marten van Kerkwijk
2016-05-24 14:03:59 UTC
Permalink
The error on int ** (-int) is OK with me too (even though I prefer just
returning floats).
Having a `floor_pow` function may still be good with this solution too.
-- Marten
Alan Isaac
2016-05-24 16:41:21 UTC
Permalink
The error on int ** (-int) is OK with me too (even though I prefer just returning floats).
What exactly is the argument against *always* returning float
(even for positive integer exponents)? Relevant context is:

- int ** int will often ("usually") overflow
- a numpy function cd meet specialized exponentiation needs

Thanks,
Alan Isaac
Stephan Hoyer
2016-05-24 17:19:35 UTC
Permalink
Post by Alan Isaac
What exactly is the argument against *always* returning float
(even for positive integer exponents)?
If we were starting over from scratch, I would agree with you, but the int
** 2 example feels quite compelling to me. I would guess there lots of code
out there that expects the result to have integer dtype.

As a contrived example, I might write np.arange(n) ** 2 to produce an
indexer for the diagonal elements of an array.
Alan Isaac
2016-05-24 17:31:44 UTC
Permalink
the int ** 2 example feels quite compelling to me
Yes, but that one case is trivial: a*a

And at least as compelling is not have a**-2 fail
and not being tricked by say np.arange(10)**10.
The latter is a promise of hidden errors.

Alan
Eric Moore
2016-05-24 17:36:33 UTC
Permalink
I'd say the most compelling case for it is that is how it has always
worked. How much code will break if we make that change? (Or if not break,
at least have a change in dtype?) Is that worth it?

Eric
Post by Alan Isaac
the int ** 2 example feels quite compelling to me
Yes, but that one case is trivial: a*a
And at least as compelling is not have a**-2 fail
and not being tricked by say np.arange(10)**10.
The latter is a promise of hidden errors.
Alan
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
Alan Isaac
2016-05-24 17:49:21 UTC
Permalink
Post by Eric Moore
How much code will break if we make that change?
Since even arange(10)**10 is already broken,
there will probably not be much new breakage.

But under any of the new proposals, *something* will break.
So the question is, which shows the most foresight?
Having (**) actually work seems worth quite a lot.

Alan Isaac
Nathaniel Smith
2016-05-24 19:05:26 UTC
Permalink
Post by Eric Moore
I'd say the most compelling case for it is that is how it has always
worked.
Post by Eric Moore
How much code will break if we make that change? (Or if not break, at
least
Post by Eric Moore
have a change in dtype?) Is that worth it?
The current behavior for arrays is:

# Returns int
In [2]: np.arange(10) ** 2
Out[2]: array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81])

# Returns nonsensical/useless results
In [3]: np.arange(10) ** -1
/home/njs/.user-python3.5-64bit/bin/ipython:1: RuntimeWarning: divide by
zero encountered in power
#!/home/njs/.user-python3.5-64bit/bin/python3.5
/home/njs/.user-python3.5-64bit/bin/ipython:1: RuntimeWarning: invalid
value encountered in power
#!/home/njs/.user-python3.5-64bit/bin/python3.5
Out[3]:
array([-9223372036854775808, 1, 0,
0, 0, 0,
0, 0, 0,
0])

-n
--
Nathaniel J. Smith -- https://vorpus.org
Eric Moore
2016-05-24 19:57:53 UTC
Permalink
Yes, I'm fully aware of that. I'm speaking toward changing the default
result dtype. Raising an error for negative exponents is a fine idea.
Changing np.arange(10)**3 to have a non-integer dtype seems like a big
change.

Speaking of this, that some of the integer array operation errors can be
controlled via the np.seterr and some cannot should also be addressed
longer term.

Eric
Post by Eric Moore
Post by Eric Moore
I'd say the most compelling case for it is that is how it has always
worked.
Post by Eric Moore
How much code will break if we make that change? (Or if not break, at
least
Post by Eric Moore
have a change in dtype?) Is that worth it?
# Returns int
In [2]: np.arange(10) ** 2
Out[2]: array([ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
# Returns nonsensical/useless results
In [3]: np.arange(10) ** -1
/home/njs/.user-python3.5-64bit/bin/ipython:1: RuntimeWarning: divide by
zero encountered in power
#!/home/njs/.user-python3.5-64bit/bin/python3.5
/home/njs/.user-python3.5-64bit/bin/ipython:1: RuntimeWarning: invalid
value encountered in power
#!/home/njs/.user-python3.5-64bit/bin/python3.5
array([-9223372036854775808, 1, 0,
0, 0, 0,
0, 0, 0,
0])
-n
--
Nathaniel J. Smith -- https://vorpus.org
_______________________________________________
NumPy-Discussion mailing list
https://mail.scipy.org/mailman/listinfo/numpy-discussion
Alan Isaac
2016-05-24 20:15:27 UTC
Permalink
Changing np.arange(10)**3 to have a non-integer dtype seems like a big change.
What about np.arange(100)**5?

Alan Isaac
R Schumacher
2016-05-24 20:33:20 UTC
Permalink
Post by Alan Isaac
Changing np.arange(10)**3 to have a non-integer dtype seems like a big change.
What about np.arange(100)**5?
import numpy
a=numpy.arange(100)**5
<string>:1: RuntimeWarning: invalid value encountered in power
Post by Alan Isaac
a=numpy.arange(100)**5.
b=numpy.arange(100.)**5
a==b
array([ True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True,
True, True], dtype=bool)
Post by Alan Isaac
numpy.arange(100)**5
array([ 0, 1, 32, 243, 1024,
3125, 7776, 16807, 32768, 59049,
100000, 161051, 248832, 371293, 537824,
759375, 1048576, 1419857, 1889568, 2476099,
3200000, 4084101, 5153632, 6436343, 7962624,
9765625, 11881376, 14348907, 17210368, 20511149,
24300000, 28629151, 33554432, 39135393, 45435424,
52521875, 60466176, 69343957, 79235168, 90224199,
102400000, 115856201, 130691232, 147008443, 164916224,
184528125, 205962976, 229345007, 254803968, 282475249,
312500000, 345025251, 380204032, 418195493, 459165024,
503284375, 550731776, 601692057, 656356768, 714924299,
777600000, 844596301, 916132832, 992436543, 1073741824,
1160290625, 1252332576, 1350125107, 1453933568, 1564031349,
1680700000, 1804229351, 1934917632, 2073071593, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648])
Post by Alan Isaac
numpy.arange(100, dtype=numpy.int64)**5
array([ 0, 1, 32, 243, 1024,
3125, 7776, 16807, 32768, 59049,
100000, 161051, 248832, 371293, 537824,
759375, 1048576, 1419857, 1889568, 2476099,
3200000, 4084101, 5153632, 6436343, 7962624,
9765625, 11881376, 14348907, 17210368, 20511149,
24300000, 28629151, 33554432, 39135393, 45435424,
52521875, 60466176, 69343957, 79235168, 90224199,
102400000, 115856201, 130691232, 147008443, 164916224,
184528125, 205962976, 229345007, 254803968, 282475249,
312500000, 345025251, 380204032, 418195493, 459165024,
503284375, 550731776, 601692057, 656356768, 714924299,
777600000, 844596301, 916132832, 992436543, 1073741824,
1160290625, 1252332576, 1350125107, 1453933568, 1564031349,
1680700000, 1804229351, 1934917632, 2073071593, 2219006624,
2373046875, 2535525376, 2706784157, 2887174368, 3077056399,
3276800000, 3486784401, 3707398432, 3939040643, 4182119424,
4437053125, 4704270176, 4984209207, 5277319168, 5584059449,
5904900000, 6240321451, 6590815232, 6956883693, 7339040224,
7737809375, 8153726976, 8587340257, 9039207968, 9509900499],
dtype=int64)
Charles R Harris
2016-06-04 17:05:08 UTC
Permalink
Changing np.arange(10)**3 to have a non-integer dtype seems like a big change.
What about np.arange(100)**5?
import numpy
a=numpy.arange(100)**5
<string>:1: RuntimeWarning: invalid value encountered in power
a=numpy.arange(100)**5.
b=numpy.arange(100.)**5
a==b
array([ True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True], dtype=bool)
numpy.arange(100)**5
array([ 0, 1, 32, 243, 1024,
3125, 7776, 16807, 32768, 59049,
100000, 161051, 248832, 371293, 537824,
759375, 1048576, 1419857, 1889568, 2476099,
3200000, 4084101, 5153632, 6436343, 7962624,
9765625, 11881376, 14348907, 17210368, 20511149,
24300000, 28629151, 33554432, 39135393, 45435424,
52521875, 60466176, 69343957, 79235168, 90224199,
102400000, 115856201, 130691232, 147008443, 164916224,
184528125, 205962976, 229345007, 254803968, 282475249,
312500000, 345025251, 380204032, 418195493, 459165024,
503284375, 550731776, 601692057, 656356768, 714924299,
777600000, 844596301, 916132832, 992436543, 1073741824,
1160290625, 1252332576, 1350125107, 1453933568, 1564031349,
1680700000, 1804229351, 1934917632, 2073071593, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648,
-2147483648, -2147483648, -2147483648, -2147483648, -2147483648])
numpy.arange(100, dtype=numpy.int64)**5
array([ 0, 1, 32, 243, 1024,
3125, 7776, 16807, 32768, 59049,
100000, 161051, 248832, 371293, 537824,
759375, 1048576, 1419857, 1889568, 2476099,
3200000, 4084101, 5153632, 6436343, 7962624,
9765625, 11881376, 14348907, 17210368, 20511149,
24300000, 28629151, 33554432, 39135393, 45435424,
52521875, 60466176, 69343957, 79235168, 90224199,
102400000, 115856201, 130691232, 147008443, 164916224,
184528125, 205962976, 229345007, 254803968, 282475249,
312500000, 345025251, 380204032, 418195493, 459165024,
503284375, 550731776, 601692057, 656356768, 714924299,
777600000, 844596301, 916132832, 992436543, 1073741824,
1160290625, 1252332576, 1350125107, 1453933568, 1564031349,
1680700000, 1804229351, 1934917632, 2073071593, 2219006624,
2373046875, 2535525376, 2706784157, 2887174368, 3077056399,
3276800000, 3486784401, 3707398432, 3939040643, 4182119424,
4437053125, 4704270176, 4984209207, 5277319168, 5584059449,
5904900000, 6240321451, 6590815232, 6956883693, 7339040224,
7737809375, 8153726976, 8587340257, 9039207968, 9509900499],
dtype=int64)
That is the Python default. To always see warnings do
`warnings.simplefilter('always')` before running.

Chuck

Stephan Hoyer
2016-05-24 17:41:54 UTC
Permalink
Post by Alan Isaac
Yes, but that one case is trivial: a*a
an_explicit_name ** 2 is much better than an_explicit_name *
an_explicit_name, though.
Matthew Brett
2016-05-24 17:41:29 UTC
Permalink
Post by Alan Isaac
the int ** 2 example feels quite compelling to me
Yes, but that one case is trivial: a*a
Right, but you'd have to know to change your code when numpy makes
this change. Your code will suddenly have a new datatype, that you
may be passing into other code, which may generate different results,
that will be hard to track down.
Post by Alan Isaac
And at least as compelling is not have a**-2 fail
I imagine that's about an order of magnitude less common in real code,
but an error seems an acceptable result to me, so I know I have to do
a ** -2.0
Post by Alan Isaac
and not being tricked by say np.arange(10)**10.
The latter is a promise of hidden errors.
It's a well-understood promise though - you always have to be careful
of integer overflow.

Best,

Matthew
Alan Isaac
2016-05-24 17:53:11 UTC
Permalink
Post by Matthew Brett
It's a well-understood promise though - you always have to be careful
of integer overflow.
Of course. But **almost all** cases overflow.

And "well understood" assume a certain sophistication
of the user, while `arange` will certainly be used
by beginners.

Alan
Loading...