2

What is the difference between numpy.power or ** for negative exponents and / when working with arrays? and why does numpy.power not act element-wise as described in the documentation.

For example, using python 2.7.3:

>>> from __future__ import division
>>> import numpy as np   
>>> A = arange(9).reshape(3,3)
>>> A
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

It appears that ** and numpy.power are not acting element-wise when the exponent is negative

>>> A**-1
array([[-9223372036854775808,                    1,                    0],
       [                   0,                    0,                    0],
       [                   0,                    0,                    0]])
>>> np.power(A, -1)
array([[-9223372036854775808,                    1,                    0],
       [                   0,                    0,                    0],
       [                   0,                    0,                    0]])

Whereas / is acting element-wise

>>> 1/A
array([[        inf,  1.        ,  0.5       ],
       [ 0.33333333,  0.25      ,  0.2       ],
       [ 0.16666667,  0.14285714,  0.125     ]])

I have no such problems when the exponent is positive. Why does it behave differently for negative exponents?

DJCowley
  • 83
  • 2
  • 4
  • I have now found that `np.power(A, -1.0)` and `A**-1.0` give the correct result. Does this mean that `np.power` and `**` are using floor division (integer division) for integer negative exponents? – DJCowley Feb 04 '14 at 14:24

2 Answers2

5

This is primarily an issue of casting. Operators naturally assume that you do not wish to upcast a number to a different type. The closest value of 2**-1 with integers is 0, this can be verified int(2**-1) >>>0.

First create array A of type int:

A = np.arange(9).reshape(3,3)
>>> A.dtype
dtype('int64')

Copy array A to A_float as type float:

>>> A_float = A.astype(float)
>>> A_float.dtype
dtype('float64')

Run the **-1 operation on both:

>>> A_float**-1
array([[        inf,  1.        ,  0.5       ],
       [ 0.33333333,  0.25      ,  0.2       ],
       [ 0.16666667,  0.14285714,  0.125     ]])

>>> A**-1
array([[-9223372036854775808,                    1,                    0],
       [                   0,                    0,                    0],
       [                   0,                    0,                    0]])

Observe numpy does not automatically assume you want to complete this operation as float and attempts to accomplish this with integers. If you signify a float in either operand you will obtain a float output due to the "safe" casting rules:

>>> A**-1.0
array([[        inf,  1.        ,  0.5       ],
       [ 0.33333333,  0.25      ,  0.2       ],
       [ 0.16666667,  0.14285714,  0.125     ]])

Another option is to force np.power to cast the operation as a float:

>>> np.power(A,-1,dtype=float)
array([[        inf,  1.        ,  0.5       ],
       [ 0.33333333,  0.25      ,  0.2       ],
       [ 0.16666667,  0.14285714,  0.125     ]])

I am not sure why you are obtaining a float result with 1/A. 1.0/A works just fine however.

Daniel
  • 19,179
  • 7
  • 60
  • 74
2

** or np.power is working, but is performing integer division. Moreover, performing same operations in ipython results in RuntimeWarning of zero division.

RuntimeWarning: divide by zero encountered in true_divide

Now, we can turn same operations into float division by turning operand into float array

A = np.array([[0., 1., 2.], [3., 4., 5.], [6., 7., 8.]])

Then, 1/, ** and np.power behave identically

Sudeep Juvekar
  • 4,898
  • 3
  • 29
  • 35
  • I tried it in ipython and did not get a `RuntimeWarning`. – DJCowley Feb 04 '14 at 14:30
  • 1
    @DJCowley It depends on the `numpy` configuration. See for example [this](http://stackoverflow.com/q/15933741/510937) question and related answer. – Bakuriu Feb 04 '14 at 14:36
  • Additionally, there is a global default verbosity in warnings that you can set in disable-warnings.py file. For example, see this question http://stackoverflow.com/questions/9031783/hide-all-warnings-in-ipython . (This might be digressing from the question, or maybe it's not. After all, we do find useful information regarding np.power and ** using these warnings) – Sudeep Juvekar Feb 04 '14 at 14:41