|
| 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 index_copy-inl.h |
| 22 | + * \brief implementation of index_copy tensor operation |
| 23 | + */ |
| 24 | + |
| 25 | +#ifndef MXNET_OPERATOR_CONTRIB_INDEX_COPY_INL_H_ |
| 26 | +#define MXNET_OPERATOR_CONTRIB_INDEX_COPY_INL_H_ |
| 27 | + |
| 28 | +#include <mxnet/operator_util.h> |
| 29 | +#include <vector> |
| 30 | +#include <limits> |
| 31 | +#include <algorithm> |
| 32 | +#include "../elemwise_op_common.h" |
| 33 | +#include "../mshadow_op.h" |
| 34 | +#include "../mxnet_op.h" |
| 35 | + |
| 36 | +namespace mxnet { |
| 37 | +namespace op { |
| 38 | + |
| 39 | +template<int req> |
| 40 | +struct index_copy_forward { |
| 41 | + template<typename DType, typename IType> |
| 42 | + MSHADOW_XINLINE static void Map(int i, |
| 43 | + int dim, |
| 44 | + IType* index, |
| 45 | + DType* new_tensor, |
| 46 | + DType* out_tensor) { |
| 47 | + DType* out_ptr = out_tensor + static_cast<int>(index[i]) * dim; |
| 48 | + DType* new_ptr = new_tensor + i * dim; |
| 49 | + for (int idx = 0; idx < dim; ++idx) { |
| 50 | + KERNEL_ASSIGN(out_ptr[idx], req, new_ptr[idx]); |
| 51 | + } |
| 52 | + } |
| 53 | +}; |
| 54 | + |
| 55 | +template<typename xpu> |
| 56 | +void IndexCopyForward(const nnvm::NodeAttrs& attrs, |
| 57 | + const OpContext& ctx, |
| 58 | + const std::vector<TBlob>& inputs, |
| 59 | + const std::vector<OpReqType>& req, |
| 60 | + const std::vector<TBlob>& outputs) { |
| 61 | + CHECK_EQ(inputs.size(), 3U); |
| 62 | + CHECK_EQ(outputs.size(), 1U); |
| 63 | + CHECK_EQ(req.size(), 1U); |
| 64 | + mshadow::Stream<xpu> *s = ctx.get_stream<xpu>(); |
| 65 | + const TBlob& out = outputs[0]; |
| 66 | + const TBlob& original_tensor = inputs[0]; |
| 67 | + const TBlob& idx_vector = inputs[1]; |
| 68 | + const TBlob& copied_tensor = inputs[2]; |
| 69 | + int dim = inputs[2].Size() / inputs[1].Size(); |
| 70 | + // copy original tensor to output |
| 71 | + mxnet_op::copy(s, out, original_tensor); |
| 72 | + // index copy |
| 73 | + MSHADOW_TYPE_SWITCH(out.type_flag_, DType, { |
| 74 | + MSHADOW_TYPE_SWITCH(idx_vector.type_flag_, IType, { |
| 75 | + MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, { |
| 76 | + mxnet_op::Kernel<index_copy_forward<req_type>, xpu>::Launch(s, |
| 77 | + idx_vector.Size(), dim, |
| 78 | + idx_vector.dptr<IType>(), |
| 79 | + copied_tensor.dptr<DType>(), |
| 80 | + out.dptr<DType>()); |
| 81 | + }); |
| 82 | + }); |
| 83 | + }); |
| 84 | +} |
| 85 | + |
| 86 | +template<int req> |
| 87 | +struct index_copy_backward { |
| 88 | + template<typename DType, typename IType> |
| 89 | + MSHADOW_XINLINE static void Map(int i, |
| 90 | + int dim, |
| 91 | + int index_size, |
| 92 | + DType* out_grad, |
| 93 | + IType* index, |
| 94 | + DType* in_grad_1, |
| 95 | + DType* in_grad_2) { |
| 96 | + // Copy to in_grad_2 |
| 97 | + for (int p = 0; p < index_size; ++p) { |
| 98 | + int idx = static_cast<int>(index[p]); |
| 99 | + if (i >= idx*dim && i < (idx+1)*dim) { |
| 100 | + int offset = i - idx*dim; |
| 101 | + KERNEL_ASSIGN(in_grad_2[p*dim+offset], req, out_grad[i]); |
| 102 | + return; |
| 103 | + } |
| 104 | + } |
| 105 | + // Copy to in_grad_1 |
| 106 | + KERNEL_ASSIGN(in_grad_1[i], req, out_grad[i]); |
| 107 | + } |
| 108 | +}; |
| 109 | + |
| 110 | +template<typename xpu> |
| 111 | +void IndexCopyBackward(const nnvm::NodeAttrs& attrs, |
| 112 | + const OpContext& ctx, |
| 113 | + const std::vector<TBlob>& inputs, |
| 114 | + const std::vector<OpReqType>& req, |
| 115 | + const std::vector<TBlob>& outputs) { |
| 116 | + CHECK_EQ(inputs.size(), 4U); |
| 117 | + CHECK_EQ(outputs.size(), 3U); |
| 118 | + mshadow::Stream<xpu> *s = ctx.get_stream<xpu>(); |
| 119 | + const TBlob& out_grad = inputs[0]; |
| 120 | + const TBlob& index = inputs[2]; |
| 121 | + const TBlob& in_grad_1 = outputs[0]; |
| 122 | + const TBlob& in_grad_2 = outputs[2]; |
| 123 | + int dim = inputs[3].Size() / inputs[2].Size(); |
| 124 | + int index_size = inputs[2].Size(); |
| 125 | + // index_copy_backward |
| 126 | + MSHADOW_TYPE_SWITCH(out_grad.type_flag_, DType, { |
| 127 | + MSHADOW_TYPE_SWITCH(index.type_flag_, IType, { |
| 128 | + MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, { |
| 129 | + mxnet_op::Kernel<index_copy_backward<req_type>, xpu>::Launch(s, |
| 130 | + out_grad.Size(), |
| 131 | + dim, index_size, |
| 132 | + out_grad.dptr<DType>(), |
| 133 | + index.dptr<IType>(), |
| 134 | + in_grad_1.dptr<DType>(), |
| 135 | + in_grad_2.dptr<DType>()); |
| 136 | + }); |
| 137 | + }); |
| 138 | + }); |
| 139 | +} |
| 140 | + |
| 141 | +inline bool IndexCopyShape(const nnvm::NodeAttrs& attrs, |
| 142 | + std::vector<TShape> *in_attrs, |
| 143 | + std::vector<TShape> *out_attrs) { |
| 144 | + // inputs[0]: original tensor |
| 145 | + // inputs[1]: index vector |
| 146 | + // inputs[2]: copied tensor |
| 147 | + CHECK_EQ(in_attrs->size(), 3U); |
| 148 | + // outputs[0]: a new tensor |
| 149 | + CHECK_EQ(out_attrs->size(), 1U); |
| 150 | + // inputs[1] must be a vector |
| 151 | + CHECK_EQ(in_attrs->at(1).ndim(), 1); |
| 152 | + // Shape matching |
| 153 | + CHECK_EQ(in_attrs->at(0).ndim(), in_attrs->at(2).ndim()); |
| 154 | + for (size_t i = 0; i < in_attrs->at(0).ndim(); ++i) { |
| 155 | + if (i == 0) { |
| 156 | + CHECK_GE(in_attrs->at(0)[i], in_attrs->at(2)[i]); |
| 157 | + } else { |
| 158 | + CHECK_EQ(in_attrs->at(0)[i], in_attrs->at(2)[i]); |
| 159 | + } |
| 160 | + } |
| 161 | + // The the length of the fitrst dim of copied tensor |
| 162 | + // must equal to the size of index vector |
| 163 | + CHECK_EQ(in_attrs->at(1)[0], in_attrs->at(2)[0]); |
| 164 | + SHAPE_ASSIGN_CHECK(*out_attrs, 0, in_attrs->at(0)); |
| 165 | + SHAPE_ASSIGN_CHECK(*in_attrs, 0, out_attrs->at(0)); |
| 166 | + return out_attrs->at(0).ndim() != 0U && |
| 167 | + out_attrs->at(0).Size() != 0U; |
| 168 | +} |
| 169 | + |
| 170 | +} // namespace op |
| 171 | +} // namespace mxnet |
| 172 | + |
| 173 | +#endif // MXNET_OPERATOR_CONTRIB_INDEX_COPY_INL_H_ |
0 commit comments