diff --git a/python/mxnet/ndarray/ndarray.py b/python/mxnet/ndarray/ndarray.py index 49a4406aa0ff..6b75a893a382 100644 --- a/python/mxnet/ndarray/ndarray.py +++ b/python/mxnet/ndarray/ndarray.py @@ -3560,13 +3560,15 @@ def _ufunc_helper(lhs, rhs, fn_array, fn_scalar, lfn_scalar, rfn_scalar=None): if isinstance(rhs, numeric_types): return fn_scalar(lhs, rhs) else: + is_int = isinstance(lhs, integer_types) if rfn_scalar is None: # commutative function - return lfn_scalar(rhs, float(lhs)) + return lfn_scalar(rhs, scalar=float(lhs)) else: - return rfn_scalar(rhs, float(lhs)) + return rfn_scalar(rhs, scalar=float(lhs)) elif isinstance(rhs, numeric_types): - return lfn_scalar(lhs, float(rhs)) + is_int = isinstance(rhs, integer_types) + return lfn_scalar(lhs, scalar=float(rhs)) elif isinstance(rhs, NDArray): return fn_array(lhs, rhs) else: diff --git a/src/operator/contrib/gradient_multiplier_op.cc b/src/operator/contrib/gradient_multiplier_op.cc index 47f891ef802b..f1664a3eaac4 100644 --- a/src/operator/contrib/gradient_multiplier_op.cc +++ b/src/operator/contrib/gradient_multiplier_op.cc @@ -76,9 +76,7 @@ In forward pass it acts as an identity transform. During backpropagation it multiplies the gradient from the subsequent level by a scalar factor lambda and passes it to the preceding layer. )code" ADD_FILELINE) -.set_attr_parser([](NodeAttrs* attrs) { - attrs->parsed = std::stod(attrs->dict["scalar"]); - }) +.set_attr_parser(ParamParser) .set_attr("FInferStorageType", ElemwiseStorageType<1, 1, false, true, true>) .set_attr("FCompute", UnaryOp::IdentityCompute) .set_attr("FComputeEx", UnaryOp::IdentityComputeEx) @@ -87,7 +85,7 @@ the preceding layer. [](const NodeAttrs& attrs){ return std::vector{true}; }) -.add_argument("scalar", "float", "lambda multiplier"); +.add_arguments(NumpyBinaryScalarParam::__FIELDS__()); MXNET_OPERATOR_REGISTER_BINARY_SCALAR(_contrib_backward_gradientmultiplier) .set_attr("TIsBackward", true) diff --git a/src/operator/numpy/np_elemwise_broadcast_logic_op.cc b/src/operator/numpy/np_elemwise_broadcast_logic_op.cc index b44a22d65856..8c8320de30c6 100644 --- a/src/operator/numpy/np_elemwise_broadcast_logic_op.cc +++ b/src/operator/numpy/np_elemwise_broadcast_logic_op.cc @@ -223,7 +223,8 @@ struct TVMBinaryBroadcastScalarCompute { // scalar param type_codes[1] = kDLFloat; - values[1].v_float64 = nnvm::get(attrs.parsed); + const NumpyBinaryScalarParam& param = nnvm::get(attrs.parsed); + values[1].v_float64 = param.scalar; // output tensor type_codes[2] = kTVMDLTensorHandle; diff --git a/src/operator/numpy/np_true_divide-inl.h b/src/operator/numpy/np_true_divide-inl.h index 3132097b7cb5..c6b8cdfd7852 100644 --- a/src/operator/numpy/np_true_divide-inl.h +++ b/src/operator/numpy/np_true_divide-inl.h @@ -47,7 +47,8 @@ void TrueDivideScalarCompute(const nnvm::NodeAttrs &attrs, using namespace mxnet_op; using namespace mshadow::expr; Stream *s = ctx.get_stream(); - const double alpha = nnvm::get(attrs.parsed); + const NumpyBinaryScalarParam& param = nnvm::get(attrs.parsed); + const double alpha = param.scalar; const TBlob& data = inputs[0]; const TBlob& out = outputs[0]; if (out.type_flag_ == data.type_flag_) { diff --git a/src/operator/numpy/np_true_divide.cc b/src/operator/numpy/np_true_divide.cc index bf7324be5d16..7311d896d71c 100644 --- a/src/operator/numpy/np_true_divide.cc +++ b/src/operator/numpy/np_true_divide.cc @@ -104,9 +104,7 @@ NNVM_REGISTER_OP(_backward_npi_broadcast_div) NNVM_REGISTER_OP(_npi_true_divide_scalar) .set_num_inputs(1) .set_num_outputs(1) -.set_attr_parser([](NodeAttrs* attrs) { - attrs->parsed = std::stod(attrs->dict["scalar"]); - }) +.set_attr_parser(ParamParser) .set_attr("FInferShape", ElemwiseShape<1, 1>) .set_attr("FInferType", TrueDivideType<1>) .set_attr("FInplaceOption", @@ -122,14 +120,12 @@ NNVM_REGISTER_OP(_npi_true_divide_scalar) .set_attr("FCompute", TrueDivideScalarCompute) .set_attr("FGradient", ElemwiseGradUseNone{"_backward_div_scalar"}) .add_argument("data", "NDArray-or-Symbol", "source input") -.add_argument("scalar", "float", "scalar input"); +.add_arguments(NumpyBinaryScalarParam::__FIELDS__()); NNVM_REGISTER_OP(_npi_rtrue_divide_scalar) .set_num_inputs(1) .set_num_outputs(1) -.set_attr_parser([](NodeAttrs* attrs) { - attrs->parsed = std::stod(attrs->dict["scalar"]); - }) +.set_attr_parser(ParamParser) .set_attr("FInferShape", ElemwiseShape<1, 1>) .set_attr("FInferType", TrueDivideType<1>) .set_attr("FInplaceOption", @@ -145,7 +141,7 @@ NNVM_REGISTER_OP(_npi_rtrue_divide_scalar) .set_attr("FCompute", TrueDivideScalarCompute) .set_attr("FGradient", ElemwiseGradUseIn{"_backward_rdiv_scalar"}) .add_argument("data", "NDArray-or-Symbol", "source input") -.add_argument("scalar", "float", "scalar input"); +.add_arguments(NumpyBinaryScalarParam::__FIELDS__()); } // namespace op } // namespace mxnet diff --git a/src/operator/tensor/elemwise_binary_scalar_op.h b/src/operator/tensor/elemwise_binary_scalar_op.h index 3e3a1e3bdad4..c09e41867f46 100644 --- a/src/operator/tensor/elemwise_binary_scalar_op.h +++ b/src/operator/tensor/elemwise_binary_scalar_op.h @@ -44,7 +44,7 @@ struct NumpyBinaryScalarParam : public dmlc::Parameter { .set_default(1) .describe("Scalar input value"); DMLC_DECLARE_FIELD(is_int) - .set_default(false) + .set_default(true) .describe("Indicate whether scalar input is int type"); } diff --git a/tests/python/unittest/test_numpy_op.py b/tests/python/unittest/test_numpy_op.py index 3bb0668af7bb..fca7c71f4b57 100644 --- a/tests/python/unittest/test_numpy_op.py +++ b/tests/python/unittest/test_numpy_op.py @@ -2719,7 +2719,7 @@ def hybrid_forward(self, F, a, *args, **kwargs): mx_test_x1 = np.array(np_test_x1, dtype=ltype) mx_test_x2 = np_test_x2 np_func = getattr(_np, func) - mx_func = TestBinaryScalar(func, np_test_x2) + mx_func = TestBinaryScalar(func, mx_test_x2) if hybridize: mx_func.hybridize() rtol = 1e-2 if ltype is np.float16 else 1e-3 @@ -2729,14 +2729,14 @@ def hybrid_forward(self, F, a, *args, **kwargs): mx_test_x1.attach_grad() np_out = np_func(np_test_x1, np_test_x2) with mx.autograd.record(): - y = mx_func(mx_test_x1, mx_test_x2) + y = mx_func(mx_test_x1) assert y.shape == np_out.shape assert_almost_equal(y.asnumpy(), np_out.astype(y.dtype), rtol=rtol, atol=atol) if lgrad: y.backward() assert_almost_equal(mx_test_x1.grad.asnumpy(), collapse_sum_like(lgrad(y.asnumpy(), np_test_x1, np_test_x2), mx_test_x1.shape), - rtol=1e-1, atol=1e-2, equal_nan=True, use_broadcast=False) + rtol=rtol, atol=atol, equal_nan=True, use_broadcast=False) # Test imperative np_out = getattr(_np, func)(np_test_x1, np_test_x2) @@ -2757,7 +2757,7 @@ def hybrid_forward(self, F, a, *args, **kwargs): flags = [True, False] for func, func_data in funcs.items(): low, high, lgrad = func_data - for shape, ltype, is_int, is_int, hybridize in itertools.product(shapes, ltypes, flags, flags): + for shape, ltype, is_int, hybridize in itertools.product(shapes, ltypes, flags, flags): check_binary_scalar_func(func, low, high, shape, lgrad, ltype, is_int, hybridize)