Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: Inconsistent behaviour of return type of int64.__pow__ #8809

Closed
eric-wieser opened this issue Mar 22, 2017 · 5 comments
Closed

BUG: Inconsistent behaviour of return type of int64.__pow__ #8809

eric-wieser opened this issue Mar 22, 2017 · 5 comments

Comments

@eric-wieser
Copy link
Member

eric-wieser commented Mar 22, 2017

Raised by this stackoverflow question. The following code , run on master...

x_actual = 49997
x3_actual = x_actual ** 3

for dtype in [np.int64, np.uint64, np.float64]:
	x = {}
	x['scalar'] = dtype(x_actual)
	x[    '0d'] = np.array(x_actual, dtype=dtype)
	x[    '1d'] = np.array([x_actual], dtype=dtype)
	x[    '2d'] = np.array([[x_actual]], dtype=dtype)

	x3 = {k: x**3 for k, x in x.items()}

	assert all(v == x3_actual for v in x3.values())  #ok

	print("input type of {}:".format(np.dtype(dtype)))
	for k, v in x3.items():
		print("{:>8s}: {}".format(k, v.dtype))
	print()

Gives the alarming output of:

input of int64
  scalar: int64
      0d: int64
      1d: int64
      2d: int64

input of uint64
  scalar: float64
      0d: float64
      1d: uint64    # What?
      2d: uint64    # What?

input of float64
  scalar: float64
      0d: float64
      1d: float64
      2d: float64

The return dtype of power should not depend on the shape of the input array

@eric-wieser eric-wieser changed the title BUG: Inconsistent behaviour of return type of uint64.__pow__ BUG: Inconsistent behaviour of return type of __pow__ Mar 22, 2017
@eric-wieser eric-wieser changed the title BUG: Inconsistent behaviour of return type of __pow__ BUG: Inconsistent behaviour of return type of int64.__pow__ Mar 22, 2017
eric-wieser added a commit to eric-wieser/numpy that referenced this issue Mar 27, 2017
Partials resolves numpy#8809

Previously, the signatures were:
* power(int, int) -> int
* power(uint, uint) -> uint.

Since we forbid the second argument from being negative, we can also define
* power(int, uint) -> int: same as power(int, int), without an error check
* power(uint, int) -> uint: same as power(uint, uint), with an error check

If we do not define these, then the signature used is:
* t = common(int, uint); power(t, t) -> t:

This is bad, because common(int64, uint64) is float
@ahaldane
Copy link
Member

ahaldane commented May 8, 2017

Isn't this a problem with all ufuncs?

Extending your example a little, and testing for multiplication a*b:

val = 1
cases = [(' p', val)]
for dtype,name in zip([np.int64, np.uint64], ['i', 'u']):
    cases.extend([('s' + name, dtype(val)),
                  ('0' + name, np.array(val, dtype=dtype)),
                  ('1' + name, np.array([val], dtype=dtype)),
                  ('2' + name, np.array([[val]], dtype=dtype))])
names, tps = zip(*cases)
ret = [[(a*b) for n,b in cases] for n1,a in cases]
kinds = [['p' if type(k) == int else k.dtype.kind for k in r] for r in ret]
print "     " + " ".join(names)
print "\n".join(n + "    " + "  ".join(d) for n,d in zip(names, kinds))

Output: (p = python, i = int, u = uint, s = scalar, 0,1,2 = 0d,1d,2d)

      p si 0i 1i 2i su 0u 1u 2u
 p    p  i  i  i  i  f  f  u  u
si    i  i  i  i  i  f  f  u  u
0i    i  i  i  i  i  f  f  u  u
1i    i  i  i  i  i  i  i  f  f
2i    i  i  i  i  i  i  i  f  f
su    f  f  f  i  i  u  u  u  u
0u    f  f  f  i  i  u  u  u  u
1u    u  u  u  f  f  u  u  u  u
2u    u  u  u  f  f  u  u  u  u

the power operation gives the same table.

@ahaldane
Copy link
Member

ahaldane commented May 8, 2017

Also, this is loosely related to #8231 and the related discussions on the mailing list last year about integer powers:

First in June under subject "Integers to integer powers, let's make a decision ", then in October under the same thread plus others like "fpower".

(but to be clear, this is a somewhat different issue)

@eric-wieser
Copy link
Member Author

The shape inconsistencies is #10322 again

@FrozenBob
Copy link

This is the result of NumPy's "type demotion" rules for mixed scalar/array operations. When a positive-dimensional array and a 0-dimensional array or array scalar participate in a binary operation, the scalar gets "type demoted" based on its value, so 3 gets demoted to uint8, and the result dtype is uint64. On the other hand, when both operands are scalar, NumPy treats 3 as having dtype np.int_, which is signed, and numpy can't find an integer dtype big enough to hold all values of uint64 and int_, so it uses float64.

This is documented in the docs for numpy.result_type. (The implementation is in convert_datatype.c.)

The Stack Overflow question also showed what looked like NumPy using a floating-point power implementation for the uint64 output case. I don't know why that was happening, but it seems to have been fixed at some point after the question was asked.

@seberg
Copy link
Member

seberg commented Nov 19, 2022

Closing as duplicate of gh-22624

@seberg seberg closed this as completed Nov 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants