Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c1ac694

Browse files
WarrenWeckessercharris
authored andcommitted
BUG: lib: Fix handling of integer arrays by gradient.
In numpy.gradient, convert integer array inputs to float64 to avoid unwanted modular arithmetic. Closes gh-15207.
1 parent 9d979a7 commit c1ac694

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

numpy/lib/function_base.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -979,13 +979,18 @@ def gradient(f, *varargs, **kwargs):
979979
# scalar or 1d array for each axis
980980
dx = list(varargs)
981981
for i, distances in enumerate(dx):
982-
if np.ndim(distances) == 0:
982+
distances = np.asanyarray(distances)
983+
if distances.ndim == 0:
983984
continue
984-
elif np.ndim(distances) != 1:
985+
elif distances.ndim != 1:
985986
raise ValueError("distances must be either scalars or 1d")
986987
if len(distances) != f.shape[axes[i]]:
987988
raise ValueError("when 1d, distances must match "
988989
"the length of the corresponding dimension")
990+
if np.issubdtype(distances.dtype, np.integer):
991+
# Convert numpy integer types to float64 to avoid modular
992+
# arithmetic in np.diff(distances).
993+
distances = distances.astype(np.float64)
989994
diffx = np.diff(distances)
990995
# if distances are constant reduce to the scalar case
991996
# since it brings a consistent speedup
@@ -1024,8 +1029,12 @@ def gradient(f, *varargs, **kwargs):
10241029
elif np.issubdtype(otype, np.inexact):
10251030
pass
10261031
else:
1027-
# all other types convert to floating point
1028-
otype = np.double
1032+
# All other types convert to floating point.
1033+
# First check if f is a numpy integer type; if so, convert f to float64
1034+
# to avoid modular arithmetic when computing the changes in f.
1035+
if np.issubdtype(otype, np.integer):
1036+
f = f.astype(np.float64)
1037+
otype = np.float64
10291038

10301039
for axis, ax_dx in zip(axes, dx):
10311040
if f.shape[axis] < edge_order + 1:

numpy/lib/tests/test_function_base.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,40 @@ def test_values(self):
10841084
assert_raises(ValueError, gradient, np.arange(1), edge_order=2)
10851085
assert_raises(ValueError, gradient, np.arange(2), edge_order=2)
10861086

1087+
@pytest.mark.parametrize('f_dtype', [np.uint8, np.uint16,
1088+
np.uint32, np.uint64])
1089+
def test_f_decreasing_unsigned_int(self, f_dtype):
1090+
f = np.array([5, 4, 3, 2, 1], dtype=f_dtype)
1091+
g = gradient(f)
1092+
assert_array_equal(g, [-1]*len(f))
1093+
1094+
@pytest.mark.parametrize('f_dtype', [np.int8, np.int16,
1095+
np.int32, np.int64])
1096+
def test_f_signed_int_big_jump(self, f_dtype):
1097+
maxint = np.iinfo(f_dtype).max
1098+
x = np.array([1, 3])
1099+
f = np.array([-1, maxint], dtype=f_dtype)
1100+
dfdx = gradient(f, x)
1101+
assert_array_equal(dfdx, [(maxint + 1) // 2]*2)
1102+
1103+
@pytest.mark.parametrize('x_dtype', [np.uint8, np.uint16,
1104+
np.uint32, np.uint64])
1105+
def test_x_decreasing_unsigned(self, x_dtype):
1106+
x = np.array([3, 2, 1], dtype=x_dtype)
1107+
f = np.array([0, 2, 4])
1108+
dfdx = gradient(f, x)
1109+
assert_array_equal(dfdx, [-2]*len(x))
1110+
1111+
@pytest.mark.parametrize('x_dtype', [np.int8, np.int16,
1112+
np.int32, np.int64])
1113+
def test_x_signed_int_big_jump(self, x_dtype):
1114+
minint = np.iinfo(x_dtype).min
1115+
maxint = np.iinfo(x_dtype).max
1116+
x = np.array([-1, maxint], dtype=x_dtype)
1117+
f = np.array([minint // 2, 0])
1118+
dfdx = gradient(f, x)
1119+
assert_array_equal(dfdx, [0.5, 0.5])
1120+
10871121

10881122
class TestAngle(object):
10891123

0 commit comments

Comments
 (0)