From 0b9ec437ea78597d7719559473b54473fd157db1 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Fri, 16 Jul 2021 17:29:00 -0700 Subject: [PATCH 01/10] fix #20477 --- python/mxnet/ndarray/numpy/_op.py | 20 ++++----- python/mxnet/numpy/multiarray.py | 22 +++++----- src/api/operator/ufunc_helper.cc | 35 +++++++++++++++- .../numpy/np_elemwise_broadcast_op.cc | 9 ++++ src/operator/numpy/np_elemwise_broadcast_op.h | 32 ++++++++++++++- .../unittest/test_array_api_standard.py | 41 +++++++++++++++++++ 6 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 tests/python/unittest/test_array_api_standard.py diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index fa255f0ccac4..1da3f6094f75 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -1010,7 +1010,7 @@ def add(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.add(x1, x2, out=out) - return _api_internal.add(x1, x2, out) + return _api_internal.add(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1046,7 +1046,7 @@ def subtract(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.subtract(x1, x2, out=out) - return _api_internal.subtract(x1, x2, out) + return _api_internal.subtract(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1082,7 +1082,7 @@ def multiply(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.multiply(x1, x2, out=out) - return _api_internal.multiply(x1, x2, out) + return _api_internal.multiply(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1121,7 +1121,7 @@ def divide(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.divide(x1, x2, out=out) - return _api_internal.true_divide(x1, x2, out) + return _api_internal.true_divide(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1162,7 +1162,7 @@ def true_divide(x1, x2, out=None): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.true_divide(x1, x2, out=out) - return _api_internal.true_divide(x1, x2, out) + return _api_internal.true_divide(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1191,7 +1191,7 @@ def mod(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.mod(x1, x2, out=out) - return _api_internal.mod(x1, x2, out) + return _api_internal.mod(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1381,7 +1381,7 @@ def matmul(a, b, out=None): ... mxnet.base.MXNetError: ... : Multiplication by scalars is not allowed. """ - return _api_internal.matmul(a, b, out) + return _api_internal.matmul(a, b, out, False) @set_module('mxnet.ndarray.numpy') @@ -6753,7 +6753,7 @@ def bitwise_and(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.bitwise_and(x1, x2, out=out) - return _api_internal.bitwise_and(x1, x2, out) + return _api_internal.bitwise_and(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -6793,7 +6793,7 @@ def bitwise_xor(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.bitwise_xor(x1, x2, out=out) - return _api_internal.bitwise_xor(x1, x2, out) + return _api_internal.bitwise_xor(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -6833,7 +6833,7 @@ def bitwise_or(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.bitwise_or(x1, x2, out=out) - return _api_internal.bitwise_or(x1, x2, out) + return _api_internal.bitwise_or(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index 5cca1fa9225a..b8d2c8ca7760 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -48,7 +48,7 @@ is_np_default_dtype from ..context import current_context from ..ndarray import numpy as _mx_nd_np -from ..ndarray.numpy import _internal as _npi +from ..ndarray.numpy import _internal as _npi, _api_internal from ..ndarray.ndarray import _storage_type from ..dlpack import ndarray_from_numpy from .utils import _get_np_op @@ -1015,7 +1015,7 @@ def __iadd__(self, other): """x.__iadd__(y) <=> x += y""" if not self.writable: raise ValueError('trying to add to a readonly ndarray') - return add(self, other, out=self) + return _api_internal.add(self, other, self, True) def __invert__(self): """x.__invert__() <=> ~x""" @@ -1039,17 +1039,17 @@ def __xor__(self, other): @wrap_mxnp_np_ufunc def __iand__(self, other): """x.__iand__(y) <=> x &= y""" - return bitwise_and(self, other, out=self) + return _api_internal.bitwise_and(self, other, self, True) @wrap_mxnp_np_ufunc def __ior__(self, other): r"""x.__ior__(y) <=> x \|= y""" - return bitwise_or(self, other, out=self) + return _api_internal.bitwise_or(self, other, self, True) @wrap_mxnp_np_ufunc def __ixor__(self, other): """x.__ixor__(y) <=> x ^= y""" - return bitwise_xor(self, other, out=self) + return _api_internal.bitwise_xor(self, other, self, True) def __round__(self, n=0): """x.__round__(n)""" @@ -1081,7 +1081,7 @@ def __isub__(self, other): """x.__isub__(y) <=> x -= y""" if not self.writable: raise ValueError('trying to subtract from a readonly ndarray') - return subtract(self, other, out=self) + return _api_internal.subtract(self, other, self, True) @wrap_mxnp_np_ufunc def __rsub__(self, other): @@ -1101,7 +1101,7 @@ def __imul__(self, other): r"""x.__imul__(y) <=> x \*= y""" if not self.writable: raise ValueError('trying to add to a readonly ndarray') - return multiply(self, other, out=self) + return _api_internal.multiply(self, other, self, True) @wrap_mxnp_np_ufunc def __rmul__(self, other): @@ -1121,7 +1121,7 @@ def __rdiv__(self, other): @wrap_mxnp_np_ufunc def __idiv__(self, other): """x.__idiv__(y) <=> x /= y""" - return divide(self, other, out=self) + return _api_internal.true_divide(self, other, self, True) @wrap_mxnp_np_ufunc def __truediv__(self, other): @@ -1136,7 +1136,7 @@ def __rtruediv__(self, other): @wrap_mxnp_np_ufunc def __itruediv__(self, other): """x.__itruediv__(y) <=> x /= y""" - return divide(self, other, out=self) + return _api_internal.true_divide(self, other, self, True) @wrap_mxnp_np_ufunc def __mod__(self, other): @@ -1151,7 +1151,7 @@ def __rmod__(self, other): @wrap_mxnp_np_ufunc def __imod__(self, other): """x.__imod__(y) <=> x %= y""" - return mod(self, other, out=self) + return _api_internal.mod(self, other, self, True) @wrap_mxnp_np_ufunc def __pow__(self, other): @@ -1209,7 +1209,7 @@ def __rmatmul__(self, other): @wrap_mxnp_np_ufunc def __imatmul__(self, other): """x.__imatmul__(y) <=> x @= y""" - return matmul(self, other, out=self) + return _api_internal.matmul(self, other, self, True) def __bool__(self): num_elements = self.size diff --git a/src/api/operator/ufunc_helper.cc b/src/api/operator/ufunc_helper.cc index b960267d4469..70dd9c18c0ed 100644 --- a/src/api/operator/ufunc_helper.cc +++ b/src/api/operator/ufunc_helper.cc @@ -25,6 +25,7 @@ #include "utils.h" #include "../../imperative/imperative_utils.h" #include "../../operator/tensor/elemwise_binary_scalar_op.h" +#include "../../operator/numpy/np_elemwise_broadcast_op.h" namespace mxnet { @@ -52,6 +53,28 @@ void UFuncHelper(NDArray* lhs, NDArray* rhs, NDArray* out, } } +void UFuncHelper(NDArray* lhs, NDArray* rhs, NDArray* out, + runtime::MXNetRetValue* ret, const nnvm::Op* op, bool in_place) { + using namespace runtime; + nnvm::NodeAttrs attrs; + op::NumpyBinaryParam param; + param.in_place = in_place; + attrs.op = op; + attrs.parsed = param; + SetAttrDict(&attrs); + attrs.op = op; + NDArray* inputs[] = {lhs, rhs}; + int num_inputs = 2; + NDArray** outputs = out == nullptr ? nullptr : &out; + int num_outputs = out != nullptr; + auto ndoutputs = Invoke(op, &attrs, num_inputs, inputs, &num_outputs, outputs); + if (outputs) { + *ret = PythonArg(2); + } else { + *ret = reinterpret_cast(ndoutputs[0]); + } +} + void UFuncHelper(NDArray* lhs, int64_t rhs, NDArray* out, runtime::MXNetRetValue* ret, const nnvm::Op* op) { using namespace runtime; @@ -149,7 +172,17 @@ void UFuncHelper(runtime::MXNetArgs args, NDArray* out = args[2].operator NDArray*(); if (args[0].type_code() == kNDArrayHandle) { if (args[1].type_code() == kNDArrayHandle) { - UFuncHelper(args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array); + int args_size = args.size(); + if (args_size == 4) { + bool in_place = args[3].operator bool(); + if (in_place) { + UFuncHelper(args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array, true); + } else { + UFuncHelper(args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array, false); + } + } else { + UFuncHelper(args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array); + } } else if (args[1].type_code() == kDLInt) { UFuncHelper(args[0].operator NDArray*(), args[1].operator int64_t(), out, ret, lfn_scalar); } else { diff --git a/src/operator/numpy/np_elemwise_broadcast_op.cc b/src/operator/numpy/np_elemwise_broadcast_op.cc index ae6697c0b23e..7731af5141e0 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op.cc +++ b/src/operator/numpy/np_elemwise_broadcast_op.cc @@ -29,6 +29,7 @@ namespace mxnet { namespace op { DMLC_REGISTER_PARAMETER(NumpyBinaryScalarParam); +DMLC_REGISTER_PARAMETER(NumpyBinaryParam); #define MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(name) \ NNVM_REGISTER_OP(name) \ @@ -51,6 +52,13 @@ bool NumpyBinaryMixedPrecisionType(const nnvm::NodeAttrs& attrs, CHECK_EQ(out_attrs->size(), 1U); const int ltype = in_attrs->at(0); const int rtype = in_attrs->at(1); + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace && ((common::is_float(ltype) && common::is_float(rtype)) || + (!common::is_float(ltype) && !common::is_float(rtype)))) { + TYPE_ASSIGN_CHECK(*out_attrs, 0, ltype); + return true; + } if (ltype != -1 && rtype != -1 && (ltype != rtype)) { // Only when both input types are known and not the same, we enter the mixed-precision mode TYPE_ASSIGN_CHECK(*out_attrs, 0, common::np_binary_out_infer_type(ltype, rtype)); @@ -64,6 +72,7 @@ bool NumpyBinaryMixedPrecisionType(const nnvm::NodeAttrs& attrs, NNVM_REGISTER_OP(name) \ .set_num_inputs(2) \ .set_num_outputs(1) \ + .set_attr_parser(ParamParser) \ .set_attr("FListInputNames", \ [](const NodeAttrs& attrs) { \ return std::vector{"lhs", "rhs"}; \ diff --git a/src/operator/numpy/np_elemwise_broadcast_op.h b/src/operator/numpy/np_elemwise_broadcast_op.h index be19b3876a40..1cfcc52607bb 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op.h +++ b/src/operator/numpy/np_elemwise_broadcast_op.h @@ -35,6 +35,21 @@ namespace mxnet { namespace op { +struct NumpyBinaryParam : public dmlc::Parameter { + bool in_place; + DMLC_DECLARE_PARAMETER(NumpyBinaryParam) { + DMLC_DECLARE_FIELD(in_place) + .set_default(false) + .describe("Indicate whether this binary operation is in-place"); + } + + void SetAttrDict(std::unordered_map* dict) { + std::ostringstream in_place_s; + in_place_s << in_place; + (*dict)["in_place"] = in_place_s.str(); + } +}; + inline void PrintErrorMessage(const std::string& op_name, const int dtype1, const int dtype2) { LOG(FATAL) << "Operator " << op_name << " does not support combination of " << mshadow::dtype_string(dtype1) << " with " << mshadow::dtype_string(dtype2) @@ -235,10 +250,23 @@ void MixedBinaryBroadcastCompute(const nnvm::NodeAttrs& attrs, mxnet::TShape new_lshape, new_rshape, new_oshape; int ndim = BinaryBroadcastShapeCompact(lhs.shape_, rhs.shape_, out.shape_, &new_lshape, &new_rshape, &new_oshape); - if (!ndim) { + + mshadow::Stream *s = ctx.get_stream(); + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TBlob temp_tblob; + MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + BinaryBroadcastCompute( + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + } else if (!ndim) { MixedBinaryElemwiseCompute(attrs, ctx, inputs, req, outputs); } else { - mshadow::Stream *s = ctx.get_stream(); if (common::is_float(lhs.type_flag_) && common::is_float(rhs.type_flag_)) { if (lhs.type_flag_ == out.type_flag_) { MixedAllRealBinaryBroadcastCompute( diff --git a/tests/python/unittest/test_array_api_standard.py b/tests/python/unittest/test_array_api_standard.py new file mode 100644 index 000000000000..6af248a35c42 --- /dev/null +++ b/tests/python/unittest/test_array_api_standard.py @@ -0,0 +1,41 @@ +# 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 +import pytest +import numpy as onp +import mxnet as mx +import mxnet.ndarray.numpy._internal as _npi +from mxnet import np, npx +from mxnet.test_utils import assert_almost_equal + +@pytest.mark.parametrize('a_dtype', [onp.float16, onp.float32, onp.float64]) +@pytest.mark.parametrize('b_dtype', [onp.float16, onp.float32, onp.float64]) +@pytest.mark.parametrize('shape', [ + (), + (2, 0, 2, 2), + (5, 5) +]) +@pytest.mark.parametrize('op', [ + '__iadd__', '__iand__', '__ior__', '__ixor__', '__isub__', '__imul__', '__imatmul__', + '__imod__', '__itruediv__', '__idiv__']) +def test_in_place_dtype(a_dtype, b_dtype, shape, op): + a = np.random.uniform(size=shape, dtype=a_dtype) + b = np.random.uniform(size=shape, dtype=b_dtype) + getattr(a, op)(b) + assert a.dtype == a_dtype From 9b2623cc47f6b06233979918eb4d192e9bd3a4a9 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Fri, 10 Dec 2021 13:30:41 -0800 Subject: [PATCH 02/10] update --- python/mxnet/ndarray/numpy/_op.py | 10 +- python/mxnet/numpy/multiarray.py | 8 +- src/api/operator/ufunc_helper.cc | 25 ++ src/operator/numpy/np_elemwise_broadcast_op.h | 65 ++- .../numpy/np_elemwise_broadcast_op_add.cc | 2 + .../numpy/np_elemwise_broadcast_op_mod.cc | 8 +- .../numpy/np_elemwise_broadcast_op_sub.cc | 8 +- src/operator/numpy/np_true_divide-inl.h | 389 ++++++++++-------- src/operator/numpy/np_true_divide.cc | 10 +- .../unittest/test_array_api_standard.py | 41 -- tests/python/unittest/test_numpy_op.py | 23 ++ 11 files changed, 354 insertions(+), 235 deletions(-) delete mode 100644 tests/python/unittest/test_array_api_standard.py diff --git a/python/mxnet/ndarray/numpy/_op.py b/python/mxnet/ndarray/numpy/_op.py index 1495fabaff69..5fd44c7ca8f3 100644 --- a/python/mxnet/ndarray/numpy/_op.py +++ b/python/mxnet/ndarray/numpy/_op.py @@ -1218,7 +1218,7 @@ def floor_divide(x1, x2, out=None): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.floor_divide(x1, x2, out=out) - return _api_internal.floor_divide(x1, x2, out) + return _api_internal.floor_divide(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1466,7 +1466,7 @@ def remainder(x1, x2, out=None): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): _np.mod(x1, x2, out=out) - return _api_internal.mod(x1, x2, out) + return _api_internal.mod(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -1496,7 +1496,7 @@ def power(x1, x2, out=None, **kwargs): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.power(x1, x2, out=out) - return _api_internal.power(x1, x2, out) + return _api_internal.power(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -10089,7 +10089,7 @@ def bitwise_left_shift(x1, x2, out=None): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.left_shift(x1, x2, out=out) - return _api_internal.bitwise_left_shift(x1, x2, out) + return _api_internal.bitwise_left_shift(x1, x2, out, False) @set_module('mxnet.ndarray.numpy') @@ -10128,4 +10128,4 @@ def bitwise_right_shift(x1, x2, out=None): """ if isinstance(x1, numeric_types) and isinstance(x2, numeric_types): return _np.right_shift(x1, x2, out=out) - return _api_internal.bitwise_right_shift(x1, x2, out) + return _api_internal.bitwise_right_shift(x1, x2, out, False) diff --git a/python/mxnet/numpy/multiarray.py b/python/mxnet/numpy/multiarray.py index 161ab1d937f7..558b2e977bc7 100644 --- a/python/mxnet/numpy/multiarray.py +++ b/python/mxnet/numpy/multiarray.py @@ -1160,12 +1160,12 @@ def __ixor__(self, other): @wrap_mxnp_np_ufunc def __ilshift__(self, other): """x.__ilshift__(y) <=> x <<= y""" - return bitwise_left_shift(self, other, out=self) + return _api_internal.bitwise_left_shift(self, other, self, True) @wrap_mxnp_np_ufunc def __irshift__(self, other): """x.__irshift__(y) <=> x >>= y""" - return bitwise_right_shift(self, other, out=self) + return _api_internal.bitwise_right_shift(self, other, self, True) @wrap_mxnp_np_ufunc def __rlshift__(self, other): @@ -1229,7 +1229,7 @@ def __ifloordiv__(self, other): """x.__ifloordiv__(y) <=> x //= y""" if not self.writable: raise ValueError('trying to divide from a readonly ndarray') - return floor_divide(self, other, out=self) + return _api_internal.floor_divide(self, other, self, True) @wrap_mxnp_np_ufunc def __rfloordiv__(self, other): @@ -1314,7 +1314,7 @@ def __rpow__(self, other): @wrap_mxnp_np_ufunc def __ipow__(self, other): """x.__ipow__(y) <=> x **= y""" - return power(self, other, out=self) + return _api_internal.power(self, other, self, True) @wrap_mxnp_np_ufunc def __eq__(self, other): diff --git a/src/api/operator/ufunc_helper.cc b/src/api/operator/ufunc_helper.cc index 93a17b2d9736..be6006422a3e 100644 --- a/src/api/operator/ufunc_helper.cc +++ b/src/api/operator/ufunc_helper.cc @@ -56,6 +56,31 @@ void UFuncHelper(NDArray* lhs, } } +void UFuncHelper(NDArray* lhs, + NDArray* rhs, + NDArray* out, + runtime::MXNetRetValue* ret, + const nnvm::Op* op, + bool in_place) { + using namespace runtime; + nnvm::NodeAttrs attrs; + op::NumpyBinaryParam param = {}; + param.in_place = in_place; + attrs.op = op; + attrs.parsed = param; + SetAttrDict(&attrs); + NDArray* inputs[] = {lhs, rhs}; + int num_inputs = 2; + NDArray** outputs = out == nullptr ? nullptr : &out; + int num_outputs = out != nullptr; + auto ndoutputs = Invoke(op, &attrs, num_inputs, inputs, &num_outputs, outputs); + if (outputs) { + *ret = PythonArg(2); + } else { + *ret = reinterpret_cast(ndoutputs[0]); + } +} + void UFuncHelper(NDArray* lhs, int64_t rhs, NDArray* out, diff --git a/src/operator/numpy/np_elemwise_broadcast_op.h b/src/operator/numpy/np_elemwise_broadcast_op.h index 86a309d55bc9..87fc4b30ac0a 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op.h +++ b/src/operator/numpy/np_elemwise_broadcast_op.h @@ -345,7 +345,20 @@ void MixedBinaryBroadcastCompute(const nnvm::NodeAttrs& attrs, mxnet::TShape new_lshape, new_rshape, new_oshape; int ndim = BinaryBroadcastShapeCompact( lhs.shape_, rhs.shape_, out.shape_, &new_lshape, &new_rshape, &new_oshape); - if (!ndim) { + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TBlob temp_tblob; + mshadow::Stream* s = ctx.get_stream(); + MSHADOW_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + BinaryBroadcastComputeWithBool( + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + } else if (!ndim) { MixedBinaryElemwiseCompute(attrs, ctx, inputs, req, outputs); } else { mshadow::Stream* s = ctx.get_stream(); @@ -490,7 +503,21 @@ void NumpyBinaryBroadcastComputeWithBool(const nnvm::NodeAttrs& attrs, BinaryBroadcastComputeWithBool(attrs, ctx, inputs, req, outputs); return; } - if (!common::is_float(lhs.type_flag_) && !common::is_float(rhs.type_flag_)) { + + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + Stream* s = ctx.get_stream(); + TBlob temp_tblob; + MSHADOW_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + BinaryBroadcastComputeWithBool( + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + } else if (!common::is_float(lhs.type_flag_) && !common::is_float(rhs.type_flag_)) { Stream* s = ctx.get_stream(); TBlob temp_tblob; if (lhs.type_flag_ == out.type_flag_) { @@ -560,7 +587,18 @@ void NumpyBinaryBroadcastIntComputeWithBool(const nnvm::NodeAttrs& attrs, } Stream* s = ctx.get_stream(); TBlob temp_tblob; - if (lhs.type_flag_ == out.type_flag_) { + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + MXNET_INT_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + BinaryBroadcastIntComputeWithBool( + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + } else if (lhs.type_flag_ == out.type_flag_) { MXNET_INT_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { Tensor temp_tensor = ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); @@ -888,6 +926,12 @@ inline bool NumpyBinaryMixedPrecisionType(const nnvm::NodeAttrs& attrs, CHECK_EQ(out_attrs->size(), 1U); const int ltype = in_attrs->at(0); const int rtype = in_attrs->at(1); + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TYPE_ASSIGN_CHECK(*out_attrs, 0, ltype); + return true; + } if (ltype != -1 && rtype != -1 && (ltype != rtype)) { // Only when both input types are known and not the same, we enter the mixed-precision mode TYPE_ASSIGN_CHECK(*out_attrs, 0, common::type_promotion(ltype, rtype)); @@ -901,6 +945,7 @@ inline bool NumpyBinaryMixedPrecisionType(const nnvm::NodeAttrs& attrs, NNVM_REGISTER_OP(name) \ .set_num_inputs(2) \ .set_num_outputs(1) \ + .set_attr_parser(ParamParser) \ .set_attr("FListInputNames", \ [](const NodeAttrs& attrs) { \ return std::vector{"lhs", "rhs"}; \ @@ -930,6 +975,12 @@ inline bool NumpyBinaryMixedIntPrecisionTypeWithBool(const nnvm::NodeAttrs& attr << "1st input only supports integer types or bool types."; CHECK(common::is_int(rtype) || rtype == mshadow::kBool) << "2nd input only supports integer types or bool types."; + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TYPE_ASSIGN_CHECK(*out_attrs, 0, ltype); + return true; + } if (ltype != -1 && rtype != -1 && (ltype != rtype)) { // Only when both input types are known and not the same, we enter the mixed-precision mode TYPE_ASSIGN_CHECK(*out_attrs, 0, common::type_promotion(ltype, rtype)); @@ -943,6 +994,7 @@ inline bool NumpyBinaryMixedIntPrecisionTypeWithBool(const nnvm::NodeAttrs& attr NNVM_REGISTER_OP(name) \ .set_num_inputs(2) \ .set_num_outputs(1) \ + .set_attr_parser(ParamParser) \ .set_attr("FListInputNames", \ [](const NodeAttrs& attrs) { \ return std::vector{"lhs", "rhs"}; \ @@ -970,6 +1022,12 @@ inline bool NumpyBinaryMixedIntPrecisionType(const nnvm::NodeAttrs& attrs, const int rtype = in_attrs->at(1); CHECK(common::is_int(ltype)) << "1st input only supports integer types."; CHECK(common::is_int(rtype)) << "2nd input only supports integer types."; + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TYPE_ASSIGN_CHECK(*out_attrs, 0, ltype); + return true; + } if (ltype != -1 && rtype != -1 && (ltype != rtype)) { // Only when both input types are known and not the same, we enter the mixed-precision mode TYPE_ASSIGN_CHECK(*out_attrs, 0, common::type_promotion(ltype, rtype)); @@ -983,6 +1041,7 @@ inline bool NumpyBinaryMixedIntPrecisionType(const nnvm::NodeAttrs& attrs, NNVM_REGISTER_OP(name) \ .set_num_inputs(2) \ .set_num_outputs(1) \ + .set_attr_parser(ParamParser) \ .set_attr("FListInputNames", \ [](const NodeAttrs& attrs) { \ return std::vector{"lhs", "rhs"}; \ diff --git a/src/operator/numpy/np_elemwise_broadcast_op_add.cc b/src/operator/numpy/np_elemwise_broadcast_op_add.cc index 50a79ab5dc2f..253f35fae099 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op_add.cc +++ b/src/operator/numpy/np_elemwise_broadcast_op_add.cc @@ -27,6 +27,8 @@ namespace mxnet { namespace op { +DMLC_REGISTER_PARAMETER(NumpyBinaryParam); + MXNET_OPERATOR_REGISTER_NP_BINARY_MIXED_PRECISION(_npi_add) .set_attr("FCompute", NumpyBinaryBroadcastComputeWithBool("FCompute", - NumpyBinaryBroadcastCompute) + NumpyBinaryBroadcastComputeWithBool) .set_attr("FGradient", ElemwiseGradUseIn{"_backward_npi_broadcast_mod"}); NNVM_REGISTER_OP(_backward_npi_broadcast_mod) diff --git a/src/operator/numpy/np_elemwise_broadcast_op_sub.cc b/src/operator/numpy/np_elemwise_broadcast_op_sub.cc index 5f3ba7653549..a66606064b5b 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op_sub.cc +++ b/src/operator/numpy/np_elemwise_broadcast_op_sub.cc @@ -29,10 +29,10 @@ namespace op { MXNET_OPERATOR_REGISTER_NP_BINARY_MIXED_PRECISION(_npi_subtract) .set_attr("FCompute", - NumpyBinaryBroadcastCompute) + NumpyBinaryBroadcastComputeWithBool) .set_attr("FGradient", ElemwiseGradUseIn{"_backward_npi_broadcast_sub"}); NNVM_REGISTER_OP(_backward_npi_broadcast_sub) diff --git a/src/operator/numpy/np_true_divide-inl.h b/src/operator/numpy/np_true_divide-inl.h index 047489f648cc..838aaa5f3fa4 100644 --- a/src/operator/numpy/np_true_divide-inl.h +++ b/src/operator/numpy/np_true_divide-inl.h @@ -90,89 +90,108 @@ void TrueDivideElemwiseCompute(const nnvm::NodeAttrs& attrs, const TBlob& lhs = inputs[0]; const TBlob& rhs = inputs[1]; const TBlob& out = outputs[0]; - if (lhs.type_flag_ == rhs.type_flag_) { - // Case when types of the 2 input tensors are the same - if (common::is_float(lhs.type_flag_)) { - // If both are the same floats, normal launch - MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { - MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, DType, { - Kernel, xpu>::Launch( - s, out.Size(), out.dptr(), lhs.dptr(), rhs.dptr()); - }); - }); - } else { - // If both are the same integers, output is float32 or float64 - CHECK_EQ(out.type_flag_, mxnet::common::GetDefaultDtype()) - << "true_divide only supports float32 and float64" - " output when input's dtype is " - << type_string(lhs.type_flag_); - MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { - MXNET_INT_TYPE_SWITCH(lhs.type_flag_, DType, { - Kernel, xpu>::Launch( - s, out.Size(), out.dptr(), lhs.dptr(), rhs.dptr()); - }); + + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TBlob temp_tblob; + MSHADOW_TYPE_SWITCH_EXT(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { + MSHADOW_TYPE_SWITCH_EXT(out.type_flag_, DType, { + Kernel, xpu>::Launch( + s, out.Size(), out.dptr(), lhs.dptr(), temp_tblob.dptr()); }); - } + }); } else { - // Case when types of the 2 input tensors are different - if (common::is_float(lhs.type_flag_) && common::is_float(rhs.type_flag_)) { - // both lhs and rhs are float types, output type is the more precise one - TBlob temp_tblob; - if (lhs.type_flag_ == out.type_flag_) { - MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { - Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); - temp_tblob = TBlob(temp_tensor); - }); - CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + if (lhs.type_flag_ == rhs.type_flag_) { + // Case when types of the 2 input tensors are the same + if (common::is_float(lhs.type_flag_)) { + // If both are the same floats, normal launch MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { - MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { + MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, DType, { Kernel, xpu>::Launch( - s, out.Size(), out.dptr(), lhs.dptr(), temp_tblob.dptr()); + s, out.Size(), out.dptr(), lhs.dptr(), rhs.dptr()); }); }); } else { - MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { - Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(lhs.Size()), s); - temp_tblob = TBlob(temp_tensor); - }); - CastCompute(attrs, ctx, {lhs}, {kWriteTo}, {temp_tblob}); + // If both are the same integers, output is float32 or float64 + CHECK_EQ(out.type_flag_, mxnet::common::GetDefaultDtype()) + << "true_divide only supports float32 and float64" + " output when input's dtype is " + << type_string(lhs.type_flag_); MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { - MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { + MXNET_INT_TYPE_SWITCH(lhs.type_flag_, DType, { Kernel, xpu>::Launch( - s, out.Size(), out.dptr(), temp_tblob.dptr(), rhs.dptr()); + s, out.Size(), out.dptr(), lhs.dptr(), rhs.dptr()); }); }); } - } else if (common::is_float(lhs.type_flag_) || common::is_float(rhs.type_flag_)) { - // one is float type, the other is integer type, the output type should be the same as float - CHECK_EQ(out.type_flag_, common::is_float(lhs.type_flag_) ? lhs.type_flag_ : rhs.type_flag_) - << "This case out type should be same as the float type"; - if (common::is_float(lhs.type_flag_)) { - // lhs is the float one - MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { + } else { + // Case when types of the 2 input tensors are different + if (common::is_float(lhs.type_flag_) && common::is_float(rhs.type_flag_)) { + // both lhs and rhs are float types, output type is the more precise one + TBlob temp_tblob; + if (lhs.type_flag_ == out.type_flag_) { MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { - MXNET_INT_TYPE_SWITCH(rhs.type_flag_, RType, { - Kernel, xpu>::Launch( - s, out.Size(), out.dptr(), rhs.dptr(), lhs.dptr()); + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { + MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { + Kernel, xpu>::Launch( + s, out.Size(), out.dptr(), lhs.dptr(), temp_tblob.dptr()); }); }); - }); - } else { - // rhs is the float one - MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { - MXNET_INT_TYPE_SWITCH(lhs.type_flag_, LType, { - MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { + } else { + MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(lhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {lhs}, {kWriteTo}, {temp_tblob}); + MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { + MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { Kernel, xpu>::Launch( - s, out.Size(), out.dptr(), lhs.dptr(), rhs.dptr()); + s, out.Size(), out.dptr(), temp_tblob.dptr(), rhs.dptr()); }); }); - }); + } + } else if (common::is_float(lhs.type_flag_) || common::is_float(rhs.type_flag_)) { + // one is float type, the other is integer type, the output type should be the same as float + CHECK_EQ(out.type_flag_, common::is_float(lhs.type_flag_) ? lhs.type_flag_ : rhs.type_flag_) + << "This case out type should be same as the float type"; + if (common::is_float(lhs.type_flag_)) { + // lhs is the float one + MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { + MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { + MXNET_INT_TYPE_SWITCH(rhs.type_flag_, RType, { + Kernel, xpu>::Launch( + s, out.Size(), out.dptr(), rhs.dptr(), lhs.dptr()); + }); + }); + }); + } else { + // rhs is the float one + MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { + MXNET_INT_TYPE_SWITCH(lhs.type_flag_, LType, { + MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { + Kernel, xpu>::Launch( + s, out.Size(), out.dptr(), lhs.dptr(), rhs.dptr()); + }); + }); + }); + } + } else { + // lhs is integer type, rhs is integer type, output type should be float + LOG(FATAL) << "not implemented yet..."; } - } else { - // lhs is integer type, rhs is integer type, output type should be float - LOG(FATAL) << "not implemented yet..."; } } } @@ -190,6 +209,8 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, mxnet::TShape new_lshape, new_rshape, new_oshape; int ndim = BinaryBroadcastShapeCompact( inputs[0].shape_, inputs[1].shape_, outputs[0].shape_, &new_lshape, &new_rshape, &new_oshape); + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; if (!ndim) { TrueDivideElemwiseCompute(attrs, ctx, inputs, req, outputs); } else { @@ -203,125 +224,147 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, mshadow::Shape oshape = new_oshape.get(); mshadow::Shape lstride = calc_stride(new_lshape.get()); mshadow::Shape rstride = calc_stride(new_rshape.get()); - if (lhs.type_flag_ == rhs.type_flag_) { - // When the both inputs have the same data types - if (common::is_float(lhs.type_flag_)) { - // If both inputs are the same float types, output is the same float type - MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, DType, { - Kernel, xpu>::template LaunchEx( - s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - rhs.dptr(), - out.dptr()); - }); - } else { - CHECK_EQ(out.type_flag_, mxnet::common::GetDefaultDtype()) - << "true_divide only supports float32 and float64 output when input's dtype is " - << type_string(lhs.type_flag_); - MXNET_INT_TYPE_SWITCH(lhs.type_flag_, DType, { - // If both inputs are the same integer types, output is float type - Kernel, xpu>::template LaunchEx( - s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - rhs.dptr(), - out.dptr()); - }); - } + if (is_inplace) { + TBlob temp_tblob; + MSHADOW_TYPE_SWITCH_EXT(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + MSHADOW_TYPE_SWITCH_EXT(out.type_flag_, DType, { + Kernel, + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + temp_tblob.dptr(), + out.dptr()); + }); } else { - if (common::is_float(lhs.type_flag_) && common::is_float(rhs.type_flag_)) { - // lhs and rhs have different float types, the output is the more precise one - TBlob temp_tblob; - if (lhs.type_flag_ == out.type_flag_) { - MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { - Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); - temp_tblob = TBlob(temp_tensor); - }); - CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); - MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { - Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - temp_tblob.dptr(), - out.dptr()); + if (lhs.type_flag_ == rhs.type_flag_) { + // When the both inputs have the same data types + if (common::is_float(lhs.type_flag_)) { + // If both inputs are the same float types, output is the same float type + MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, DType, { + Kernel, xpu>::template LaunchEx( + s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + rhs.dptr(), + out.dptr()); }); } else { - MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { - Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(lhs.Size()), s); - temp_tblob = TBlob(temp_tensor); - }); - CastCompute(attrs, ctx, {lhs}, {kWriteTo}, {temp_tblob}); - MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { - Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - temp_tblob.dptr(), - rhs.dptr(), - out.dptr()); + CHECK_EQ(out.type_flag_, mxnet::common::GetDefaultDtype()) + << "true_divide only supports float32 and float64 output when input's dtype is " + << type_string(lhs.type_flag_); + MXNET_INT_TYPE_SWITCH(lhs.type_flag_, DType, { + // If both inputs are the same integer types, output is float type + Kernel, xpu>::template LaunchEx( + s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + rhs.dptr(), + out.dptr()); }); } - } else if (common::is_float(lhs.type_flag_) || common::is_float(rhs.type_flag_)) { - // one of lhs and rhs is float, the output is the same type as the float one - if (common::is_float(lhs.type_flag_)) { - // lhs is float type, output will be the same float type - CHECK_EQ(lhs.type_flag_, out.type_flag_) - << "lhs should have the same type as out, infer type broken?"; - MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { - MXNET_INT_TYPE_SWITCH(rhs.type_flag_, RType, { - Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - rstride, - lstride, - oshape, - rhs.dptr(), - lhs.dptr(), - out.dptr()); + } else { + if (common::is_float(lhs.type_flag_) && common::is_float(rhs.type_flag_)) { + // lhs and rhs have different float types, the output is the more precise one + TBlob temp_tblob; + if (lhs.type_flag_ == out.type_flag_) { + MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); }); - }); - } else { - // rhs is float type, output will be the same float type - CHECK_EQ(rhs.type_flag_, out.type_flag_) - << "rhs should have the same type as out, infer type broken?"; - MXNET_INT_TYPE_SWITCH(lhs.type_flag_, LType, { + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { + Kernel, + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + temp_tblob.dptr(), + out.dptr()); + }); + } else { MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(lhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {lhs}, {kWriteTo}, {temp_tblob}); + MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - rhs.dptr(), - out.dptr()); + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + temp_tblob.dptr(), + rhs.dptr(), + out.dptr()); }); - }); + } + } else if (common::is_float(lhs.type_flag_) || common::is_float(rhs.type_flag_)) { + // one of lhs and rhs is float, the output is the same type as the float one + if (common::is_float(lhs.type_flag_)) { + // lhs is float type, output will be the same float type + CHECK_EQ(lhs.type_flag_, out.type_flag_) + << "lhs should have the same type as out, infer type broken?"; + MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { + MXNET_INT_TYPE_SWITCH(rhs.type_flag_, RType, { + Kernel, + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + rstride, + lstride, + oshape, + rhs.dptr(), + lhs.dptr(), + out.dptr()); + }); + }); + } else { + // rhs is float type, output will be the same float type + CHECK_EQ(rhs.type_flag_, out.type_flag_) + << "rhs should have the same type as out, infer type broken?"; + MXNET_INT_TYPE_SWITCH(lhs.type_flag_, LType, { + MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { + Kernel, + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + rhs.dptr(), + out.dptr()); + }); + }); + } + } else { + // lhs and rhs have different integer types, the output is float type + LOG(FATAL) << "not implemented yet..."; } - } else { - // lhs and rhs have different integer types, the output is float type - LOG(FATAL) << "not implemented yet..."; } } }); diff --git a/src/operator/numpy/np_true_divide.cc b/src/operator/numpy/np_true_divide.cc index 639379d36cd0..d272240bdc4d 100644 --- a/src/operator/numpy/np_true_divide.cc +++ b/src/operator/numpy/np_true_divide.cc @@ -51,8 +51,15 @@ bool TrueDivideType(const nnvm::NodeAttrs& attrs, if (dtype == -1) return false; } - const int lhs_dtype = in_attrs->at(0); + + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TYPE_ASSIGN_CHECK(*out_attrs, 0, lhs_dtype); + return true; + } + const int rhs_dtype = (num_inputs == 2) ? in_attrs->at(1) : @@ -64,6 +71,7 @@ bool TrueDivideType(const nnvm::NodeAttrs& attrs, NNVM_REGISTER_OP(_npi_true_divide) .set_num_inputs(2) .set_num_outputs(1) + .set_attr_parser(ParamParser) .set_attr("FListInputNames", [](const NodeAttrs& attrs) { return std::vector{"lhs", "rhs"}; diff --git a/tests/python/unittest/test_array_api_standard.py b/tests/python/unittest/test_array_api_standard.py deleted file mode 100644 index 6af248a35c42..000000000000 --- a/tests/python/unittest/test_array_api_standard.py +++ /dev/null @@ -1,41 +0,0 @@ -# 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 -import pytest -import numpy as onp -import mxnet as mx -import mxnet.ndarray.numpy._internal as _npi -from mxnet import np, npx -from mxnet.test_utils import assert_almost_equal - -@pytest.mark.parametrize('a_dtype', [onp.float16, onp.float32, onp.float64]) -@pytest.mark.parametrize('b_dtype', [onp.float16, onp.float32, onp.float64]) -@pytest.mark.parametrize('shape', [ - (), - (2, 0, 2, 2), - (5, 5) -]) -@pytest.mark.parametrize('op', [ - '__iadd__', '__iand__', '__ior__', '__ixor__', '__isub__', '__imul__', '__imatmul__', - '__imod__', '__itruediv__', '__idiv__']) -def test_in_place_dtype(a_dtype, b_dtype, shape, op): - a = np.random.uniform(size=shape, dtype=a_dtype) - b = np.random.uniform(size=shape, dtype=b_dtype) - getattr(a, op)(b) - assert a.dtype == a_dtype diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 99aacbf0e6fb..d5e97628f6d4 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -11759,3 +11759,26 @@ def array_values(low, high, shape): assert_almost_equal(mx_out.asnumpy(), np_out.astype(mx_out.dtype), rtol=rtol, atol=atol, use_broadcast=False, equal_nan=True) + +@pytest.mark.parametrize('a_dtype', np.numeric_dtypes + np.boolean_dtypes) +@pytest.mark.parametrize('b_dtype', np.numeric_dtypes + np.boolean_dtypes) +@pytest.mark.parametrize('shape', [ + (), + (2, 0, 2, 2), + (5, 5) +]) +@pytest.mark.parametrize('op', [ + '__iadd__', '__iand__', '__ior__', '__ixor__', '__isub__', '__imul__', '__imod__', + '__itruediv__', '__idiv__', '__ifloordiv__', '__ipow__', '__ilshift__', '__irshift__']) +def test_in_place_dtype(a_dtype, b_dtype, shape, op): + if op in ('__ilshift__', '__irshift__', '__iand__', '__ior__', '__ixor__') and \ + (a_dtype not in np.integer_dtypes or b_dtype not in np.integer_dtypes): + return + if op in ('__itruediv__', '__idiv__') and \ + (a_dtype in np.boolean_dtypes or b_dtype in np.boolean_dtypes): + return + a = np.random.uniform(size=shape).astype(a_dtype) + b = np.random.uniform(size=shape).astype(b_dtype) + getattr(a, op)(b) + assert a.dtype == a_dtype + npx.waitall() From 976f8f697ea4c30de7f6ec312f92af4775fdd504 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Fri, 10 Dec 2021 13:57:54 -0800 Subject: [PATCH 03/10] fix sanity --- src/api/operator/ufunc_helper.cc | 12 +- src/operator/numpy/np_elemwise_broadcast_op.h | 29 ++-- src/operator/numpy/np_true_divide-inl.h | 138 +++++++++--------- src/operator/numpy/np_true_divide.cc | 2 +- 4 files changed, 91 insertions(+), 90 deletions(-) diff --git a/src/api/operator/ufunc_helper.cc b/src/api/operator/ufunc_helper.cc index be6006422a3e..b4d10049df34 100644 --- a/src/api/operator/ufunc_helper.cc +++ b/src/api/operator/ufunc_helper.cc @@ -65,9 +65,9 @@ void UFuncHelper(NDArray* lhs, using namespace runtime; nnvm::NodeAttrs attrs; op::NumpyBinaryParam param = {}; - param.in_place = in_place; - attrs.op = op; - attrs.parsed = param; + param.in_place = in_place; + attrs.op = op; + attrs.parsed = param; SetAttrDict(&attrs); NDArray* inputs[] = {lhs, rhs}; int num_inputs = 2; @@ -194,9 +194,11 @@ void UFuncHelper(runtime::MXNetArgs args, if (args_size == 4) { bool in_place = args[3].operator bool(); if (in_place) { - UFuncHelper(args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array, true); + UFuncHelper( + args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array, true); } else { - UFuncHelper(args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array, false); + UFuncHelper( + args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array, false); } } else { UFuncHelper(args[0].operator NDArray*(), args[1].operator NDArray*(), out, ret, fn_array); diff --git a/src/operator/numpy/np_elemwise_broadcast_op.h b/src/operator/numpy/np_elemwise_broadcast_op.h index 87fc4b30ac0a..20395eea6ccc 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op.h +++ b/src/operator/numpy/np_elemwise_broadcast_op.h @@ -38,9 +38,8 @@ namespace op { struct NumpyBinaryParam : public dmlc::Parameter { bool in_place; DMLC_DECLARE_PARAMETER(NumpyBinaryParam) { - DMLC_DECLARE_FIELD(in_place) - .set_default(false) - .describe("Indicate whether this binary operation is in-place"); + DMLC_DECLARE_FIELD(in_place).set_default(false).describe( + "Indicate whether this binary operation is in-place"); } void SetAttrDict(std::unordered_map* dict) { @@ -346,18 +345,18 @@ void MixedBinaryBroadcastCompute(const nnvm::NodeAttrs& attrs, int ndim = BinaryBroadcastShapeCompact( lhs.shape_, rhs.shape_, out.shape_, &new_lshape, &new_rshape, &new_oshape); const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { TBlob temp_tblob; mshadow::Stream* s = ctx.get_stream(); MSHADOW_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); temp_tblob = TBlob(temp_tensor); }); CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); BinaryBroadcastComputeWithBool( - attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); } else if (!ndim) { MixedBinaryElemwiseCompute(attrs, ctx, inputs, req, outputs); } else { @@ -505,18 +504,18 @@ void NumpyBinaryBroadcastComputeWithBool(const nnvm::NodeAttrs& attrs, } const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { Stream* s = ctx.get_stream(); TBlob temp_tblob; MSHADOW_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); temp_tblob = TBlob(temp_tensor); }); CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); BinaryBroadcastComputeWithBool( - attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); } else if (!common::is_float(lhs.type_flag_) && !common::is_float(rhs.type_flag_)) { Stream* s = ctx.get_stream(); TBlob temp_tblob; @@ -588,16 +587,16 @@ void NumpyBinaryBroadcastIntComputeWithBool(const nnvm::NodeAttrs& attrs, Stream* s = ctx.get_stream(); TBlob temp_tblob; const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { MXNET_INT_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); temp_tblob = TBlob(temp_tensor); }); CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); BinaryBroadcastIntComputeWithBool( - attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); } else if (lhs.type_flag_ == out.type_flag_) { MXNET_INT_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { Tensor temp_tensor = @@ -927,7 +926,7 @@ inline bool NumpyBinaryMixedPrecisionType(const nnvm::NodeAttrs& attrs, const int ltype = in_attrs->at(0); const int rtype = in_attrs->at(1); const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { TYPE_ASSIGN_CHECK(*out_attrs, 0, ltype); return true; @@ -976,7 +975,7 @@ inline bool NumpyBinaryMixedIntPrecisionTypeWithBool(const nnvm::NodeAttrs& attr CHECK(common::is_int(rtype) || rtype == mshadow::kBool) << "2nd input only supports integer types or bool types."; const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { TYPE_ASSIGN_CHECK(*out_attrs, 0, ltype); return true; @@ -1023,7 +1022,7 @@ inline bool NumpyBinaryMixedIntPrecisionType(const nnvm::NodeAttrs& attrs, CHECK(common::is_int(ltype)) << "1st input only supports integer types."; CHECK(common::is_int(rtype)) << "2nd input only supports integer types."; const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { TYPE_ASSIGN_CHECK(*out_attrs, 0, ltype); return true; diff --git a/src/operator/numpy/np_true_divide-inl.h b/src/operator/numpy/np_true_divide-inl.h index 838aaa5f3fa4..2f81faf551f8 100644 --- a/src/operator/numpy/np_true_divide-inl.h +++ b/src/operator/numpy/np_true_divide-inl.h @@ -92,7 +92,7 @@ void TrueDivideElemwiseCompute(const nnvm::NodeAttrs& attrs, const TBlob& out = outputs[0]; const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { TBlob temp_tblob; MSHADOW_TYPE_SWITCH_EXT(lhs.type_flag_, LType, { @@ -122,7 +122,7 @@ void TrueDivideElemwiseCompute(const nnvm::NodeAttrs& attrs, // If both are the same integers, output is float32 or float64 CHECK_EQ(out.type_flag_, mxnet::common::GetDefaultDtype()) << "true_divide only supports float32 and float64" - " output when input's dtype is " + " output when input's dtype is " << type_string(lhs.type_flag_); MXNET_ASSIGN_REQ_SWITCH(req[0], Req, { MXNET_INT_TYPE_SWITCH(lhs.type_flag_, DType, { @@ -210,7 +210,7 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, int ndim = BinaryBroadcastShapeCompact( inputs[0].shape_, inputs[1].shape_, outputs[0].shape_, &new_lshape, &new_rshape, &new_oshape); const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (!ndim) { TrueDivideElemwiseCompute(attrs, ctx, inputs, req, outputs); } else { @@ -233,16 +233,16 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, }); CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); MSHADOW_TYPE_SWITCH_EXT(out.type_flag_, DType, { - Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - temp_tblob.dptr(), - out.dptr()); + Kernel, xpu>::template LaunchEx( + s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + temp_tblob.dptr(), + out.dptr()); }); } else { if (lhs.type_flag_ == rhs.type_flag_) { @@ -250,16 +250,16 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, if (common::is_float(lhs.type_flag_)) { // If both inputs are the same float types, output is the same float type MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, DType, { - Kernel, xpu>::template LaunchEx( - s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - rhs.dptr(), - out.dptr()); + Kernel, + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + rhs.dptr(), + out.dptr()); }); } else { CHECK_EQ(out.type_flag_, mxnet::common::GetDefaultDtype()) @@ -267,16 +267,16 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, << type_string(lhs.type_flag_); MXNET_INT_TYPE_SWITCH(lhs.type_flag_, DType, { // If both inputs are the same integer types, output is float type - Kernel, xpu>::template LaunchEx( - s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - rhs.dptr(), - out.dptr()); + Kernel, + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + rhs.dptr(), + out.dptr()); }); } } else { @@ -292,15 +292,15 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - temp_tblob.dptr(), - out.dptr()); + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + temp_tblob.dptr(), + out.dptr()); }); } else { MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { @@ -311,15 +311,15 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, CastCompute(attrs, ctx, {lhs}, {kWriteTo}, {temp_tblob}); MSHADOW_REAL_TYPE_SWITCH(out.type_flag_, DType, { Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - temp_tblob.dptr(), - rhs.dptr(), - out.dptr()); + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + temp_tblob.dptr(), + rhs.dptr(), + out.dptr()); }); } } else if (common::is_float(lhs.type_flag_) || common::is_float(rhs.type_flag_)) { @@ -331,15 +331,15 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, MSHADOW_REAL_TYPE_SWITCH(lhs.type_flag_, LType, { MXNET_INT_TYPE_SWITCH(rhs.type_flag_, RType, { Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - rstride, - lstride, - oshape, - rhs.dptr(), - lhs.dptr(), - out.dptr()); + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + rstride, + lstride, + oshape, + rhs.dptr(), + lhs.dptr(), + out.dptr()); }); }); } else { @@ -349,15 +349,15 @@ void TrueDivideBroadcastCompute(const nnvm::NodeAttrs& attrs, MXNET_INT_TYPE_SWITCH(lhs.type_flag_, LType, { MSHADOW_REAL_TYPE_SWITCH(rhs.type_flag_, RType, { Kernel, - xpu>::template LaunchEx(s, - new_oshape.Size(), - req[0], - lstride, - rstride, - oshape, - lhs.dptr(), - rhs.dptr(), - out.dptr()); + xpu>::template LaunchEx(s, + new_oshape.Size(), + req[0], + lstride, + rstride, + oshape, + lhs.dptr(), + rhs.dptr(), + out.dptr()); }); }); } diff --git a/src/operator/numpy/np_true_divide.cc b/src/operator/numpy/np_true_divide.cc index d272240bdc4d..b68fccff9562 100644 --- a/src/operator/numpy/np_true_divide.cc +++ b/src/operator/numpy/np_true_divide.cc @@ -54,7 +54,7 @@ bool TrueDivideType(const nnvm::NodeAttrs& attrs, const int lhs_dtype = in_attrs->at(0); const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; + bool is_inplace = param.in_place; if (is_inplace) { TYPE_ASSIGN_CHECK(*out_attrs, 0, lhs_dtype); return true; From a16534ae41209eeb8c99145c6f0f617815b47f24 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Fri, 10 Dec 2021 15:01:09 -0800 Subject: [PATCH 04/10] fix --- src/operator/numpy/np_elemwise_broadcast_op_mod.cc | 8 ++++---- tests/python/unittest/test_numpy_op.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/operator/numpy/np_elemwise_broadcast_op_mod.cc b/src/operator/numpy/np_elemwise_broadcast_op_mod.cc index f2de1b971737..e47a2f2bc96f 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op_mod.cc +++ b/src/operator/numpy/np_elemwise_broadcast_op_mod.cc @@ -29,10 +29,10 @@ namespace op { MXNET_OPERATOR_REGISTER_NP_BINARY_MIXED_PRECISION(_npi_mod) .set_attr("FCompute", - NumpyBinaryBroadcastComputeWithBool) + NumpyBinaryBroadcastCompute) .set_attr("FGradient", ElemwiseGradUseIn{"_backward_npi_broadcast_mod"}); NNVM_REGISTER_OP(_backward_npi_broadcast_mod) diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index d5e97628f6d4..622b7cab3943 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -11774,7 +11774,7 @@ def test_in_place_dtype(a_dtype, b_dtype, shape, op): if op in ('__ilshift__', '__irshift__', '__iand__', '__ior__', '__ixor__') and \ (a_dtype not in np.integer_dtypes or b_dtype not in np.integer_dtypes): return - if op in ('__itruediv__', '__idiv__') and \ + if op in ('__itruediv__', '__idiv__', '__imod__') and \ (a_dtype in np.boolean_dtypes or b_dtype in np.boolean_dtypes): return a = np.random.uniform(size=shape).astype(a_dtype) From 943318006edd92d87aa1060b0bdd7241fc3fe115 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Sun, 12 Dec 2021 21:49:58 -0800 Subject: [PATCH 05/10] fix --- src/operator/numpy/np_elemwise_broadcast_op.h | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/operator/numpy/np_elemwise_broadcast_op.h b/src/operator/numpy/np_elemwise_broadcast_op.h index 20395eea6ccc..4a776d73043e 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op.h +++ b/src/operator/numpy/np_elemwise_broadcast_op.h @@ -344,20 +344,7 @@ void MixedBinaryBroadcastCompute(const nnvm::NodeAttrs& attrs, mxnet::TShape new_lshape, new_rshape, new_oshape; int ndim = BinaryBroadcastShapeCompact( lhs.shape_, rhs.shape_, out.shape_, &new_lshape, &new_rshape, &new_oshape); - const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; - if (is_inplace) { - TBlob temp_tblob; - mshadow::Stream* s = ctx.get_stream(); - MSHADOW_TYPE_SWITCH_EXT_WITH_BOOL(lhs.type_flag_, LType, { - Tensor temp_tensor = - ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); - temp_tblob = TBlob(temp_tensor); - }); - CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); - BinaryBroadcastComputeWithBool( - attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); - } else if (!ndim) { + if (!ndim) { MixedBinaryElemwiseCompute(attrs, ctx, inputs, req, outputs); } else { mshadow::Stream* s = ctx.get_stream(); @@ -477,6 +464,22 @@ void NumpyBinaryBroadcastCompute(const nnvm::NodeAttrs& attrs, return; } + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TBlob temp_tblob; + mshadow::Stream* s = ctx.get_stream(); + MSHADOW_TYPE_SWITCH_EXT(lhs.type_flag_, LType, { + Tensor temp_tensor = + ctx.requested[0].get_space_typed(Shape1(rhs.Size()), s); + temp_tblob = TBlob(temp_tensor); + }); + CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); + BinaryBroadcastCompute( + attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + return; + } + MixedBinaryBroadcastCompute(attrs, ctx, inputs, req, outputs); } From 964f2333650ad3b509910b2ce91340a252924925 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Sun, 12 Dec 2021 23:17:45 -0800 Subject: [PATCH 06/10] fix true_divide --- src/operator/numpy/np_true_divide.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/operator/numpy/np_true_divide.cc b/src/operator/numpy/np_true_divide.cc index b68fccff9562..33570e280a61 100644 --- a/src/operator/numpy/np_true_divide.cc +++ b/src/operator/numpy/np_true_divide.cc @@ -53,11 +53,13 @@ bool TrueDivideType(const nnvm::NodeAttrs& attrs, } const int lhs_dtype = in_attrs->at(0); - const NumpyBinaryParam& param = nnvm::get(attrs.parsed); - bool is_inplace = param.in_place; - if (is_inplace) { - TYPE_ASSIGN_CHECK(*out_attrs, 0, lhs_dtype); - return true; + if (num_inputs == 2) { + const NumpyBinaryParam& param = nnvm::get(attrs.parsed); + bool is_inplace = param.in_place; + if (is_inplace) { + TYPE_ASSIGN_CHECK(*out_attrs, 0, lhs_dtype); + return true; + } } const int rhs_dtype = From 79d351c47f9efa0834194a6987ddbf733f8dd673 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Mon, 13 Dec 2021 09:32:59 -0800 Subject: [PATCH 07/10] fix tests --- tests/python/unittest/test_numpy_op.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 622b7cab3943..741ff46ec04c 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -11777,8 +11777,8 @@ def test_in_place_dtype(a_dtype, b_dtype, shape, op): if op in ('__itruediv__', '__idiv__', '__imod__') and \ (a_dtype in np.boolean_dtypes or b_dtype in np.boolean_dtypes): return - a = np.random.uniform(size=shape).astype(a_dtype) - b = np.random.uniform(size=shape).astype(b_dtype) + a = np.ones(shape, dtype=a_dtype) + b = np.ones(shape, dtype=b_dtype) getattr(a, op)(b) assert a.dtype == a_dtype npx.waitall() From a98a888aadcd99bb3771da7b4ef2a7a6d89b7344 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Mon, 13 Dec 2021 12:55:06 -0800 Subject: [PATCH 08/10] fix tests --- src/operator/numpy/np_elemwise_broadcast_op.h | 1 + tests/python/unittest/test_numpy_op.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/operator/numpy/np_elemwise_broadcast_op.h b/src/operator/numpy/np_elemwise_broadcast_op.h index 4a776d73043e..8bc9851c6144 100644 --- a/src/operator/numpy/np_elemwise_broadcast_op.h +++ b/src/operator/numpy/np_elemwise_broadcast_op.h @@ -519,6 +519,7 @@ void NumpyBinaryBroadcastComputeWithBool(const nnvm::NodeAttrs& attrs, CastCompute(attrs, ctx, {rhs}, {kWriteTo}, {temp_tblob}); BinaryBroadcastComputeWithBool( attrs, ctx, {temp_tblob.reshape(rhs.shape_), lhs}, req, outputs); + return; } else if (!common::is_float(lhs.type_flag_) && !common::is_float(rhs.type_flag_)) { Stream* s = ctx.get_stream(); TBlob temp_tblob; diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 741ff46ec04c..20c9cb5fea81 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -11760,6 +11760,7 @@ def array_values(low, high, shape): use_broadcast=False, equal_nan=True) +@use_np @pytest.mark.parametrize('a_dtype', np.numeric_dtypes + np.boolean_dtypes) @pytest.mark.parametrize('b_dtype', np.numeric_dtypes + np.boolean_dtypes) @pytest.mark.parametrize('shape', [ @@ -11768,8 +11769,7 @@ def array_values(low, high, shape): (5, 5) ]) @pytest.mark.parametrize('op', [ - '__iadd__', '__iand__', '__ior__', '__ixor__', '__isub__', '__imul__', '__imod__', - '__itruediv__', '__idiv__', '__ifloordiv__', '__ipow__', '__ilshift__', '__irshift__']) + '__iadd__']) def test_in_place_dtype(a_dtype, b_dtype, shape, op): if op in ('__ilshift__', '__irshift__', '__iand__', '__ior__', '__ixor__') and \ (a_dtype not in np.integer_dtypes or b_dtype not in np.integer_dtypes): @@ -11777,8 +11777,8 @@ def test_in_place_dtype(a_dtype, b_dtype, shape, op): if op in ('__itruediv__', '__idiv__', '__imod__') and \ (a_dtype in np.boolean_dtypes or b_dtype in np.boolean_dtypes): return - a = np.ones(shape, dtype=a_dtype) - b = np.ones(shape, dtype=b_dtype) + a = np.array(onp.random.uniform(1, 5, shape), dtype=a_dtype) + b = np.array(onp.random.uniform(1, 5, shape), dtype=b_dtype) getattr(a, op)(b) assert a.dtype == a_dtype npx.waitall() From 4c79b1636e0bf54f6ce03c4927cecf079d896401 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Mon, 13 Dec 2021 15:32:52 -0800 Subject: [PATCH 09/10] fix --- tests/python/unittest/test_numpy_op.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 20c9cb5fea81..a01b37995eef 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -11761,8 +11761,8 @@ def array_values(low, high, shape): @use_np -@pytest.mark.parametrize('a_dtype', np.numeric_dtypes + np.boolean_dtypes) -@pytest.mark.parametrize('b_dtype', np.numeric_dtypes + np.boolean_dtypes) +@pytest.mark.parametrize('a_dtype', np.numeric_dtypes) +@pytest.mark.parametrize('b_dtype', np.numeric_dtypes) @pytest.mark.parametrize('shape', [ (), (2, 0, 2, 2), @@ -11774,9 +11774,6 @@ def test_in_place_dtype(a_dtype, b_dtype, shape, op): if op in ('__ilshift__', '__irshift__', '__iand__', '__ior__', '__ixor__') and \ (a_dtype not in np.integer_dtypes or b_dtype not in np.integer_dtypes): return - if op in ('__itruediv__', '__idiv__', '__imod__') and \ - (a_dtype in np.boolean_dtypes or b_dtype in np.boolean_dtypes): - return a = np.array(onp.random.uniform(1, 5, shape), dtype=a_dtype) b = np.array(onp.random.uniform(1, 5, shape), dtype=b_dtype) getattr(a, op)(b) From 40367e5c33363b368a8cdb4b3c28d05664144988 Mon Sep 17 00:00:00 2001 From: barry-jin Date: Mon, 13 Dec 2021 18:49:45 -0800 Subject: [PATCH 10/10] fix tests --- tests/python/unittest/test_numpy_op.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index a01b37995eef..2356b6033211 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -11769,8 +11769,14 @@ def array_values(low, high, shape): (5, 5) ]) @pytest.mark.parametrize('op', [ - '__iadd__']) + '__iadd__', '__iand__', '__ior__', '__ixor__', '__isub__', '__imul__', '__imod__', + '__itruediv__', '__idiv__', '__ifloordiv__', '__ipow__', '__ilshift__', '__irshift__']) def test_in_place_dtype(a_dtype, b_dtype, shape, op): + try: + promote_type = np.result_type(a_dtype, b_dtype) + except Exception as e: + # Unkown type promotion between two types + return if op in ('__ilshift__', '__irshift__', '__iand__', '__ior__', '__ixor__') and \ (a_dtype not in np.integer_dtypes or b_dtype not in np.integer_dtypes): return