blob: a1d584b8b7518c785eb7d84a9e0ac4fac22bd523 [file] [log] [blame]
/*
* 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.
*/
/*!
* \file broadcast_reduce_op_value.cc
* \brief CPU Implementation of broadcast and reduce functions based on value.
*/
#include "./broadcast_reduce_op.h"
namespace mxnet {
namespace op {
DMLC_REGISTER_PARAMETER(ReduceAxesParam);
DMLC_REGISTER_PARAMETER(ReduceAxisParam);
DMLC_REGISTER_PARAMETER(BroadcastAxesParam);
DMLC_REGISTER_PARAMETER(BroadcastToParam);
DMLC_REGISTER_PARAMETER(BroadcastLikeParam);
template <typename DType>
void BroadcastAxisKer(DType* src, DType* dst, index_t outer, index_t inner, index_t size) {
#pragma omp parallel for num_threads(engine::OpenMP::Get()->GetRecommendedOMPThreadCount())
for (index_t i = 0; i < outer * size; i++) {
const index_t m = i / size;
const index_t n = i % size;
void* offset = reinterpret_cast<void*>(dst + m * size * inner + n * inner);
memcpy(offset, reinterpret_cast<void*>(src + m * inner), inner * sizeof(DType));
}
}
inline void BroadcastAxisComputeCPU(const nnvm::NodeAttrs& attrs,
const OpContext& ctx,
const std::vector<TBlob>& inputs,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& outputs) {
using namespace mshadow;
const BroadcastAxesParam& param = nnvm::get<BroadcastAxesParam>(attrs.parsed);
if (param.axis.ndim() == 1 && inputs[0].shape_[param.axis[0]] == 1 && req[0] == kWriteTo) {
MSHADOW_TYPE_SWITCH(outputs[0].type_flag_, DType, {
auto dst = outputs[0].dptr<DType>();
auto src = inputs[0].dptr<DType>();
index_t outer = inputs[0].shape_.ProdShape(0, param.axis[0]);
index_t inner = inputs[0].shape_.ProdShape(param.axis[0], inputs[0].shape_.ndim());
BroadcastAxisKer(src, dst, outer, inner, param.size[0]);
});
} else {
BroadcastComputeImpl<cpu>(attrs, ctx, inputs, req, outputs, inputs[0].shape_);
}
}
MXNET_OPERATOR_REGISTER_BROADCAST(broadcast_axis)
.add_alias("broadcast_axes")
.describe(R"code(Broadcasts the input array over particular axes.
Broadcasting is allowed on axes with size 1, such as from `(2,1,3,1)` to
`(2,8,3,9)`. Elements will be duplicated on the broadcasted axes.
`broadcast_axes` is an alias to the function `broadcast_axis`.
Example::
// given x of shape (1,2,1)
x = [[[ 1.],
[ 2.]]]
// broadcast x on on axis 2
broadcast_axis(x, axis=2, size=3) = [[[ 1., 1., 1.],
[ 2., 2., 2.]]]
// broadcast x on on axes 0 and 2
broadcast_axis(x, axis=(0,2), size=(2,3)) = [[[ 1., 1., 1.],
[ 2., 2., 2.]],
[[ 1., 1., 1.],
[ 2., 2., 2.]]]
)code" ADD_FILELINE)
.set_attr_parser(ParamParser<BroadcastAxesParam>)
.add_arguments(BroadcastAxesParam::__FIELDS__())
.set_attr<mxnet::FInferShape>("FInferShape", BroadcastAxesShape)
.set_attr<FCompute>("FCompute<cpu>", BroadcastAxisComputeCPU);
MXNET_OPERATOR_REGISTER_BROADCAST(broadcast_to)
.describe(R"code(Broadcasts the input array to a new shape.
Broadcasting is a mechanism that allows NDArrays to perform arithmetic operations
with arrays of different shapes efficiently without creating multiple copies of arrays.
Also see, `Broadcasting <https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html>`_ for more explanation.
Broadcasting is allowed on axes with size 1, such as from `(2,1,3,1)` to
`(2,8,3,9)`. Elements will be duplicated on the broadcasted axes.
For example::
broadcast_to([[1,2,3]], shape=(2,3)) = [[ 1., 2., 3.],
[ 1., 2., 3.]])
The dimension which you do not want to change can also be kept as `0` which means copy the original value.
So with `shape=(2,0)`, we will obtain the same result as in the above example.
)code" ADD_FILELINE)
.set_attr_parser(ParamParser<BroadcastToParam>)
.add_arguments(BroadcastToParam::__FIELDS__())
.set_attr<mxnet::FInferShape>("FInferShape", BroadcastToShape)
.set_attr<FCompute>("FCompute<cpu>", BroadcastCompute<cpu>);
// backward op for broadcast.
NNVM_REGISTER_OP(_broadcast_backward)
.set_attr_parser(ParamParser<ReduceAxesParam>)
.set_attr<nnvm::TIsBackward>("TIsBackward", true)
.set_attr<FCompute>("FCompute<cpu>", ReduceAxesCompute<cpu, mshadow::red::sum>)
.set_attr<FResourceRequest>("FResourceRequest", [](const NodeAttrs& attrs) {
return std::vector<ResourceRequest>{ResourceRequest::kTempSpace};
});
NNVM_REGISTER_OP(broadcast_like)
.add_alias("_npx_broadcast_like")
.set_num_inputs(2)
.set_num_outputs(1)
.set_attr<nnvm::FListInputNames>("FListInputNames",
[](const NodeAttrs& attrs) {
return std::vector<std::string>{"lhs", "rhs"};
})
.set_attr<nnvm::FInferType>(
"FInferType",
[](const nnvm::NodeAttrs& attrs, std::vector<int>* in_attrs, std::vector<int>* out_attrs) {
CHECK_EQ(in_attrs->size(), 2) << " in operator " << attrs.name;
std::vector<int> checked_in_attrs = {(*in_attrs)[0]};
bool ret = !type_is_none((*in_attrs)[1]) &&
ElemwiseType<1, 1>(attrs, &checked_in_attrs, out_attrs);
(*in_attrs)[0] = checked_in_attrs[0];
return ret;
})
.set_attr<nnvm::FGradient>(
"FGradient",
[](const nnvm::ObjectPtr& n, const std::vector<nnvm::NodeEntry>& ograds) {
if (CheckGradAllZero(ograds))
return MakeZeroGradNodes(n, ograds);
std::vector<nnvm::NodeEntry> lhs =
MakeNonlossGradNode("_broadcast_backward", n, ograds, {}, {{"keepdims", "true"}});
lhs.emplace_back(
MakeNode("zeros_like", n->attrs.name + "_rhs_backward", {n->inputs[1]}, nullptr, &n));
return lhs;
})
.add_argument("lhs", "NDArray-or-Symbol", "First input.")
.add_argument("rhs", "NDArray-or-Symbol", "Second input.")
.describe(R"code(Broadcasts lhs to have the same shape as rhs.
Broadcasting is a mechanism that allows NDArrays to perform arithmetic operations
with arrays of different shapes efficiently without creating multiple copies of arrays.
Also see, `Broadcasting <https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html>`_ for more explanation.
Broadcasting is allowed on axes with size 1, such as from `(2,1,3,1)` to
`(2,8,3,9)`. Elements will be duplicated on the broadcasted axes.
For example::
broadcast_like([[1,2,3]], [[5,6,7],[7,8,9]]) = [[ 1., 2., 3.],
[ 1., 2., 3.]])
broadcast_like([9], [1,2,3,4,5], lhs_axes=(0,), rhs_axes=(-1,)) = [9,9,9,9,9]
)code" ADD_FILELINE)
.set_attr_parser(ParamParser<BroadcastLikeParam>)
.add_arguments(BroadcastLikeParam::__FIELDS__())
.set_attr<mxnet::FInferShape>("FInferShape", BroadcastLikeShape)
.set_attr<FCompute>("FCompute<cpu>", BroadcastCompute<cpu>);
} // namespace op
} // namespace mxnet