Repository: cupy/cupy
Pull request: Fix `ravel` for strides 0
Always enforce the copy if the array is neither c- or f-contiguous.

Closes #5972 

Problem statement:
`ravel` not working when stride is 0
### Description

When having a scalar that is broadcasted, `.ravel()` does not create a new array with copies of the scalar, but returns the same view.



### To Reproduce

```
>>> import numpy
>>> a=numpy.array([1])
>>> b=numpy.broadcast_to(a, (10,))
>>> b.strides
(0,)
>>> b.ravel().strides
(8,)
```

```
>>> import cupy
>>> a=cupy.array([1])
>>> b=cupy.broadcast_to(a, (10,))
>>> b.strides
(0,)
>>> b.ravel().strides
(0,)
```

### Installation

_No response_

### Environment

master branch

### Additional Information

_No response_

Hints from issue discussion:

Commit message: review changes

Patch to review:
diff --git a/cupy/_core/core.pyx b/cupy/_core/core.pyx
index 84e90649b16..ebd57cdefb8 100644
--- a/cupy/_core/core.pyx
+++ b/cupy/_core/core.pyx
@@ -728,7 +728,8 @@ cdef class ndarray:
            :meth:`numpy.ndarray.ravel`
 
         """
-        return _manipulation._ndarray_ravel(self, order)
+        return _internal_ascontiguousarray(
+            _manipulation._ndarray_ravel(self, order))
 
     cpdef ndarray squeeze(self, axis=None):
         """Returns a view with size-one axes removed.
diff --git a/tests/cupy_tests/manipulation_tests/test_shape.py b/tests/cupy_tests/manipulation_tests/test_shape.py
index e9cadbe4be8..5ba6f53ab9a 100644
--- a/tests/cupy_tests/manipulation_tests/test_shape.py
+++ b/tests/cupy_tests/manipulation_tests/test_shape.py
@@ -170,6 +170,31 @@ def test_ravel3(self, xp, order):
         a = xp.asfortranarray(a)
         return a.ravel(order)
 
+    @testing.for_orders('CFA')
+    @testing.numpy_cupy_array_equal()
+    def test_ravel_non_contiguous(self, xp, order):
+        a = xp.arange(10)[::2]
+        assert not a.flags.c_contiguous and not a.flags.f_contiguous
+        b = a.ravel(order)
+        if order == 'C':
+            assert b.flags.c_contiguous
+        else:
+            assert b.flags.f_contiguous
+        return b
+
+    @testing.for_orders('CFA')
+    @testing.numpy_cupy_array_equal()
+    def test_ravel_broadcasted(self, xp, order):
+        a = xp.array([1])
+        b = xp.broadcast_to(a, (10,))
+        assert not b.flags.c_contiguous and not b.flags.f_contiguous
+        b = b.ravel(order)
+        if order == 'C':
+            assert b.flags.c_contiguous
+        else:
+            assert b.flags.f_contiguous
+        return b
+
     @testing.numpy_cupy_array_equal()
     def test_external_ravel(self, xp):
         a = testing.shaped_arange((2, 3, 4), xp)


Code review comments:
diff_hunk: @@ -170,6 +170,31 @@ def test_ravel3(self, xp, order):
         a = xp.asfortranarray(a)
         return a.ravel(order)
 
+    @testing.for_orders('CFA')
+    @testing.numpy_cupy_array_equal()
+    def test_ravel_non_contiguous(self, xp, order):
+        a = xp.arange(10)[::2]
+        assert not a.flags.c_contiguous and not a.flags.f_contiguous
+        b = a.ravel(order)
+        if order == 'C':
+            assert b.flags.c_contiguous
+        else:
+            assert b.flags.f_contiguous
path: tests/cupy_tests/manipulation_tests/test_shape.py
text: @user1:
```suggestion
        assert b.flags.c_contiguous
```

The `order` argument is how to read the input.

`assert b.ndim == 1 and b.flags.c_contiguous` or `assert b.flags.c_contiguous and b.flags.f_contiguous` might state the intention clearer, while I think they are verbose.

diff_hunk: @@ -170,6 +170,31 @@ def test_ravel3(self, xp, order):
         a = xp.asfortranarray(a)
         return a.ravel(order)
 
+    @testing.for_orders('CFA')
+    @testing.numpy_cupy_array_equal()
+    def test_ravel_non_contiguous(self, xp, order):
+        a = xp.arange(10)[::2]
+        assert not a.flags.c_contiguous and not a.flags.f_contiguous
+        b = a.ravel(order)
+        if order == 'C':
+            assert b.flags.c_contiguous
+        else:
+            assert b.flags.f_contiguous
+        return b
+
+    @testing.for_orders('CFA')
+    @testing.numpy_cupy_array_equal()
+    def test_ravel_broadcasted(self, xp, order):
+        a = xp.array([1])
+        b = xp.broadcast_to(a, (10,))
+        assert not b.flags.c_contiguous and not b.flags.f_contiguous
+        b = b.ravel(order)
+        if order == 'C':
+            assert b.flags.c_contiguous
+        else:
+            assert b.flags.f_contiguous
path: tests/cupy_tests/manipulation_tests/test_shape.py
text: @user1:
ditto
```suggestion
        assert b.flags.c_contiguous
```