From 4c67383da0538436829dd3703c44772dff84d8d3 Mon Sep 17 00:00:00 2001 From: Ryosuke Okuta Date: Mon, 19 Jun 2017 15:06:58 +0900 Subject: [PATCH 1/3] Add order and subok argument --- cupy/core/core.pyx | 28 ++++++---- cupy/creation/from_data.py | 22 ++++++-- .../creation_tests/test_from_data.py | 52 ++++++++++++++----- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/cupy/core/core.pyx b/cupy/core/core.pyx index 0afa46362be..08eca84f732 100644 --- a/cupy/core/core.pyx +++ b/cupy/core/core.pyx @@ -269,9 +269,9 @@ cdef class ndarray: dtype: Type specifier. order ({'C', 'F', 'A', 'K'}): Row-major (C-style) or column-major (Fortran-style) order. - When `order` is 'A', it uses 'F' if `a` is column-major and - uses `C` otherwise. - And when `order` is 'K', it keeps strides as closely as + When ``order`` is 'A', it uses 'F' if ``a`` is column-major and + uses 'C' otherwise. + And when ``order`` is 'K', it keeps strides as closely as possible. copy (bool): If it is False and no cast happens, then this method returns the array itself. Otherwise, a copy is returned. @@ -349,8 +349,8 @@ cdef class ndarray: Args: order ({'C', 'F', 'A', 'K'}): Row-major (C-style) or column-major (Fortran-style) order. - When `order` is 'A', it uses 'F' if `a` is column-major and - uses `C` otherwise. + When ``order`` is 'A', it uses 'F' if ``a`` is column-major and + uses 'C' otherwise. And when `order` is 'K', it keeps strides as closely as possible. @@ -1893,19 +1893,22 @@ cdef _argmax = create_reduction_func( # Array creation routines # ----------------------------------------------------------------------------- -cpdef ndarray array(obj, dtype=None, bint copy=True, Py_ssize_t ndmin=0): - # TODO(beam2d): Support order and subok options +cpdef ndarray array(obj, dtype=None, bint copy=True, str order='K', + bint subok=False, Py_ssize_t ndmin=0): + # TODO(beam2d): Support subok options cdef Py_ssize_t nvidem cdef ndarray a, src + if subok: + raise NotImplementedError if isinstance(obj, ndarray): src = obj if dtype is None: dtype = src.dtype dev = src.data.device if dev is None or dev.id == device.get_device_id(): - a = src.astype(dtype, copy=copy) + a = src.astype(dtype, order=order, copy=copy) else: - a = src.copy().astype(dtype, copy=False) + a = src.copy(order=order).astype(dtype, copy=False) ndim = a._shape.size() if ndmin > ndim: @@ -1914,12 +1917,15 @@ cpdef ndarray array(obj, dtype=None, bint copy=True, Py_ssize_t ndmin=0): a = a.view() a.shape = (1,) * (ndmin - ndim) + a.shape else: - a_cpu = numpy.array(obj, dtype=dtype, copy=False, order='C', + if order == 'K': + order = 'A' + a_cpu = numpy.array(obj, dtype=dtype, copy=False, order=order, ndmin=ndmin) + order = 'C' if a_cpu.flags.c_contiguous else 'F' a_dtype = a_cpu.dtype if a_dtype.char not in '?bhilqBHILQefd': raise ValueError('Unsupported dtype %s' % a_dtype) - a = ndarray(a_cpu.shape, dtype=a_dtype) + a = ndarray(a_cpu.shape, dtype=a_dtype, order=order) if a_cpu.ndim == 0: a.fill(a_cpu[()]) return a diff --git a/cupy/creation/from_data.py b/cupy/creation/from_data.py index cd3a76ebd78..f35ff307124 100644 --- a/cupy/creation/from_data.py +++ b/cupy/creation/from_data.py @@ -1,7 +1,7 @@ from cupy import core -def array(obj, dtype=None, copy=True, ndmin=0): +def array(obj, dtype=None, copy=True, order='K', subok=False, ndmin=0): """Creates an array on the current device. This function currently does not support the ``order`` and ``subok`` @@ -13,17 +13,33 @@ def array(obj, dtype=None, copy=True, ndmin=0): dtype: Data type specifier. copy (bool): If ``False``, this function returns ``obj`` if possible. Otherwise this function always returns a new array. + order ({'C', 'F', 'A', 'K'}): Row-major (C-style) or column-major + (Fortran-style) order. + When ``order`` is 'A', it uses 'F' if ``a`` is column-major and + uses 'C' otherwise. + And when ``order`` is 'K', it keeps strides as closely as + possible. + If ``obj`` is ``numpy.ndarray``, thi function returns 'C' or 'F' + order array. + subok (bool): If True, then sub-classes will be passed-through, + otherwise the returned array will be forced to be a base-class + array (default). ndmin (int): Minimum number of dimensions. Ones are inserted to the head of the shape if needed. Returns: cupy.ndarray: An array on the current device. + + + .. note:: + This method currently does not support ``subok`` + arguments. + .. seealso:: :func:`numpy.array` """ - # TODO(beam2d): Support order and subok options - return core.array(obj, dtype, copy, ndmin) + return core.array(obj, dtype, copy, order, subok, ndmin) def asarray(a, dtype=None): diff --git a/tests/cupy_tests/creation_tests/test_from_data.py b/tests/cupy_tests/creation_tests/test_from_data.py index 2ff5a1aa53f..583a4759e97 100644 --- a/tests/cupy_tests/creation_tests/test_from_data.py +++ b/tests/cupy_tests/creation_tests/test_from_data.py @@ -11,51 +11,75 @@ class TestFromData(unittest.TestCase): _multiprocess_can_split_ = True + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array(self, xp, dtype): - return xp.array([[1, 2, 3], [2, 3, 4]], dtype=dtype) + def test_array(self, xp, dtype, order): + return xp.array([[1, 2, 3], [2, 3, 4]], dtype=dtype, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_from_numpy(self, xp, dtype): + def test_array_from_numpy(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), numpy, dtype) - return xp.array(a) + return xp.array(a, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_copy(self, xp, dtype): + def test_array_copy(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), xp, dtype) - return xp.array(a) + return xp.array(a, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_copy_is_copied(self, xp, dtype): + def test_array_copy_is_copied(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), xp, dtype) - b = xp.array(a) + b = xp.array(a, order=order) a.fill(0) return b + @testing.for_orders('CFAK') @testing.for_all_dtypes(name='dtype1') @testing.for_all_dtypes(name='dtype2') @testing.numpy_cupy_array_equal() - def test_array_copy_with_dtype(self, xp, dtype1, dtype2): + def test_array_copy_with_dtype(self, xp, dtype1, dtype2, order): a = testing.shaped_arange((2, 3, 4), xp, dtype1) - return xp.array(a, dtype=dtype2) + return xp.array(a, dtype=dtype2, order=order) + @testing.for_orders('CFAK') @testing.numpy_cupy_array_equal() - def test_array_copy_with_dtype_being_none(self, xp): + def test_array_copy_with_dtype_being_none(self, xp, order): a = testing.shaped_arange((2, 3, 4), xp) - return xp.array(a, dtype=None) + return xp.array(a, dtype=None, order=order) + @testing.for_orders('CFAK') @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() - def test_array_no_copy(self, xp, dtype): + def test_array_no_copy(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), xp, dtype) - b = xp.array(a, copy=False) + b = xp.array(a, copy=False, order=order) a.fill(0) return b + @testing.for_orders('CFAK') + @testing.for_all_dtypes() + @testing.numpy_cupy_array_equal() + def test_array_f_contiguous_input(self, xp, dtype, order): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + a = xp.asfortranarray(a) + b = xp.array(a, copy=False, order=order) + return b + + @testing.for_all_dtypes() + @testing.numpy_cupy_array_equal() + def test_array_f_contiguous_output(self, xp, dtype): + a = testing.shaped_arange((2, 3, 4), xp, dtype) + b = xp.array(a, copy=False, order='F') + self.assertTrue(b.flags.f_contiguous) + return b + @testing.multi_gpu(2) def test_array_multi_device(self): with cuda.Device(0): From ef7e406e6bacb4d8f0e2ecc6304223934ecc3200 Mon Sep 17 00:00:00 2001 From: Ryosuke Okuta Date: Thu, 27 Jul 2017 01:27:27 +0900 Subject: [PATCH 2/3] Fix contiguous check --- cupy/core/core.pyx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cupy/core/core.pyx b/cupy/core/core.pyx index 08eca84f732..d605fea7b1e 100644 --- a/cupy/core/core.pyx +++ b/cupy/core/core.pyx @@ -1551,10 +1551,13 @@ cdef class ndarray: arr.dtype, self.dtype)) if self.shape != arr.shape: raise ValueError('Shape mismatch') - if not self._c_contiguous: + if self._c_contiguous: + arr = numpy.ascontiguousarray(arr) + elif self._f_contiguous: + arr = numpy.asfortranarray(arr) + else: raise RuntimeError('Cannot set to non-contiguous array') - arr = numpy.ascontiguousarray(arr) ptr = arr.ctypes.get_as_parameter() if stream is None: self.data.copy_from_host(ptr, self.nbytes) From f9e91953d7fda0dafa4cc71f4d5d7b0260f5f8f3 Mon Sep 17 00:00:00 2001 From: Ryosuke Okuta Date: Mon, 31 Jul 2017 15:14:56 +0900 Subject: [PATCH 3/3] Add array order check --- cupy/testing/array.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cupy/testing/array.py b/cupy/testing/array.py index 23608f67de4..0e459c80c4a 100644 --- a/cupy/testing/array.py +++ b/cupy/testing/array.py @@ -89,6 +89,18 @@ def assert_array_equal(x, y, err_msg='', verbose=True): numpy.testing.assert_array_equal( cupy.asnumpy(x), cupy.asnumpy(y), err_msg=err_msg, verbose=verbose) + if (isinstance(x, (numpy.ndarray, cupy.ndarray)) and + isinstance(y, (numpy.ndarray, cupy.ndarray))): + if x.flags.c_contiguous != x.flags.c_contiguous: + raise AssertionError( + 'The state of c_contiguous flag is different. ' + '(x:{} y:{})'.format(x.flags.c_contiguous, + y.flags.c_contiguous)) + if x.flags.c_contiguous != x.flags.c_contiguous: + raise AssertionError( + 'The state of f_contiguous flag is different. ' + '(x:{} y:{})'.format(x.flags.f_contiguous, + y.flags.f_contiguous)) def assert_array_list_equal(xlist, ylist, err_msg='', verbose=True):