1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 /*!
21  * \file np_elemwise_binary_op_extended.cc
22  * \brief CPU Implementation of extended functions for elementwise numpy binary broadcast operator.
23  */
24 
25 #include <dmlc/strtonum.h>
26 #include "../../common/utils.h"
27 #include "./np_elemwise_broadcast_op.h"
28 
29 namespace mxnet {
30 namespace op {
31 
32 #define MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(name)                    \
33   NNVM_REGISTER_OP(name)                                                  \
34   .set_num_inputs(1)                                                      \
35   .set_num_outputs(1)                                                     \
36   .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)                   \
37   .set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>)       \
38   .set_attr<nnvm::FInferType>("FInferType", NumpyBinaryScalarType)        \
39   .set_attr<FResourceRequest>("FResourceRequest",                         \
40     [](const NodeAttrs& attrs) {                                          \
41       return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};   \
42     })                                                                    \
43   .add_argument("data", "NDArray-or-Symbol", "source input")              \
44   .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
45 
46 MXNET_OPERATOR_REGISTER_BINARY_BROADCAST(_npi_copysign)
47 .describe(R"code()code" ADD_FILELINE)
48 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastCompute<cpu, mshadow_op::copysign>)
49 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_copysign"});
50 
51 NNVM_REGISTER_OP(_backward_npi_copysign)
52 .set_num_inputs(3)
53 .set_num_outputs(2)
54 .set_attr<nnvm::TIsBackward>("TIsBackward", true)
55 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0102(const NodeAttrs& attrs)56   [](const NodeAttrs& attrs){
57     return std::vector<std::pair<int, int> >{{0, 1}};
58   })
59 .set_attr<FResourceRequest>("FResourceRequest",
__anon03a3cbee0202(const NodeAttrs& attrs) 60   [](const NodeAttrs& attrs) {
61     return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
62   })
63 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastBackwardUseIn<cpu, mshadow_op::copysign_grad,
64                                                                   mshadow_op::copysign_rgrad>);
65 
66 NNVM_REGISTER_OP(_npi_lcm)
67 .set_num_inputs(2)
68 .set_num_outputs(1)
69 .set_attr<nnvm::FListInputNames>("FListInputNames",
__anon03a3cbee0302(const NodeAttrs& attrs) 70 [](const NodeAttrs& attrs) {
71      return std::vector<std::string>{"lhs", "rhs"};
72 })
73 .set_attr<mxnet::FInferShape>("FInferShape", BinaryBroadcastShape)
74 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<2, 1>)
75 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0402(const NodeAttrs& attrs)76 [](const NodeAttrs& attrs){
77      return std::vector<std::pair<int, int> >{{0, 0}, {1, 0}};
78 })
79 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
80 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastIntCompute<cpu, mshadow_op::lcm>)
81 .add_argument("lhs", "NDArray-or-Symbol", "First input to the function")
82 .add_argument("rhs", "NDArray-or-Symbol", "Second input to the function");
83 
84 NNVM_REGISTER_OP(_npi_lcm_scalar)
85 .set_num_inputs(1)
86 .set_num_outputs(1)
87 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
88 .set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>)
89 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<1, 1>)
90 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0502(const NodeAttrs& attrs)91   [](const NodeAttrs& attrs){
92     return std::vector<std::pair<int, int> >{{0, 0}};
93   })
94 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
95 .add_argument("data", "NDArray-or-Symbol", "source input")
96 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
97 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Compute<cpu, mshadow_op::lcm>);
98 
99 NNVM_REGISTER_OP(_npi_bitwise_and)
100 .set_num_inputs(2)
101 .set_num_outputs(1)
102 .set_attr<nnvm::FListInputNames>("FListInputNames",
__anon03a3cbee0602(const NodeAttrs& attrs) 103 [](const NodeAttrs& attrs) {
104      return std::vector<std::string>{"lhs", "rhs"};
105 })
106 .set_attr<mxnet::FInferShape>("FInferShape", BinaryBroadcastShape)
107 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<2, 1>)
108 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0702(const NodeAttrs& attrs)109 [](const NodeAttrs& attrs){
110      return std::vector<std::pair<int, int> >{{0, 0}, {1, 0}};
111 })
112 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
113 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastIntCompute<cpu, mshadow_op::bitwise_and>)
114 .add_argument("lhs", "NDArray-or-Symbol", "First input to the function")
115 .add_argument("rhs", "NDArray-or-Symbol", "Second input to the function");
116 
117 NNVM_REGISTER_OP(_npi_bitwise_and_scalar)
118 .set_num_inputs(1)
119 .set_num_outputs(1)
120 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
121 .set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>)
122 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<1, 1>)
123 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0802(const NodeAttrs& attrs)124   [](const NodeAttrs& attrs){
125     return std::vector<std::pair<int, int> >{{0, 0}};
126   })
127 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
128 .add_argument("data", "NDArray-or-Symbol", "source input")
129 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
130 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::ComputeInt<cpu, mshadow_op::bitwise_and>);
131 
132 NNVM_REGISTER_OP(_npi_bitwise_xor)
133 .set_num_inputs(2)
134 .set_num_outputs(1)
135 .set_attr<nnvm::FListInputNames>("FListInputNames",
__anon03a3cbee0902(const NodeAttrs& attrs) 136 [](const NodeAttrs& attrs) {
137      return std::vector<std::string>{"lhs", "rhs"};
138 })
139 .set_attr<mxnet::FInferShape>("FInferShape", BinaryBroadcastShape)
140 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<2, 1>)
141 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0a02(const NodeAttrs& attrs)142 [](const NodeAttrs& attrs){
143      return std::vector<std::pair<int, int> >{{0, 0}, {1, 0}};
144 })
145 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
146 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastIntCompute<cpu, mshadow_op::bitwise_xor>)
147 .add_argument("lhs", "NDArray-or-Symbol", "First input to the function")
148 .add_argument("rhs", "NDArray-or-Symbol", "Second input to the function");
149 
150 NNVM_REGISTER_OP(_npi_bitwise_or)
151 .set_num_inputs(2)
152 .set_num_outputs(1)
153 .set_attr<nnvm::FListInputNames>("FListInputNames",
__anon03a3cbee0b02(const NodeAttrs& attrs) 154 [](const NodeAttrs& attrs) {
155      return std::vector<std::string>{"lhs", "rhs"};
156 })
157 .set_attr<mxnet::FInferShape>("FInferShape", BinaryBroadcastShape)
158 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<2, 1>)
159 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0c02(const NodeAttrs& attrs)160 [](const NodeAttrs& attrs){
161      return std::vector<std::pair<int, int> >{{0, 0}, {1, 0}};
162 })
163 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
164 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastIntCompute<cpu, mshadow_op::bitwise_or>)
165 .add_argument("lhs", "NDArray-or-Symbol", "First input to the function")
166 .add_argument("rhs", "NDArray-or-Symbol", "Second input to the function");
167 
168 NNVM_REGISTER_OP(_npi_bitwise_xor_scalar)
169 .set_num_inputs(1)
170 .set_num_outputs(1)
171 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
172 .set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>)
173 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<1, 1>)
174 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0d02(const NodeAttrs& attrs)175   [](const NodeAttrs& attrs){
176     return std::vector<std::pair<int, int> >{{0, 0}};
177   })
178 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
179 .add_argument("data", "NDArray-or-Symbol", "source input")
180 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
181 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::ComputeInt<cpu, mshadow_op::bitwise_xor>);
182 
183 NNVM_REGISTER_OP(_npi_bitwise_or_scalar)
184 .set_num_inputs(1)
185 .set_num_outputs(1)
186 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
187 .set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>)
188 .set_attr<nnvm::FInferType>("FInferType", ElemwiseIntType<1, 1>)
189 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee0e02(const NodeAttrs& attrs)190   [](const NodeAttrs& attrs){
191     return std::vector<std::pair<int, int> >{{0, 0}};
192   })
193 .set_attr<nnvm::FGradient>("FGradient", MakeZeroGradNodes)
194 .add_argument("data", "NDArray-or-Symbol", "source input")
195 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
196 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::ComputeInt<cpu, mshadow_op::bitwise_or>);
197 
198 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_npi_copysign_scalar)
199 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Compute<cpu, mshadow_op::copysign>)
200 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_copysign_scalar"});
201 
202 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_npi_rcopysign_scalar)
203 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Compute<cpu, mshadow_op::rcopysign>)
204 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_rcopysign_scalar"});
205 
206 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_backward_npi_copysign_scalar)
207 .set_attr<FCompute>("FCompute<cpu>",
208                     BinaryScalarOp::Backward<cpu, mshadow_op::copysign_grad>);
209 
210 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_backward_npi_rcopysign_scalar)
211 .set_attr<FCompute>("FCompute<cpu>",
212                     BinaryScalarOp::Backward<cpu, mshadow_op::rcopysign_grad>);
213 
Arctan2OpType(const nnvm::NodeAttrs & attrs,std::vector<int> * in_attrs,std::vector<int> * out_attrs)214 inline bool Arctan2OpType(const nnvm::NodeAttrs& attrs,
215                           std::vector<int>* in_attrs,
216                           std::vector<int>* out_attrs) {
217   CHECK_EQ(in_attrs->size(), 2U);
218   CHECK_EQ(out_attrs->size(), 1U);
219 
220   TYPE_ASSIGN_CHECK(*out_attrs, 0, in_attrs->at(0));
221   TYPE_ASSIGN_CHECK(*out_attrs, 0, in_attrs->at(1));
222   TYPE_ASSIGN_CHECK(*in_attrs, 0, out_attrs->at(0));
223   TYPE_ASSIGN_CHECK(*in_attrs, 1, out_attrs->at(0));
224   // check if it is float16, float32 or float64. If not, raise error.
225   CHECK(common::is_float(in_attrs->at(0))) << "Do not support `int` as input.\n";
226   return out_attrs->at(0) != -1;
227 }
228 
229 NNVM_REGISTER_OP(_npi_arctan2)
230 .set_num_inputs(2)
231 .set_num_outputs(1)
232 .set_attr<nnvm::FListInputNames>("FListInputNames",
__anon03a3cbee0f02(const NodeAttrs& attrs) 233   [](const NodeAttrs& attrs) {
234     return std::vector<std::string>{"x1", "x2"};
235   })
236 .set_attr<mxnet::FInferShape>("FInferShape", BinaryBroadcastShape)
237 .set_attr<nnvm::FInferType>("FInferType", Arctan2OpType)
238 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastCompute<cpu, mshadow_op::arctan2>)
239 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_arctan2"})
240 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee1002(const NodeAttrs& attrs) 241   [](const NodeAttrs& attrs) {
242     return std::vector<std::pair<int, int> >{{0, 0}};
243   })
244 .add_argument("x1", "NDArray-or-Symbol", "The input array")
245 .add_argument("x2", "NDArray-or-Symbol", "The input array");
246 
247 NNVM_REGISTER_OP(_backward_npi_arctan2)
248 .set_num_inputs(3)
249 .set_num_outputs(2)
250 .set_attr<nnvm::TIsBackward>("TIsBackward", true)
251 .set_attr<FResourceRequest>("FResourceRequest",
__anon03a3cbee1102(const NodeAttrs& attrs) 252   [](const NodeAttrs& attrs) {
253     return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
254   })
255 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastBackwardUseIn<cpu, mshadow_op::arctan2_grad,
256                                                                   mshadow_op::arctan2_rgrad>);
257 
258 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_npi_arctan2_scalar)
259 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Compute<cpu, mshadow_op::arctan2>)
260 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_arctan2_scalar"});
261 
262 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_npi_rarctan2_scalar)
263 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Compute<cpu, mshadow_op::rarctan2>)
264 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_rarctan2_scalar"});
265 
266 MXNET_OPERATOR_REGISTER_BINARY(_backward_npi_arctan2_scalar)
267 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
268 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
269 .set_attr<FCompute>("FCompute<cpu>",
270                     BinaryScalarOp::Backward<cpu, mshadow_op::arctan2_grad>);
271 
272 MXNET_OPERATOR_REGISTER_BINARY(_backward_npi_rarctan2_scalar)
273 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
274 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
275 .set_attr<FCompute>("FCompute<cpu>",
276                     BinaryScalarOp::Backward<cpu, mshadow_op::arctan2_rgrad>);
277 
HypotOpType(const nnvm::NodeAttrs & attrs,std::vector<int> * in_attrs,std::vector<int> * out_attrs)278 bool HypotOpType(const nnvm::NodeAttrs& attrs,
279                  std::vector<int>* in_attrs,
280                  std::vector<int>* out_attrs) {
281   CHECK_EQ(in_attrs->size(), 2U);
282   CHECK_EQ(out_attrs->size(), 1U);
283 
284   TYPE_ASSIGN_CHECK(*out_attrs, 0, in_attrs->at(0));
285   TYPE_ASSIGN_CHECK(*out_attrs, 0, in_attrs->at(1));
286   TYPE_ASSIGN_CHECK(*in_attrs, 0, out_attrs->at(0));
287   TYPE_ASSIGN_CHECK(*in_attrs, 1, out_attrs->at(0));
288 
289   CHECK(common::is_float(in_attrs->at(0))) << "Do not support `int` as input.\n";
290   return out_attrs->at(0) != -1;
291 }
292 
293 // rigister hypot that do not support int here
294 NNVM_REGISTER_OP(_npi_hypot)
295 .set_num_inputs(2)
296 .set_num_outputs(1)
297 .set_attr<nnvm::FListInputNames>("FListInputNames",
__anon03a3cbee1202(const NodeAttrs& attrs) 298   [](const NodeAttrs& attrs) {
299     return std::vector<std::string>{"x1", "x2"};
300   })
301 .set_attr<mxnet::FInferShape>("FInferShape", BinaryBroadcastShape)
302 .set_attr<nnvm::FInferType>("FInferType", HypotOpType)
303 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastCompute<cpu, mshadow_op::hypot>)
304 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_hypot"})
305 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee1302(const NodeAttrs& attrs) 306   [](const NodeAttrs& attrs) {
307     return std::vector<std::pair<int, int> >{{0, 0}, {1, 0}};
308   })
309 .add_argument("x1", "NDArray-or-Symbol", "The input array")
310 .add_argument("x2", "NDArray-or-Symbol", "The input array");
311 
312 NNVM_REGISTER_OP(_backward_npi_hypot)
313 .set_num_inputs(3)
314 .set_num_outputs(2)
315 .set_attr<nnvm::TIsBackward>("TIsBackward", true)
316 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee1402(const NodeAttrs& attrs) 317   [](const NodeAttrs& attrs) {
318     return std::vector<std::pair<int, int> > {{0, 1}};
319   })
320 .set_attr<FResourceRequest>("FResourceRequest",
__anon03a3cbee1502(const NodeAttrs& attrs) 321   [](const NodeAttrs& attrs) {
322     return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
323   })
324 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastBackwardUseIn<cpu, mshadow_op::hypot_grad_left,
325                                                                   mshadow_op::hypot_grad_right>);
326 
327 MXNET_OPERATOR_REGISTER_BINARY_BROADCAST(_npi_ldexp)
328 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastCompute<cpu, mshadow_op::ldexp>)
329 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_ldexp"});
330 
331 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_npi_ldexp_scalar)
332 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Compute<cpu, mshadow_op::ldexp>)
333 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_ldexp_scalar"});
334 
335 MXNET_OPERATOR_REGISTER_NP_BINARY_SCALAR(_npi_rldexp_scalar)
336 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Compute<cpu, mshadow_op::rldexp>)
337 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_npi_rldexp_scalar"});
338 
339 NNVM_REGISTER_OP(_backward_npi_ldexp)
340 .set_num_inputs(3)
341 .set_num_outputs(2)
342 .set_attr<nnvm::TIsBackward>("TIsBackward", true)
343 .set_attr<nnvm::FInplaceOption>("FInplaceOption",
__anon03a3cbee1602(const NodeAttrs& attrs)344   [](const NodeAttrs& attrs){
345     return std::vector<std::pair<int, int> >{{0, 1}};
346   })
347 .set_attr<FResourceRequest>("FResourceRequest",
__anon03a3cbee1702(const NodeAttrs& attrs) 348   [](const NodeAttrs& attrs) {
349     return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
350   })
351 .set_attr<FCompute>("FCompute<cpu>", BinaryBroadcastBackwardUseIn<cpu, mshadow_op::ldexp_grad,
352                                                                   mshadow_op::ldexp_rgrad>);
353 
354 MXNET_OPERATOR_REGISTER_BINARY(_backward_npi_ldexp_scalar)
355 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
356 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
357 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Backward<cpu, mshadow_op::ldexp_grad>);
358 
359 MXNET_OPERATOR_REGISTER_BINARY(_backward_npi_rldexp_scalar)
360 .add_arguments(NumpyBinaryScalarParam::__FIELDS__())
361 .set_attr_parser(ParamParser<NumpyBinaryScalarParam>)
362 .set_attr<FCompute>("FCompute<cpu>", BinaryScalarOp::Backward<cpu, mshadow_op::rldexp_grad>);
363 
364 }  // namespace op
365 }  // namespace mxnet
366