# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # pylint: skip-file from __future__ import absolute_import from __future__ import division import itertools import os import pytest import operator import numpy as _np import mxnet as mx from mxnet import np, npx, autograd from mxnet.gluon import HybridBlock from mxnet.test_utils import same, assert_almost_equal, rand_shape_nd, rand_ndarray, use_np from common import retry, TemporaryDirectory, xfail_when_nonstandard_decimal_separator from mxnet.test_utils import verify_generator, gen_buckets_probs_with_ppf, assert_exception, is_op_runnable, collapse_sum_like from mxnet.ndarray.ndarray import py_slice from mxnet.base import integer_types @use_np def test_np_empty(): # (input dtype, expected output dtype) dtype_pairs = [ (np.int8, np.int8), (np.int32, np.int32), (np.float16, np.float16), (np.float32, np.float32), (np.float64, np.float64), (np.bool_, np.bool_), (np.bool, np.bool_), ('int8', np.int8), ('int32', np.int32), ('float16', np.float16), ('float32', np.float32), ('float64', np.float64), ('bool', np.bool_), (None, np.float32), ] orders = ['C', 'F', 'A'] shapes = [ (), 0, (0,), (0, 0), 2, (2,), (3, 0), (4, 5), (1, 1, 1, 1), ] devices = [npx.current_device(), None] for dtype, expected_dtype in dtype_pairs: for shape in shapes: for order in orders: for device in devices: if order == 'C': ret = np.empty(shape, dtype=dtype, order=order, device=device) assert ret.dtype == expected_dtype assert ret.shape == shape if isinstance(shape, tuple) else (shape,) assert ret.device == npx.current_device() else: assert_exception(np.empty, NotImplementedError, shape, dtype=dtype, order=order, device=device) @use_np def test_np_array_creation(): dtypes = [_np.int8, _np.int32, _np.float16, _np.float32, _np.float64, _np.bool, _np.bool_, 'int8', 'int32', 'float16', 'float32', 'float64', 'bool', None] objects = [ [], (), [[1, 2], [3, 4]], _np.random.randint(-10, 10, size=rand_shape_nd(3)), _np.random.uniform(size=rand_shape_nd(3)), _np.random.uniform(size=(3, 0, 4)) ] for dtype in dtypes: for src in objects: mx_arr = np.array(src, dtype=dtype) assert mx_arr.device == mx.current_device() if dtype is None: dtype = src.dtype if isinstance(src, _np.ndarray) else _np.float32 if isinstance(src, mx.nd.NDArray): np_arr = _np.array(src.asnumpy(), dtype=dtype) else: np_arr = _np.array(src, dtype=dtype) assert mx_arr.dtype == np_arr.dtype assert same(mx_arr.asnumpy(), np_arr) @use_np @pytest.mark.serial def test_np_zeros(): # test np.zeros in Gluon class TestZeros(HybridBlock): def __init__(self, shape, dtype=None): super(TestZeros, self).__init__() self._shape = shape self._dtype = dtype def forward(self, x, *args, **kwargs): return x + np.zeros(shape, dtype=dtype) class TestZerosOutputType(HybridBlock): def forward(self, x, *args, **kwargs): return x, np.zeros(shape=()) # test np.zeros in imperative def check_zero_array_creation(shape, dtype): np_out = _np.zeros(shape=shape, dtype=dtype) mx_out = np.zeros(shape=shape, dtype=dtype) assert same(mx_out.asnumpy(), np_out) if dtype is None: assert mx_out.dtype == _np.float32 assert np_out.dtype == _np.float64 else: assert mx_out.dtype == np_out.dtype shapes = [(0,), (2, 0, 2), (0, 0, 0, 0), ()] shapes += [rand_shape_nd(ndim, allow_zero_size=True) for ndim in range(5)] dtypes = [_np.int8, _np.int32, _np.float16, _np.float32, _np.float64, None] for shape in shapes: for dtype in dtypes: check_zero_array_creation(shape, dtype) x = np.array(_np.random.uniform(size=shape), dtype=dtype) if dtype is None: x = x.astype('float32') for hybridize in [True, False]: test_zeros = TestZeros(shape, dtype) test_zeros_output_type = TestZerosOutputType() if hybridize: test_zeros.hybridize() test_zeros_output_type.hybridize() y = test_zeros(x) assert type(y) == np.ndarray assert same(x.asnumpy(), y.asnumpy()) y = test_zeros_output_type(x) assert type(y[1]) == np.ndarray for shape in shapes: for dtype in [_np.bool, bool, _np.bool, 'bool']: check_zero_array_creation(shape, dtype) @use_np def test_np_ones(): # test np.ones in Gluon class TestOnes(HybridBlock): def __init__(self, shape, dtype=None): super(TestOnes, self).__init__() self._shape = shape self._dtype = dtype def forward(self, x, *args, **kwargs): return x * np.ones(shape, dtype=dtype) class TestOnesOutputType(HybridBlock): def forward(self, x, *args, **kwargs): return x, np.ones(shape=()) # test np.ones in imperative def check_ones_array_creation(shape, dtype): np_out = _np.ones(shape=shape, dtype=dtype) mx_out = np.ones(shape=shape, dtype=dtype) assert same(mx_out.asnumpy(), np_out) if dtype is None: assert mx_out.dtype == _np.float32 assert np_out.dtype == _np.float64 else: assert mx_out.dtype == np_out.dtype shapes = [(0,), (2, 0, 2), (0, 0, 0, 0), ()] shapes += [rand_shape_nd(ndim, allow_zero_size=True) for ndim in range(5)] dtypes = [_np.int8, _np.int32, _np.float16, _np.float32, _np.float64, None] for shape in shapes: for dtype in dtypes: check_ones_array_creation(shape, dtype) x = mx.nd.array(_np.random.uniform(size=shape), dtype=dtype).as_np_ndarray() if dtype is None: x = x.astype('float32') for hybridize in [True, False]: test_ones = TestOnes(shape, dtype) test_ones_output_type = TestOnesOutputType() if hybridize: test_ones.hybridize() test_ones_output_type.hybridize() y = test_ones(x) assert type(y) == np.ndarray assert same(x.asnumpy(), y.asnumpy()) y = test_ones_output_type(x) assert type(y[1]) == np.ndarray for shape in shapes: for dtype in [_np.bool, bool, _np.bool, 'bool']: check_ones_array_creation(shape, dtype) @use_np @pytest.mark.serial def test_identity(): class TestIdentity(HybridBlock): def __init__(self, shape, dtype=None): super(TestIdentity, self).__init__() self._n = n self._dtype = dtype def forward(self, x): return x * np.identity(self._n, self._dtype) class TestIdentityOutputType(HybridBlock): def forward(self, x): return x, np.identity(0) def check_identity_array_creation(shape, dtype): np_out = _np.identity(n=n, dtype=dtype) mx_out = np.identity(n=n, dtype=dtype) assert same(mx_out.asnumpy(), np_out) if dtype is None: assert mx_out.dtype == _np.float32 assert np_out.dtype == _np.float64 ns = [0, 1, 2, 3, 5, 15, 30, 200] dtypes = [_np.int8, _np.int32, _np.float16, _np.float32, _np.float64, None] for n in ns: for dtype in dtypes: check_identity_array_creation(n, dtype) x = mx.nd.array(_np.random.uniform(size=(n, n)), dtype=dtype).as_np_ndarray() if dtype is None: x = x.astype('float32') for hybridize in [True, False]: test_identity = TestIdentity(n, dtype) test_identity_output_type = TestIdentityOutputType() if hybridize: test_identity.hybridize() test_identity_output_type.hybridize() y = test_identity(x) assert type(y) == np.ndarray assert same(x.asnumpy() * _np.identity(n, dtype), y.asnumpy()) y = test_identity_output_type(x) assert type(y[1]) == np.ndarray @xfail_when_nonstandard_decimal_separator @pytest.mark.serial def test_np_ndarray_binary_element_wise_ops(): np_op_map = { '+': _np.add, '*': _np.multiply, '-': _np.subtract, '/': _np.divide, 'mod': _np.mod, 'pow': _np.power, } if is_op_runnable(): np_op_map.update({ '==': _np.equal, '!=': _np.not_equal, '>': _np.greater, '>=': _np.greater_equal, '<': _np.less, '<=': _np.less_equal }) def _get_grad_func(op, scalar=None, reverse=False): if op == '+': if scalar is None: return lambda ograd, x1, x2, out: (collapse_sum_like(ograd, x1.shape), collapse_sum_like(ograd, x2.shape)) elif not reverse: return lambda ograd, x1, x2, out: ograd else: return lambda ograd, x1, x2, out: ograd elif op == '-': if scalar is None: return lambda ograd, x1, x2, out: (collapse_sum_like(ograd, x1.shape), -collapse_sum_like(ograd, x2.shape)) elif not reverse: return lambda ograd, x1, x2, out: ograd else: return lambda ograd, x1, x2, out: -ograd elif op == '*': if scalar is None: return lambda ograd, x1, x2, out: (collapse_sum_like(ograd * x2, x1.shape), collapse_sum_like(ograd * x1, x2.shape)) elif not reverse: return lambda ograd, x1, x2, out: ograd * x2 else: return lambda ograd, x1, x2, out: ograd * x1 elif op == '/': if scalar is None: return lambda ograd, x1, x2, out: (collapse_sum_like(ograd / x2, x1.shape), collapse_sum_like(-x1 * ograd / (x2 * x2), x2.shape)) elif not reverse: return lambda ograd, x1, x2, out: ograd / x2 else: return lambda ograd, x1, x2, out: -x1 * ograd / (x2 * x2) elif op == 'mod': if scalar is None: return lambda ograd, x1, x2, out: (collapse_sum_like(ograd, x1.shape), collapse_sum_like(-ograd * _np.floor(x1 / x2), x2.shape)) elif not reverse: return lambda ograd, x1, x2, out: ograd else: return lambda ograd, x1, x2, out: -ograd * _np.floor(x1 / x2) elif op == 'pow': if scalar is None: return lambda ograd, x1, x2, out: (collapse_sum_like(ograd * x2 * _np.power(x1, x2 - 1), x1.shape), collapse_sum_like(ograd * out * _np.log(x1), x2.shape)) elif not reverse: return lambda ograd, x1, x2, out: ograd * x2 * _np.power(x1, x2 - 1) else: return lambda ograd, x1, x2, out: ograd * out * _np.log(x1) elif op in ('==', '!=', '<', '<=', '>', '>='): if scalar is None: return lambda ograd, x1, x2, out: (_np.zeros_like(x1), _np.zeros_like(x2)) else: return lambda ograd, x1, x2, out: _np.zeros_like(ograd) return None def get_np_ret(x1, x2, op): return np_op_map[op](x1, x2) @use_np class TestBinaryElementWiseOp(HybridBlock): def __init__(self, op, scalar=None, reverse=False): super(TestBinaryElementWiseOp, self).__init__() self._op = op self._scalar = scalar self._reverse = reverse # if false, scalar is the right operand. def forward(self, x, *args): if self._op == '+': if self._scalar is not None: return x + self._scalar if not self._reverse else self._scalar + x else: return x + args[0] if not self._reverse else args[0] + x elif self._op == '*': if self._scalar is not None: return x * self._scalar if not self._reverse else self._scalar * x else: return x * args[0] if not self._reverse else args[0] * x elif self._op == '-': if self._scalar is not None: return x - self._scalar if not self._reverse else self._scalar - x else: return x - args[0] if not self._reverse else args[0] - x elif self._op == '/': if self._scalar is not None: return x / self._scalar if not self._reverse else self._scalar / x else: return x / args[0] if not self._reverse else args[0] / x elif self._op == 'mod': if self._scalar is not None: return x % self._scalar if not self._reverse else self._scalar % x else: return x % args[0] if not self._reverse else args[0] % x elif self._op == 'pow': if self._scalar is not None: return x ** self._scalar if not self._reverse else self._scalar ** x else: return x ** args[0] if not self._reverse else args[0] ** x elif self._op == '>': if self._scalar is not None: return x > self._scalar if not self._reverse else self._scalar > x else: return x > args[0] elif self._op == '>=': if self._scalar is not None: return x >= self._scalar if not self._reverse else self._scalar >= x else: return x >= args[0] elif self._op == '<': if self._scalar is not None: return x < self._scalar if not self._reverse else self._scalar < x else: return x < args[0] elif self._op == '<=': if self._scalar is not None: return x <= self._scalar if not self._reverse else self._scalar <= x else: return x <= args[0] elif self._op == '==': if self._scalar is not None: return x == self._scalar if not self._reverse else self._scalar == x else: return x == args[0] elif self._op == '!=': if self._scalar is not None: return x != self._scalar if not self._reverse else self._scalar != x else: return x != args[0] else: print(self._op) assert False logic_ops = ['==', '!=', '>', '<', '>=', '<='] @use_np def check_binary_op_result(shape1, shape2, op, dtype=None): if shape1 is None: mx_input1 = abs(_np.random.uniform()) + 1 np_input1 = mx_input1 else: mx_input1 = (rand_ndarray(shape1, dtype=dtype).abs() + 1).as_np_ndarray() mx_input1.attach_grad() np_input1 = mx_input1.asnumpy() if shape2 is None: mx_input2 = abs(_np.random.uniform()) + 1 np_input2 = mx_input2 else: mx_input2 = (rand_ndarray(shape2, dtype=dtype).abs() + 1).as_np_ndarray() mx_input2.attach_grad() np_input2 = mx_input2.asnumpy() scalar = None reverse = False if isinstance(mx_input1, mx.nd.NDArray) and not isinstance(mx_input2, mx.nd.NDArray): scalar = mx_input2 reverse = False elif isinstance(mx_input2, mx.nd.NDArray) and not isinstance(mx_input1, mx.nd.NDArray): scalar = mx_input1 reverse = True grad_func = _get_grad_func(op, scalar, reverse) np_out = get_np_ret(np_input1, np_input2, op) ograd = _np.ones_like(np_out) for hybridize in [True, False]: if scalar is None: get_mx_ret_np = TestBinaryElementWiseOp(op) get_mx_ret_classic = TestBinaryElementWiseOp(op) if hybridize: get_mx_ret_np.hybridize() get_mx_ret_classic.hybridize() if grad_func is None: mx_out = get_mx_ret_np(mx_input1, mx_input2) else: with mx.autograd.record(): mx_out = get_mx_ret_np(mx_input1, mx_input2) mx_out.backward() assert type(mx_out) == np.ndarray if op in logic_ops: assert np_out.dtype == mx_out.dtype assert_almost_equal(mx_out.asnumpy(), np_out, atol=1e-6, rtol=1e-5, use_broadcast=False) if grad_func is not None: x1_grad_expected, x2_grad_expected = grad_func(ograd, np_input1, np_input2, np_out) assert_almost_equal(mx_input1.grad.asnumpy(), x1_grad_expected, atol=1e-5, rtol=1e-3, use_broadcast=False) assert_almost_equal(mx_input2.grad.asnumpy(), x2_grad_expected, atol=1e-5, rtol=1e-3, use_broadcast=False) else: get_mx_ret = TestBinaryElementWiseOp(op, scalar=scalar, reverse=reverse) if hybridize: get_mx_ret.hybridize() if reverse: mx_input = mx_input2 else: mx_input = mx_input1 if grad_func is None: mx_out = get_mx_ret(mx_input) else: with mx.autograd.record(): mx_out = get_mx_ret(mx_input) mx_out.backward() assert type(mx_out) == np.ndarray if op in logic_ops: assert np_out.dtype == mx_out.dtype assert_almost_equal(mx_out.asnumpy(), np_out, atol=1e-6, rtol=1e-5, use_broadcast=False) # check grad if grad_func is not None: x_grad_expected = grad_func(ograd, np_input1, np_input2, np_out) assert_almost_equal(mx_input.grad.asnumpy(), x_grad_expected, atol=1e-5, rtol=1e-3, use_broadcast=False) dtypes = [_np.float32, _np.float64, None] ops = np_op_map.keys() for dtype in dtypes: for op in ops: check_binary_op_result((3, 4), (3, 4), op, dtype) check_binary_op_result(None, (3, 4), op, dtype) check_binary_op_result((3, 4), None, op, dtype) check_binary_op_result((1, 4), (3, 1), op, dtype) check_binary_op_result(None, (3, 1), op, dtype) check_binary_op_result((1, 4), None, op, dtype) check_binary_op_result((1, 4), (3, 5, 4), op, dtype) check_binary_op_result((), (3, 5, 4), op, dtype) check_binary_op_result((), None, op, dtype) check_binary_op_result(None, (), op, dtype) check_binary_op_result((0, 2), (1, 1), op, dtype) check_binary_op_result((0, 2), None, op, dtype) check_binary_op_result(None, (0, 2), op, dtype) @pytest.mark.serial def test_np_hybrid_block_multiple_outputs(): @use_np class TestAllNumpyOutputs(HybridBlock): def forward(self, x, *args, **kwargs): return np.add(x, x), np.multiply(x, x) data_np = np.ones((2, 3)) block, expected_out_type = TestAllNumpyOutputs, np.ndarray net = block() for hybridize in [True, False]: if hybridize: net.hybridize(active=hybridize) out1, out2 = net(data_np) assert type(out1) is expected_out_type assert type(out2) is expected_out_type @use_np class TestMixedTypeOutputsFailure(HybridBlock): def forward(self, x, *args, **kwargs): return x.as_nd_ndarray() + x.as_nd_ndarray(), np.multiply(x, x) net = TestMixedTypeOutputsFailure() assert_exception(net, TypeError, data_np) net.hybridize() assert_exception(net, TypeError, data_np) @use_np def test_np_grad_ndarray_type(): data = np.array(2, dtype=_np.float32) data.attach_grad() assert type(data.grad) == np.ndarray assert type(data.detach()) == np.ndarray @use_np @pytest.mark.serial def test_np_ndarray_astype(): class TestAstype(HybridBlock): def __init__(self, dtype, copy): super(TestAstype, self).__init__() self._dtype = dtype self._copy = copy def forward(self, x): return x.astype(dtype=self._dtype, copy=self._copy) def check_astype_equal(itype, otype, copy, expect_zero_copy=False, hybridize=False): expect_zero_copy = copy is False and itype == otype mx_data = np.array([2, 3, 4, 5], dtype=itype) np_data = mx_data.asnumpy() test_astype = TestAstype(otype, copy) if hybridize: test_astype.hybridize() mx_ret = test_astype(mx_data) assert type(mx_ret) is np.ndarray np_ret = np_data.astype(dtype=otype, copy=copy) assert mx_ret.dtype == np_ret.dtype assert same(mx_ret.asnumpy(), np_ret) if expect_zero_copy and not hybridize: assert id(mx_ret) == id(mx_data) assert id(np_ret) == id(np_data) dtypes = [np.int8, np.uint8, np.int32, np.float16, np.float32, np.float64, np.bool, np.bool_, 'int8', 'uint8', 'int32', 'float16', 'float32', 'float64', 'bool'] for itype, otype in itertools.product(dtypes, dtypes): for copy in [True, False]: for hybridize in [True, False]: check_astype_equal(itype, otype, copy, hybridize) def test_np_ndarray_copy(): mx_data = np.array([2, 3, 4, 5], dtype=_np.int32) assert_exception(mx_data.copy, NotImplementedError, order='F') mx_ret = mx_data.copy() np_ret = mx_data.asnumpy().copy() assert same(mx_ret.asnumpy(), np_ret) def test_formatting(): def test_0d(): a = np.array(np.pi) _a = a.asnumpy() assert '{:0.3g}'.format(a) == '{:0.3g}'.format(_a) assert '{:0.3g}'.format(a[()]) == '{:0.3g}'.format(_a[()]) def test_nd_format(): a = np.array([np.pi]) assert_exception('{:30}'.format, TypeError, a) def test_nd_no_format(): a = np.array([np.pi]) _a = a.asnumpy() assert '{}'.format(a) == '{}'.format(_a) b = np.arange(8).reshape(2,2,2) assert '{}'.format(a) == '{}'.format(_a) device = mx.device.current_device() if str(device)[:3] != 'gpu': test_0d() test_nd_format() test_nd_no_format() # if the program is running in GPU, the formatted string would be appended with device notation # for exmpale, if a = np.array([np.pi]), the return value of '{}'.format(a) is '[3.1415927] @gpu(0)' @use_np @pytest.mark.serial def test_np_ndarray_indexing(): def np_int(index, int_type=_np.int32): """ Helper function for testing indexing that converts slices to slices of ints or None, and tuples to tuples of ints or None. """ def convert(num): if num is None: return num else: return int_type(num) if isinstance(index, slice): return slice(convert(index.start), convert(index.stop), convert(index.step)) elif isinstance(index, tuple): # tuple of slices and integers ret = [] for elem in index: if isinstance(elem, slice): ret.append(slice(convert(elem.start), convert(elem.stop), convert(elem.step))) else: ret.append(convert(elem)) return tuple(ret) else: assert False # Copied from test_ndarray.py. Under construction. def test_getitem(np_array, index): np_index = index if type(index) == mx.nd.NDArray: # use of NDArray is prohibited assert False if isinstance(index, np.ndarray): np_index = index.asnumpy() if isinstance(index, tuple): np_index = tuple([ idx.asnumpy() if isinstance(idx, mx.nd.NDArray) else idx for idx in index] ) np_indexed_array = np_array[np_index] mx_np_array = np.array(np_array, dtype=np_array.dtype) for autograd in [True, False]: try: if autograd: with mx.autograd.record(): mx_indexed_array = mx_np_array[index] else: mx_indexed_array = mx_np_array[index] except Exception as e: print('Failed with index = {}'.format(index)) raise e mx_indexed_array = mx_indexed_array.asnumpy() assert same(np_indexed_array, mx_indexed_array), 'Failed with index = {}'.format(index) def test_getitem_slice_bound(): mx_array = np.arange(10) np_array = mx_array.asnumpy() assert_almost_equal(mx_array[100:], np_array[100:]) assert_almost_equal(mx_array[:100], np_array[:100]) assert_almost_equal(mx_array[-100:], np_array[-100:]) assert_almost_equal(mx_array[:-100], np_array[:-100]) mx_array = np.arange(81).reshape(3, 3, 3, 3) np_array = mx_array.asnumpy() assert_almost_equal(mx_array[100:], np_array[100:]) assert_almost_equal(mx_array[:100], np_array[:100]) assert_almost_equal(mx_array[-100:], np_array[-100:]) assert_almost_equal(mx_array[:-100], np_array[:-100]) def test_setitem(np_array, index): def assert_same(np_array, np_index, mx_array, mx_index, mx_value, np_value=None): if np_value is not None: np_array[np_index] = np_value elif isinstance(mx_value, np.ndarray): np_array[np_index] = mx_value.asnumpy() else: np_array[np_index] = mx_value try: mx_array[mx_index] = mx_value except Exception as e: print('Failed with index = {}, value.shape = {}'.format(mx_index, mx_value.shape)) raise e assert same(np_array, mx_array.asnumpy()) def _is_basic_index(index): if isinstance(index, (integer_types, py_slice)): return True if isinstance(index, tuple) and all(isinstance(i, (integer_types, py_slice)) for i in index): return True return False np_index = index # keep this native numpy type if isinstance(index, np.ndarray): np_index = index.asnumpy() if isinstance(index, tuple): np_index = [] for idx in index: if isinstance(idx, np.ndarray): np_index.append(idx.asnumpy()) else: np_index.append(idx) np_index = tuple(np_index) mx_array = np.array(np_array, dtype=np_array.dtype) # mxnet.np.ndarray np_array = mx_array.asnumpy() # native numpy array indexed_array_shape = np_array[np_index].shape np_indexed_array = _np.random.randint(low=-10000, high=0, size=indexed_array_shape) # test value is a native numpy array without broadcast assert_same(np_array, np_index, mx_array, index, np_indexed_array) # test value is a list without broadcast assert_same(np_array, np_index, mx_array, index, np_indexed_array.tolist()) # test value is a mxnet numpy array without broadcast assert_same(np_array, np_index, mx_array, index, np.array(np_indexed_array)) # test value is an numeric_type assert_same(np_array, np_index, mx_array, index, _np.random.randint(low=-10000, high=0)) np_value = _np.random.randint(low=-10000, high=0, size=(indexed_array_shape[-1],) if len(indexed_array_shape) > 0 else ()) # test mxnet ndarray with broadcast assert_same(np_array, np_index, mx_array, index, np.array(np_value)) # test native numpy array with broadcast assert_same(np_array, np_index, mx_array, index, np_value) # test python list with broadcast assert_same(np_array, np_index, mx_array, index, np_value.tolist()) # test value shape are expanded to be longer than index array's shape # this is currently only supported in basic indexing if _is_basic_index(index): expanded_value_shape = (1, 1) + np_value.shape assert_same(np_array, np_index, mx_array, index, np.array(np_value.reshape(expanded_value_shape))) assert_same(np_array, np_index, mx_array, index, np_value.reshape(expanded_value_shape)) if len(expanded_value_shape) <= np_array[index].ndim: # NumPy does not allow value.ndim > np_array[index].ndim when value is a python list. # It may be a bug of NumPy. assert_same(np_array, np_index, mx_array, index, np_value.reshape(expanded_value_shape).tolist()) # test list with broadcast assert_same(np_array, np_index, mx_array, index, [_np.random.randint(low=-10000, high=0)] * indexed_array_shape[-1] if len(indexed_array_shape) > 0 else _np.random.randint(low=-10000, high=0)) def test_getitem_autograd(np_array, index): """ np_array: native numpy array. """ x = np.array(np_array, dtype=np_array.dtype) x.attach_grad() with mx.autograd.record(): y = x[index] y.backward() value = np.ones_like(y) x_grad = np.zeros_like(x) x_grad[index] = value assert same(x_grad.asnumpy(), x.grad.asnumpy()) def test_setitem_autograd(np_array, index): """ np_array: native numpy array. """ x = np.array(np_array, dtype=np_array.dtype) out_shape = x[index].shape y = np.array(_np.random.uniform(size=out_shape)) y.attach_grad() try: with mx.autograd.record(): x[index] = y x.backward() y_grad = np.ones_like(y) assert same(y_grad.asnumpy(), y.grad.asnumpy()) except mx.base.MXNetError as err: assert str(err).find('Inplace operations (+=, -=, x[:]=, etc) are not supported when recording with') != -1 shape = (8, 16, 9, 9) np_array = _np.arange(_np.prod(_np.array(shape)), dtype='int32').reshape(shape) # native np array # Test sliced output being ndarray: index_list = [ (), # Basic indexing # Single int as index 0, _np.int32(0), _np.int64(0), np.array(0, dtype='int32'), np.array(0, dtype='int64'), 5, _np.int32(5), _np.int64(5), np.array(5, dtype='int32'), np.array(5, dtype='int64'), -1, _np.int32(-1), _np.int64(-1), np.array(-1, dtype='int32'), np.array(-1, dtype='int64'), # Slicing as index slice(5), np_int(slice(5), _np.int32), np_int(slice(5), _np.int64), slice(1, 5), np_int(slice(1, 5), _np.int32), np_int(slice(1, 5), _np.int64), slice(1, 5, 2), slice(1, 2, 2), np_int(slice(1, 5, 2), _np.int32), np_int(slice(1, 5, 2), _np.int64), slice(7, 0, -1), np_int(slice(7, 0, -1)), np_int(slice(7, 0, -1), _np.int64), slice(None, 6), np_int(slice(None, 6)), np_int(slice(None, 6), _np.int64), slice(None, 6, 3), np_int(slice(None, 6, 3)), np_int(slice(None, 6, 3), _np.int64), slice(1, None), np_int(slice(1, None)), np_int(slice(1, None), _np.int64), slice(1, None, 3), np_int(slice(1, None, 3)), np_int(slice(1, None, 3), _np.int64), slice(None, None, 2), np_int(slice(None, None, 2)), np_int(slice(None, None, 2), _np.int64), slice(None, None, -1), np_int(slice(None, None, -1)), np_int(slice(None, None, -1), _np.int64), slice(None, None, -2), np_int(slice(None, None, -2), _np.int32), np_int(slice(None, None, -2), _np.int64), # Multiple ints as indices (1, 2, 3), np_int((1, 2, 3)), np_int((1, 2, 3), _np.int64), (-1, -2, -3), np_int((-1, -2, -3)), np_int((-1, -2, -3), _np.int64), (1, 2, 3, 4), np_int((1, 2, 3, 4)), np_int((1, 2, 3, 4), _np.int64), (-4, -3, -2, -1), (-4, mx.np.array(-3, dtype='int32'), -2, -1), (-4, mx.np.array(-3, dtype='int64'), -2, -1), np_int((-4, -3, -2, -1)), np_int((-4, -3, -2, -1), _np.int64), # slice(None) as indices (slice(None), slice(None), 1, 8), (slice(None), slice(None), np.array(1, dtype='int32'), 8), (slice(None), slice(None), np.array(1, dtype='int64'), 8), (slice(None), slice(None), -1, 8), (slice(None), slice(None), 1, -8), (slice(None), slice(None), -1, -8), np_int((slice(None), slice(None), 1, 8)), np_int((slice(None), slice(None), 1, 8), _np.int64), (slice(None), slice(None), 1, 8), np_int((slice(None), slice(None), -1, -8)), np_int((slice(None), slice(None), -1, -8), _np.int64), (slice(None), 2, slice(1, 5), 1), np_int((slice(None), 2, slice(1, 5), 1)), np_int((slice(None), 2, slice(1, 5), 1), _np.int64), # Mixture of ints and slices as indices (slice(None, None, -1), 2, slice(1, 5), 1), np_int((slice(None, None, -1), 2, slice(1, 5), 1)), np_int((slice(None, None, -1), 2, slice(1, 5), 1), _np.int64), (slice(None, None, -1), 2, slice(1, 7, 2), 1), np_int((slice(None, None, -1), 2, slice(1, 7, 2), 1)), np_int((slice(None, None, -1), 2, slice(1, 7, 2), 1), _np.int64), (slice(1, 8, 2), slice(14, 2, -2), slice(3, 8), slice(0, 7, 3)), np_int((slice(1, 8, 2), slice(14, 2, -2), slice(3, 8), slice(0, 7, 3))), np_int((slice(1, 8, 2), slice(14, 2, -2), slice(3, 8), slice(0, 7, 3)), _np.int64), (slice(1, 8, 2), 1, slice(3, 8), 2), np_int((slice(1, 8, 2), 1, slice(3, 8), 2)), np_int((slice(1, 8, 2), 1, slice(3, 8), 2), _np.int64), # Test Ellipsis ('...') (1, Ellipsis, -1), (slice(2), Ellipsis, None, 0), # Test newaxis None, (1, None, -2, 3, -4), (1, slice(2, 5), None), (slice(None), slice(1, 4), None, slice(2, 3)), (slice(1, 3), slice(1, 3), slice(1, 3), slice(1, 3), None), (slice(1, 3), slice(1, 3), None, slice(1, 3), slice(1, 3)), (None, slice(1, 2), 3, None), (1, None, 2, 3, None, None, 4), # Advanced indexing ([1, 2], slice(3, 5), None, None, [3, 4]), (slice(None), slice(3, 5), None, None, [2, 3], [3, 4]), (slice(None), slice(3, 5), None, [2, 3], None, [3, 4]), (None, slice(None), slice(3, 5), [2, 3], None, [3, 4]), [1], [1, 2], [2, 1, 3], [7, 5, 0, 3, 6, 2, 1], np.array([6, 3], dtype=np.int32), np.array([[3, 4], [0, 6]], dtype=np.int32), np.array([[7, 3], [2, 6], [0, 5], [4, 1]], dtype=np.int32), np.array([[7, 3], [2, 6], [0, 5], [4, 1]], dtype=np.int64), np.array([[2], [0], [1]], dtype=np.int32), np.array([[2], [0], [1]], dtype=np.int64), np.array([4, 7], dtype=np.int32), np.array([4, 7], dtype=np.int64), np.array([[3, 6], [2, 1]], dtype=np.int32), np.array([[3, 6], [2, 1]], dtype=np.int64), np.array([[7, 3], [2, 6], [0, 5], [4, 1]], dtype=np.int32), np.array([[7, 3], [2, 6], [0, 5], [4, 1]], dtype=np.int64), (1, [2, 3]), (1, [2, 3], np.array([[3], [0]], dtype=np.int32)), (1, [2, 3]), (1, [2, 3], np.array([[3], [0]], dtype=np.int64)), (1, [2], np.array([[5], [3]], dtype=np.int32), slice(None)), (1, [2], np.array([[5], [3]], dtype=np.int64), slice(None)), (1, [2, 3], np.array([[6], [0]], dtype=np.int32), slice(2, 5)), (1, [2, 3], np.array([[6], [0]], dtype=np.int64), slice(2, 5)), (1, [2, 3], np.array([[4], [7]], dtype=np.int32), slice(2, 5, 2)), (1, [2, 3], np.array([[4], [7]], dtype=np.int64), slice(2, 5, 2)), (1, [2], np.array([[3]], dtype=np.int32), slice(None, None, -1)), (1, [2], np.array([[3]], dtype=np.int64), slice(None, None, -1)), (1, [2], np.array([[3]], dtype=np.int32), np.array([[5, 7], [2, 4]], dtype=np.int64)), (1, [2], np.array([[4]], dtype=np.int32), np.array([[1, 3], [5, 7]], dtype='int64')), [0], [0, 1], [1, 2, 3], [2, 0, 5, 6], ([1, 1], [2, 3]), ([1], [4], [5]), ([1], [4], [5], [6]), ([[1]], [[2]]), ([[1]], [[2]], [[3]], [[4]]), (slice(0, 2), [[1], [6]], slice(0, 2), slice(0, 5, 2)), ([[[[1]]]], [[1]], slice(0, 3), [1, 5]), ([[[[1]]]], 3, slice(0, 3), [1, 3]), ([[[[1]]]], 3, slice(0, 3), 0), ([[[[1]]]], [[2], [12]], slice(0, 3), slice(None)), ([1, 2], slice(3, 5), [2, 3], [3, 4]), ([1, 2], slice(3, 5), (2, 3), [3, 4]), range(4), range(3, 0, -1), (range(4,), [1]), (1, 1, slice(None), 1), (1, 1, slice(None, 3), 1), (1, 1, slice(None, 8, 3), 1), ] for index in index_list: test_getitem(np_array, index) test_setitem(np_array, index) test_getitem_autograd(np_array, index) test_setitem_autograd(np_array, index) # Test indexing to zero-size tensors index_list = [ (slice(0, 0), slice(0, 0), 1, 2), (slice(0, 0), slice(0, 0), slice(0, 0), slice(0, 0)), ] for index in index_list: test_getitem(np_array, index) test_setitem(np_array, index) test_getitem_autograd(np_array, index) test_setitem_autograd(np_array, index) # test zero-size tensors get and setitem shapes_indices = [ ((0), [slice(None, None, None)]), ((3, 0), [2, (slice(None, None, None)), (slice(None, None, None), None)]), ] for shape, indices in shapes_indices: np_array = _np.zeros(shape) for index in indices: test_getitem(np_array, index) test_setitem(np_array, index) test_getitem_autograd(np_array, index) test_setitem_autograd(np_array, index) test_getitem_slice_bound() @use_np @pytest.mark.parametrize('load_fn', [_np.load, npx.load]) def test_np_save_load_large_ndarrays(load_fn, tmp_path): weight = mx.np.arange(32768 * 512).reshape((32768, 512)) mx.npx.savez(str(tmp_path / 'params.npz'), weight=weight) arr_loaded = load_fn(str(tmp_path / 'params.npz'))['weight'] assert _np.array_equal(arr_loaded.asnumpy() if load_fn is npx.load else arr_loaded, weight) @use_np @pytest.mark.serial @pytest.mark.parametrize('load_fn', [_np.load, npx.load]) def test_np_save_load_ndarrays(load_fn): shapes = [(2, 0, 1), (0,), (), (), (0, 4), (), (3, 0, 0, 0), (2, 1), (0, 5, 0), (4, 5, 6), (0, 0, 0)] array_list = [_np.random.randint(0, 10, size=shape) for shape in shapes] array_list = [np.array(arr, dtype=arr.dtype) for arr in array_list] # test save/load single ndarray to npy format for i, arr in enumerate(array_list): with TemporaryDirectory() as work_dir: fname = os.path.join(work_dir, 'dataset.npy') npx.save(fname, arr) arr_loaded = load_fn(fname) assert _np.array_equal(arr_loaded.asnumpy() if load_fn is npx.load else arr_loaded, array_list[i].asnumpy()) # test save/load a list of ndarrays with TemporaryDirectory() as work_dir: fname = os.path.join(work_dir, 'dataset.npz') npx.savez(fname, *array_list) if load_fn is _np.load: with load_fn(fname) as array_dict_loaded: # Ensure NPZFile is closed array_list_loaded = [ array_dict_loaded['arr_{}'.format(str(i))] for i in range(len(array_dict_loaded)) ] else: array_dict_loaded = load_fn(fname) array_list_loaded = [ array_dict_loaded['arr_{}'.format(str(i))] for i in range(len(array_dict_loaded)) ] assert len(array_list) == len(array_list_loaded) assert all(isinstance(arr, np.ndarray) for arr in arr_loaded) for a1, a2 in zip(array_list, array_list_loaded): assert _np.array_equal(a1.asnumpy(), a2.asnumpy() if load_fn is npx.load else a2) # test save/load a dict of str->ndarray arr_dict = {} keys = [str(i) for i in range(len(array_list))] for k, v in zip(keys, array_list): arr_dict[k] = v with TemporaryDirectory() as work_dir: fname = os.path.join(work_dir, 'dataset.npz') npx.savez(fname, **arr_dict) if load_fn is _np.load: with load_fn(fname) as arr_dict_loaded: # Ensure NPZFile is closed assert isinstance(arr_dict_loaded, _np.lib.npyio.NpzFile) assert len(arr_dict_loaded) == len(arr_dict) for k, v in arr_dict_loaded.items(): assert k in arr_dict assert _np.array_equal(v.asnumpy() if load_fn is npx.load else v, arr_dict[k].asnumpy()) else: arr_dict_loaded = load_fn(fname) assert isinstance(arr_dict_loaded, dict) assert len(arr_dict_loaded) == len(arr_dict) for k, v in arr_dict_loaded.items(): assert k in arr_dict assert _np.array_equal(v.asnumpy() if load_fn is npx.load else v, arr_dict[k].asnumpy()) @retry(5) @use_np @pytest.mark.serial def test_np_multinomial(): pvals_list = [[0.0, 0.1, 0.2, 0.3, 0.4], [0.4, 0.3, 0.2, 0.1, 0.0]] sizes = [None, (), (3,), (2, 5, 7), (4, 9)] experiements = 10000 for pvals_mx_np_array in [False, True]: for have_size in [False, True]: for pvals in pvals_list: if pvals_mx_np_array: pvals = mx.np.array(pvals) if have_size: for size in sizes: freq = mx.np.random.multinomial(experiements, pvals, size=size).asnumpy() / _np.float32(experiements) # for those cases that didn't need reshape if size in [None, ()]: if type(pvals) == np.ndarray: mx.test_utils.assert_almost_equal(freq, pvals.asnumpy(), rtol=0.20, atol=1e-1) else: mx.test_utils.assert_almost_equal(freq, pvals, rtol=0.20, atol=1e-1) else: # check the shape assert freq.shape == size + (len(pvals),), 'freq.shape={}, size + (len(pvals))={}'.format(freq.shape, size + (len(pvals))) freq = freq.reshape((-1, len(pvals))) # check the value for each row for i in range(freq.shape[0]): if type(pvals) == np.ndarray: mx.test_utils.assert_almost_equal(freq[i, :], pvals.asnumpy(), rtol=0.20, atol=1e-1) else: mx.test_utils.assert_almost_equal(freq[i, :], pvals, rtol=0.20, atol=1e-1) else: freq = mx.np.random.multinomial(experiements, pvals).asnumpy() / _np.float32(experiements) if type(pvals) == np.ndarray: mx.test_utils.assert_almost_equal(freq, pvals.asnumpy(), rtol=0.20, atol=1e-1) else: mx.test_utils.assert_almost_equal(freq, pvals, rtol=0.20, atol=1e-1) # check the zero dimension sizes = [(0), (0, 2), (4, 0, 2), (3, 0, 1, 2, 0)] for pvals_mx_np_array in [False, True]: for pvals in pvals_list: for size in sizes: if pvals_mx_np_array: pvals = mx.np.array(pvals) freq = mx.np.random.multinomial(experiements, pvals, size=size).asnumpy() assert freq.size == 0 # check [] as pvals for pvals_mx_np_array in [False, True]: for pvals in [[], ()]: if pvals_mx_np_array: pvals = mx.np.array(pvals) freq = mx.np.random.multinomial(experiements, pvals).asnumpy() assert freq.size == 0 for size in sizes: freq = mx.np.random.multinomial(experiements, pvals, size=size).asnumpy() assert freq.size == 0 # test small experiment for github issue # https://github.com/apache/mxnet/issues/15383 small_exp, total_exp = 20, 10000 for pvals_mx_np_array in [False, True]: for pvals in pvals_list: if pvals_mx_np_array: pvals = mx.np.array(pvals) x = np.random.multinomial(small_exp, pvals) for _ in range(total_exp // small_exp): x = x + np.random.multinomial(20, pvals) freq = (x.asnumpy() / _np.float32(total_exp)).reshape((-1, len(pvals))) for i in range(freq.shape[0]): if type(pvals) == np.ndarray: mx.test_utils.assert_almost_equal(freq[i, :], pvals.asnumpy(), rtol=0.20, atol=1e-1) else: mx.test_utils.assert_almost_equal(freq[i, :], pvals, rtol=0.20, atol=1e-1) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np def test_boolean_index_single(): # adapted from numpy's test_indexing.py # Single boolean index a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.int32) assert same(a[np.array(True, dtype=np.bool_)].asnumpy(), a[None].asnumpy()) assert same(a[np.array(False, dtype=np.bool_)].asnumpy(), a[None][0:0].asnumpy()) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np def test_boolean_index_catch_exception(): # adapted from numpy's test_indexing.py arr = np.ones((5, 4, 3)) index = np.array([True], dtype=np.bool_) assert_exception(arr.__getitem__, IndexError, index) index = np.array([False] * 6, dtype=np.bool_) assert_exception(arr.__getitem__, IndexError, index) index = np.zeros((4, 4), dtype=bool) assert_exception(arr.__getitem__, IndexError, index) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np def test_boolean_index_onedim(): # adapted from numpy's test_indexing.py # Indexing a 2-dimensional array with # boolean array of length one a = np.array([[0., 0., 0.]]) b = np.array([True], dtype=bool) assert same(a[b].asnumpy(), a.asnumpy()) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np def test_boolean_index_twodim(): # adapted from numpy's test_indexing.py # Indexing a 2-dimensional array with # 2-dimensional boolean array a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.int32) b = np.array([[ True, False, True], [False, True, False], [ True, False, True]], dtype=np.bool_) assert same(a[b].asnumpy(), _np.array([1, 3, 5, 7, 9], dtype=a.dtype)) assert same(a[b[1]].asnumpy(), _np.array([[4, 5, 6]], dtype=a.dtype)) assert same(a[b[0]].asnumpy(), a[b[2]].asnumpy()) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np def test_boolean_index_list(): # adapted from numpy's test_indexing.py a = np.array([1, 2, 3], dtype=np.int32) b = [True, False, True] # Two variants of the test because the first takes a fast path assert same(a[b].asnumpy(), _np.array([1, 3], dtype=a.dtype)) (a[None, b], [[1, 3]]) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np def test_boolean_index_tuple(): # case arr[:, mask, :] and arr[1, mask, 0] # when a boolean array is in a tuple a = np.array([[[0, 1], [2, 3]], [[4, 5], [6, 7]]], dtype=np.int32) b = np.array([[False,True], [True,False]],dtype=np.bool) _np_a = a.asnumpy() _np_b = b.asnumpy() assert same(a[:, b].asnumpy(), _np_a[:, _np_b]) assert same(a[b, :].asnumpy(), _np_a[_np_b, :]) assert same(a[0, b].asnumpy(), _np_a[0, _np_b]) assert same(a[b, 1].asnumpy(), _np_a[_np_b, 1]) a = np.arange(12).reshape(4,3) b = np.array([1.,2.,3.]) _np_a = a.asnumpy() _np_b = b.asnumpy() assert same(a[:, b > 2].shape, _np_a[:, _np_b > 2].shape) assert same(a[:, b > 2].asnumpy(), _np_a[:, _np_b > 2]) a = np.array([[1,2,3],[3,4,5]]) _np_a = a.asnumpy() assert same(a[:,a[1,:] > 0].shape, _np_a[:,_np_a[1,: ] > 0].shape) assert same(a[:,a[1,:] > 0].asnumpy(), _np_a[:,_np_a[1,: ] > 0]) a = np.ones((3,2), dtype='bool') b = np.array([1,2,3]) _np_a = a.asnumpy() _np_b = b.asnumpy() assert same(a[b > 1].asnumpy(), _np_a[_np_b > 1]) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np @pytest.mark.xfail(reason='Flaky boolean index assign. See #18334') def test_boolean_index_assign(): # test boolean indexing assign shape = (3, 2, 3) mx_data = np.random.uniform(size=shape) mx_mask = np.array([[False,True], [True,False], [True,False]],dtype=np.bool) np_data = mx_data.asnumpy() np_mask = mx_mask.asnumpy() np_data[np_data>0.5] = 0 mx_data[mx_data>0.5] = 0 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) np_data[np_mask] = 1 mx_data[mx_mask] = 1 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) np_data[np_mask, 1] = 2 mx_data[mx_mask, 1] = 2 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) np_data[np_mask, :] = 3 mx_data[mx_mask, :] = 3 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) mx_mask = np.array([[False,True, True],[False, True,False]],dtype=np.bool) np_mask = mx_mask.asnumpy() np_data[0, np_mask] = 5 mx_data[0, mx_mask] = 5 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) np_data[:, np_mask] = 6 mx_data[:, mx_mask] = 6 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) np_data[0, True, True, np_mask] = 7 mx_data[0, True, True, mx_mask] = 7 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) np_data[False, 1] = 8 mx_data[False, 1] = 8 assert_almost_equal(mx_data.asnumpy(), np_data, rtol=1e-3, atol=1e-5, use_broadcast=False) @pytest.mark.skipif(not is_op_runnable(), reason="Comparison ops can only run on either CPU instances, or GPU instances with" " compute capability >= 53 if MXNet is built with USE_TVM_OP=ON") @use_np def test_boolean_index_autograd(): a = np.random.uniform(size=(3, 4, 5)) a.attach_grad() with mx.autograd.record(): out_mx = a[a < 0.5] out_mx.backward() a_np = a.asnumpy() out_np = a_np[a_np < 0.5] assert_almost_equal(out_mx.asnumpy(), out_np, rtol=1e-4, atol=1e-5, use_broadcast=False) a_grad_np = _np.zeros(a.shape, dtype=a.dtype) a_grad_np[a_np < 0.5] = 1 assert_almost_equal(a.grad.asnumpy(), a_grad_np, rtol=1e-4, atol=1e-5, use_broadcast=False) @use_np def test_np_get_dtype(): dtypes = [_np.int8, _np.int32, _np.float16, _np.float32, _np.float64, _np.bool, _np.bool_, 'int8', 'int32', 'float16', 'float32', 'float64', 'bool', None] objects = [ [], (), [[1, 2], [3, 4]], _np.random.uniform(size=rand_shape_nd(3)), _np.random.uniform(size=(3, 0, 4)) ] for dtype in dtypes: for src in objects: mx_arr = np.array(src, dtype=dtype) assert mx_arr.device == mx.current_device() if isinstance(src, mx.nd.NDArray): np_arr = _np.array(src.asnumpy(), dtype=dtype if dtype is not None else _np.float32) else: np_arr = _np.array(src, dtype=dtype if dtype is not None else _np.float32) assert type(mx_arr.dtype) == type(np_arr.dtype) @use_np def test_np_ndarray_pickle(): a = np.random.uniform(size=(4, 5)) a_copy = a.copy() import pickle with TemporaryDirectory() as work_dir: fname = os.path.join(work_dir, 'np_ndarray_pickle_test_file') with open(fname, 'wb') as f: pickle.dump(a_copy, f) with open(fname, 'rb') as f: a_load = pickle.load(f) same(a.asnumpy(), a_load.asnumpy()) @pytest.mark.parametrize('dtype', [np.float32, np.int32]) @pytest.mark.parametrize('size', [ (3, 4, 5, 6), (2, 10), (15,), () ]) @use_np def test_dlpack(dtype, size): a = mx.np.random.uniform(size=size) a_np = a.copy() a += 1 pack = mx.npx.to_dlpack_for_read(a) b = mx.npx.from_dlpack(pack) a_copy = a.copy() pack2 = mx.npx.to_dlpack_for_write(a_copy) c = mx.npx.from_dlpack(pack2) c += 1 del a, pack, pack2 same(a_np+1, b) same(a_np+2, c) same(a_np+2, a_copy) @use_np @pytest.mark.parametrize('np_array', [ # ordinary numpy array _np.array([[1, 2], [3, 4], [5, 6]], dtype="float32"), # 0-dim _np.array((1, )).reshape(()), # 0-size _np.array(()).reshape((1, 0, 2)), ]) @pytest.mark.parametrize('zero_copy', [False, True]) def test_from_numpy(np_array, zero_copy): # Test zero_copy mx_array = mx.npx.from_numpy(np_array, zero_copy=zero_copy) mx.test_utils.assert_almost_equal(np_array, mx_array.asnumpy()) def test_from_numpy_exception(): np_array = _np.array([[1, 2], [3, 4], [5, 6]], dtype="float32") mx_array = mx.npx.from_numpy(np_array) with pytest.raises(ValueError): np_array[2, 1] = 0 mx_array[2, 1] = 100 mx.test_utils.assert_almost_equal(np_array, mx_array.asnumpy()) np_array = _np.array([[1, 2], [3, 4], [5, 6]]).transpose() assert not np_array.flags["C_CONTIGUOUS"] with pytest.raises(ValueError): mx_array = mx.nd.from_numpy(np_array) np_array = _np.array([[1, 2], [3, 4], [5, 6]], dtype="float32") mx_array = mx.npx.from_numpy(np_array, zero_copy=False) np_array[2, 1] = 0 # no error def test_mixed_array_types(): np_array = _np.array([[1, 2], [3, 4], [5, 6]], dtype="float32") mx_array = mx.np.ones((3, 1)) assert_almost_equal(mx_array + np_array, 1+np_array) def test_mixed_array_types_share_memory(): np_array = _np.array([[1, 2], [3, 4], [5, 6]], dtype="float32") mx_array = mx.npx.from_numpy(np_array) assert _np.may_share_memory(np_array, mx_array) assert _np.shares_memory(np_array, mx_array) np_array_slice = np_array[:2] mx_array_slice = mx_array[1:] assert _np.may_share_memory(np_array_slice, mx_array) assert _np.shares_memory(np_array_slice, mx_array) mx_pinned_array = mx_array.to_device(mx.cpu_pinned(0)) assert not _np.may_share_memory(np_array, mx_pinned_array) assert not _np.shares_memory(np_array, mx_pinned_array) @use_np def test_save_load_empty(tmp_path): mx.npx.savez(str(tmp_path / 'params.npz')) mx.npx.load(str(tmp_path / 'params.npz')) @use_np @pytest.mark.parametrize('shape', [ (), (1,), (1,2) ]) @pytest.mark.parametrize('dtype', ['float16', 'float32', 'float64', 'bool', 'int32']) def test_index_operator(shape, dtype): if len(shape) >= 1 or not _np.issubdtype(dtype, _np.integer): x = np.ones(shape=shape, dtype=dtype) pytest.raises(TypeError, operator.index, x) else: assert operator.index(np.ones(shape=shape, dtype=dtype)) == \ operator.index(_np.ones(shape=shape, dtype=dtype)) @pytest.mark.parametrize('api_version, raise_exception', [ (None, False), ('2021.10', False), ('2020.09', True), ('2021.24', True), ]) def test_array_namespace(api_version, raise_exception): x = np.array([1, 2, 3], dtype="float64") if raise_exception: pytest.raises(ValueError, x.__array_namespace__, api_version) else: xp = x.__array_namespace__(api_version) y = xp.array([1, 2, 3], dtype="float64") assert same(x, y)