Repository: chainer/chainer

Issue:
Test of `bincount` failes on 32bit environment
Related to #766 , behavior of `numpy.bincount` depends on the environment. Escpecially `int64` and `uint32` doesn't work on 32bit environment. It is because a given array is casted to `intp` first in `numpy.bincount`.

https://github.com/numpy/numpy/blob/maintenance/1.10.x/numpy/core/src/multiarray/compiled_base.c#L108

We need to remove this test case from the test of `cupy.bincount`.



Discussion:

Resolving patch:
diff --git a/cupy/statistics/histogram.py b/cupy/statistics/histogram.py
--- a/cupy/statistics/histogram.py
+++ b/cupy/statistics/histogram.py
@@ -1,3 +1,5 @@
+import numpy
+
 import cupy
 
 
@@ -53,7 +55,7 @@ def bincount(x, weights=None, minlength=None):
             'atomicAdd(&bin[x], 1)',
             'bincount_kernel'
         )(x, b)
-        b = b.astype(cupy.int64)
+        b = b.astype(numpy.intp)
     else:
         # atomicAdd for float64 is not provided
         b = cupy.zeros((size,), dtype=cupy.float32)


Test patch:
diff --git a/cupy/testing/helper.py b/cupy/testing/helper.py
--- a/cupy/testing/helper.py
+++ b/cupy/testing/helper.py
@@ -60,7 +60,7 @@ def _check_cupy_numpy_error(self, cupy_error, cupy_tb, numpy_error,
 def _make_positive_indices(self, impl, args, kw):
     ks = [k for k, v in kw.items() if v in _unsigned_dtypes]
     for k in ks:
-        kw[k] = numpy.int64
+        kw[k] = numpy.intp
     mask = cupy.asnumpy(impl(self, *args, **kw)) >= 0
     return numpy.nonzero(mask)
 
diff --git a/tests/cupy_tests/statics_tests/test_histogram.py b/tests/cupy_tests/statics_tests/test_histogram.py
--- a/tests/cupy_tests/statics_tests/test_histogram.py
+++ b/tests/cupy_tests/statics_tests/test_histogram.py
@@ -1,3 +1,4 @@
+import sys
 import unittest
 
 import numpy
@@ -5,21 +6,33 @@
 from cupy import testing
 
 
-# Note that numpy.bincount does not support uint64 as it casts an input array
-# to int64.
-_except_uint64 = (
+# Note that numpy.bincount does not support uint64 on 64-bit environment
+# as it casts an input array to intp.
+# And it does not support uint32, int64 and uint64 on 32-bit environment.
+_all_types = (
     numpy.float16, numpy.float32, numpy.float64,
-    numpy.int8, numpy.int16, numpy.int32, numpy.int64,
-    numpy.uint8, numpy.uint16, numpy.uint32,
+    numpy.int8, numpy.int16, numpy.int32,
+    numpy.uint8, numpy.uint16,
     numpy.bool_)
+_signed_types = (
+    numpy.int8, numpy.int16, numpy.int32,
+    numpy.bool_)
+
+if sys.maxsize > 2 ** 32:
+    _all_types = _all_types + (numpy.int64, numpy.uint32)
+    _signed_types = _signed_types + (numpy.int64,)
+
+
+def for_all_dtypes_bincount(name='dtype'):
+    return testing.for_dtypes(_all_types, name=name)
 
 
-def for_dtypes_except_uint64(name='dtype'):
-    return testing.for_dtypes(_except_uint64, name=name)
+def for_signed_dtypes_bincount(name='dtype'):
+    return testing.for_dtypes(_signed_types, name=name)
 
 
-def for_dtypes_combination_except_uint64(names):
-    return testing.helper.for_dtypes_combination(_except_uint64, names=names)
+def for_all_dtypes_combination_bincount(names):
+    return testing.helper.for_dtypes_combination(_all_types, names=names)
 
 
 @testing.gpu
@@ -27,57 +40,57 @@ class TestHistogram(unittest.TestCase):
 
     _multiprocess_can_split_ = True
 
-    @for_dtypes_except_uint64()
+    @for_all_dtypes_bincount()
     @testing.numpy_cupy_allclose()
     def test_bincount(self, xp, dtype):
         x = testing.shaped_arange((3,), xp, dtype)
         return xp.bincount(x)
 
-    @for_dtypes_except_uint64()
+    @for_all_dtypes_bincount()
     @testing.numpy_cupy_allclose()
     def test_bincount_duplicated_value(self, xp, dtype):
         x = xp.array([1, 2, 2, 1, 2, 4], dtype)
         return xp.bincount(x)
 
-    @for_dtypes_combination_except_uint64(names=['x_type', 'w_type'])
+    @for_all_dtypes_combination_bincount(names=['x_type', 'w_type'])
     @testing.numpy_cupy_allclose()
     def test_bincount_with_weight(self, xp, x_type, w_type):
         x = testing.shaped_arange((3,), xp, x_type)
         w = testing.shaped_arange((3,), xp, w_type)
         return xp.bincount(x, weights=w)
 
-    @for_dtypes_except_uint64()
+    @for_all_dtypes_bincount()
     @testing.numpy_cupy_allclose()
     def test_bincount_with_minlength(self, xp, dtype):
         x = testing.shaped_arange((3,), xp, dtype)
         return xp.bincount(x, minlength=5)
 
-    @for_dtypes_combination_except_uint64(names=['x_type', 'w_type'])
+    @for_all_dtypes_combination_bincount(names=['x_type', 'w_type'])
     @testing.numpy_cupy_raises()
     def test_bincount_invalid_weight_length(self, xp, x_type, w_type):
         x = testing.shaped_arange((1,), xp, x_type)
         w = testing.shaped_arange((2,), xp, w_type)
         return xp.bincount(x, weights=w)
 
-    @testing.for_signed_dtypes()
+    @for_signed_dtypes_bincount()
     @testing.numpy_cupy_raises()
     def test_bincount_negative(self, xp, dtype):
         x = testing.shaped_arange((3,), xp, dtype) - 2
         return xp.bincount(x)
 
-    @for_dtypes_except_uint64()
+    @for_all_dtypes_bincount()
     @testing.numpy_cupy_raises()
     def test_bincount_too_deep(self, xp, dtype):
         x = xp.array([[1]], dtype)
         return xp.bincount(x)
 
-    @for_dtypes_except_uint64()
+    @for_all_dtypes_bincount()
     @testing.numpy_cupy_raises()
     def test_bincount_too_small(self, xp, dtype):
         x = xp.zeros((), dtype)
         return xp.bincount(x)
 
-    @for_dtypes_except_uint64()
+    @for_all_dtypes_bincount()
     @testing.numpy_cupy_raises()
     def test_bincount_zero_minlength(self, xp, dtype):
         x = testing.shaped_arange((3,), xp, dtype)