Repository: cupy/cupy
Pull request: Fix edge case compatibility in `cupy.eye()`
Fix #5704

Problem statement:
Edge cases of compatibility in `numpy.eye()`
These are discovered when running hypothesis-based Array API tests. For now I am just documenting these discoveries. I do not know if they are something we'd like to fix -- I'd need more time to look into them. 

Case 1:
```python
>>> np.eye(0, k=9223372036854775808, dtype=np.int8)
array([], shape=(0, 0), dtype=int8)
>>>
>>> cp.eye(0, k=9223372036854775808, dtype=np.int8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/leof/dev/cupy_cuda114/cupy/_creation/basic.py", line 114, in eye
    ret.diagonal(k)[:] = 1
  File "cupy/_core/core.pyx", line 873, in cupy._core.core.ndarray.diagonal
    cpdef ndarray diagonal(self, offset=0, axis1=0, axis2=1):
  File "cupy/_core/core.pyx", line 881, in cupy._core.core.ndarray.diagonal
    return _indexing._ndarray_diagonal(self, offset, axis1, axis2)
  File "cupy/_core/_routines_indexing.pyx", line 218, in cupy._core._routines_indexing._ndarray_diagonal
    return _diagonal(self, offset, axis1, axis2)
OverflowError: Python int too large to convert to C ssize_t
```

Case 2:
```python
>>> np.eye(1, k=-9223372036854775808, dtype=np.int8)
array([[0]], dtype=int8)
>>>
>>> cp.eye(1, k=-9223372036854775808, dtype=np.int8)
array([[1]], dtype=int8)
```

Hints from issue discussion:

Commit message: Update cupy/_creation/basic.py

Co-authored-by: emcastillo <ecastill@preferred.jp>

Patch to review:
diff --git a/cupy/_creation/basic.py b/cupy/_creation/basic.py
index e8095b2fa15..a5ad91cff22 100644
--- a/cupy/_creation/basic.py
+++ b/cupy/_creation/basic.py
@@ -110,7 +110,9 @@ def eye(N, M=None, k=0, dtype=float, order='C'):
     """
     if M is None:
         M = N
-    ret = zeros((N, M), dtype, order=order)
+    ret = zeros((N, M), dtype=dtype, order=order)
+    if k <= -N or k >= M:
+        return ret
     ret.diagonal(k)[:] = 1
     return ret
 
diff --git a/tests/cupy_tests/creation_tests/test_basic.py b/tests/cupy_tests/creation_tests/test_basic.py
index 3a97a59ca9b..7e7b1b8538c 100644
--- a/tests/cupy_tests/creation_tests/test_basic.py
+++ b/tests/cupy_tests/creation_tests/test_basic.py
@@ -1,5 +1,3 @@
-import unittest
-
 import numpy
 import pytest
 
@@ -7,8 +5,7 @@
 from cupy import testing
 
 
-@testing.gpu
-class TestBasic(unittest.TestCase):
+class TestBasic:
     @testing.for_CF_orders()
     @testing.for_all_dtypes()
     @testing.numpy_cupy_array_equal()
@@ -172,11 +169,12 @@ def test_empty_zero_sized_array_strides(self, order):
         b = cupy.empty((1, 0, 2), dtype='d', order=order)
         assert b.strides == a.strides
 
+    @pytest.mark.parametrize('offset', [1, -1, 1<<63, -(1<<63)])
     @testing.for_CF_orders()
     @testing.for_all_dtypes()
     @testing.numpy_cupy_array_equal()
-    def test_eye(self, xp, dtype, order):
-        return xp.eye(5, 4, 1, dtype, order=order)
+    def test_eye(self, xp, dtype, order, offset):
+        return xp.eye(5, 4, offset, dtype, order=order)
 
     @testing.for_all_dtypes()
     @testing.numpy_cupy_array_equal()
@@ -280,8 +278,7 @@ def test_full_like_subok(self):
         'shape': [4, (4, ), (4, 2), (4, 2, 3), (5, 4, 2, 3)],
     })
 )
-@testing.gpu
-class TestBasicReshape(unittest.TestCase):
+class TestBasicReshape:
 
     @testing.with_requires('numpy>=1.17.0')
     @testing.for_orders('CFAK')


Code review comments:
diff_hunk: @@ -172,11 +169,12 @@ def test_empty_zero_sized_array_strides(self, order):
         b = cupy.empty((1, 0, 2), dtype='d', order=order)
         assert b.strides == a.strides
 
+    @pytest.mark.parametrize('offset', [1, -1, 1<<63, -(1<<63)])
path: tests/cupy_tests/creation_tests/test_basic.py
text: @user2:
```suggestion
    @user1.mark.parametrize('offset', [1, -1, 1 << 63, -(1 << 63)])
```
Flake8 😅

@author:
Thanks!