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_insert_op-inl.h
22 * \brief Function definition of insert operators
23 */
24 #ifndef MXNET_OPERATOR_NUMPY_NP_INSERT_OP_INL_H_
25 #define MXNET_OPERATOR_NUMPY_NP_INSERT_OP_INL_H_
26
27 #include <vector>
28 #include <memory>
29 #include <algorithm>
30 #include "../../common/utils.h"
31 #include "../tensor/sort_op.h"
32 #include "../tensor/init_op.h"
33 #include "../operator_common.h"
34 #include "../mxnet_op.h"
35 #include "./np_delete_op-inl.h"
36 namespace mxnet {
37 namespace op {
38
39 struct NumpyInsertParam : public dmlc::Parameter<NumpyInsertParam> {
40 dmlc::optional<double> val;
41 dmlc::optional<int> start;
42 dmlc::optional<int> stop;
43 dmlc::optional<int> step;
44 dmlc::optional<int> int_ind;
45 dmlc::optional<int> axis;
DMLC_DECLARE_PARAMETERNumpyInsertParam46 DMLC_DECLARE_PARAMETER(NumpyInsertParam) {
47 DMLC_DECLARE_FIELD(val)
48 .set_default(dmlc::optional<double>())
49 .describe("A scaler to be inserted into 'array'");
50 DMLC_DECLARE_FIELD(start)
51 .set_default(dmlc::optional<int>())
52 .describe("If 'obj' is slice, 'start' is one of it's arguments.");
53 DMLC_DECLARE_FIELD(stop)
54 .set_default(dmlc::optional<int>())
55 .describe("If 'obj' is slice, 'stop' is one of it's arguments.");
56 DMLC_DECLARE_FIELD(step)
57 .set_default(dmlc::optional<int>())
58 .describe("If 'obj' is slice, 'step' is one of it's arguments.");
59 DMLC_DECLARE_FIELD(int_ind)
60 .set_default(dmlc::optional<int>())
61 .describe("If 'obj' is int, 'int_ind' is the index before which"
62 "'values' is inserted");
63 DMLC_DECLARE_FIELD(axis)
64 .set_default(dmlc::optional<int>())
65 .describe("Axis along which to insert 'values'.");
66 }
67 };
68
69 /*!
70 * \brief insert when obj is 'scaler' or a 'slice' with only one element.
71 * \tparam ndim - both 'in_arr', 'in_val' and 'out_data' have same ndim before call this.
72 * \param out_data - output: insert 'value' to 'arr' according to 'index'.
73 * \param in_arr - input: 'arr', original array.
74 * \param index - input(only for first Map): it's the only element in 'obj' indicats insert position.
75 * \param in_obj - input(only for second Map): It indicats insert position, it's ndim may equals to 0.
76 * \param in_val - input: 'value', insert to 'arr' according to 'index'.
77 * \param N - (only for first Map) arr.shape_[axis]
78 * \param numnew - extra dim size in 'out_data' compared with 'arr' in 'axis'.
79 * \param axis - insert 'value' to 'arr' in 'axis'.
80 * \param moveaxis - If 'obj' is a scaler, moveaxis is true;
81 If 'obj' is a slice with one element, moveaxis is false.
82 * \note Different between the two Map:
83 The first one use a scaler index;
84 The second one use a sequence of indecies which only has one index.
85 */
86 template<int ndim>
87 struct InsertSingleIndexKernel {
88 template<typename DType, typename VType>
MapInsertSingleIndexKernel89 MSHADOW_XINLINE static void Map(int i, DType* out_data,
90 const VType* in_val, const DType* in_arr,
91 const mshadow::Shape<ndim> outshape,
92 const mshadow::Shape<ndim> valshape,
93 const int index, const int numnew,
94 const mshadow::Shape<ndim> val_stride,
95 const mshadow::Shape<ndim> old_val_stride,
96 const mshadow::Shape<ndim> arr_stride,
97 const mshadow::Shape<ndim> out_stride,
98 const int axis, bool moveaxis, const int req) {
99 // i is the global flattened index in the output
100 // out_idx: i -> position in output's shape
101 mshadow::Shape<ndim> out_idx = mxnet_op::unravel(i, outshape);
102 int64_t dest_idx;
103 if (out_idx[axis] >= index && out_idx[axis] < index + numnew) { // from 'value'
104 int idx_val = out_idx[axis] - index;
105 // val_idx: i -> position in values's shape
106 mshadow::Shape<ndim> val_idx(out_idx);
107 val_idx[axis] = idx_val;
108 for (int j = ndim - 1; j >= 0; --j) {
109 if (valshape[j] == 1) { // broadcast
110 val_idx[j] = 0;
111 }
112 }
113 dest_idx = 0;
114 if (moveaxis) { // moveaxis(values, 0, axis)
115 for (int j = 0; j < axis; ++j) {
116 dest_idx += old_val_stride[j + 1] * val_idx[j];
117 }
118 dest_idx += old_val_stride[0] * val_idx[axis];
119 for (int j = axis + 1; j < ndim ; ++j) {
120 dest_idx += old_val_stride[j] * val_idx[j];
121 }
122 } else {
123 dest_idx = mxnet_op::dot(val_stride, val_idx);
124 }
125 KERNEL_ASSIGN(out_data[i], req, static_cast<DType>(in_val[dest_idx]));
126 } else { // from 'arr'
127 int idx_arr = (out_idx[axis] < index) ?
128 out_idx[axis] : out_idx[axis] - numnew;
129 // arr_idx: i -> position in arr's shape
130 mshadow::Shape<ndim> arr_idx(out_idx);
131 arr_idx[axis] = idx_arr;
132 dest_idx = mxnet_op::dot(arr_stride, arr_idx);
133 KERNEL_ASSIGN(out_data[i], req, in_arr[dest_idx]);
134 }
135 }
136
137 template<typename DType, typename VType>
MapInsertSingleIndexKernel138 MSHADOW_XINLINE static void Map(int i, DType* out_data,
139 const VType* in_val, const DType* in_arr,
140 const mshadow::Shape<ndim> outshape,
141 const mshadow::Shape<ndim> valshape,
142 const int N, const int64_t* in_obj, const int numnew,
143 const mshadow::Shape<ndim> val_stride,
144 const mshadow::Shape<ndim> old_val_stride,
145 const mshadow::Shape<ndim> arr_stride,
146 const mshadow::Shape<ndim> out_stride,
147 const int axis, bool moveaxis, const int req) {
148 // i is the global flattened index in the output
149 // out_idx: i -> position in output's shape
150 mshadow::Shape<ndim> out_idx = mxnet_op::unravel(i, outshape);
151 int64_t dest_idx;
152 int64_t index = in_obj[0];
153 if (static_cast<int64_t>(index) < 0) {
154 index += static_cast<int64_t>(N);
155 }
156 if (out_idx[axis] >= index && out_idx[axis] < index + numnew) { // from 'value'
157 int idx_val = out_idx[axis] - index;
158 // val_idx: i -> position in values's shape
159 mshadow::Shape<ndim> val_idx(out_idx);
160 val_idx[axis] = idx_val;
161 for (int j = ndim - 1; j >= 0; --j) {
162 if (valshape[j] == 1) { // broadcast
163 val_idx[j] = 0;
164 }
165 }
166 dest_idx = 0;
167 if (moveaxis) { // moveaxis(values, 0, axis)
168 for (int j = 0; j < axis; ++j) {
169 dest_idx += old_val_stride[j + 1] * val_idx[j];
170 }
171 dest_idx += old_val_stride[0] * val_idx[axis];
172 for (int j = axis + 1; j < ndim ; ++j) {
173 dest_idx += old_val_stride[j] *val_idx[j];
174 }
175 } else {
176 dest_idx = mxnet_op::dot(val_stride, val_idx);
177 }
178 KERNEL_ASSIGN(out_data[i], req, static_cast<DType>(in_val[dest_idx]));
179 } else { // from 'arr'
180 int idx_arr = (out_idx[axis] < index) ? out_idx[axis] : out_idx[axis] - numnew;
181 // arr_idx: i -> position in arr's shape
182 mshadow::Shape<ndim> arr_idx(out_idx);
183 arr_idx[axis] = idx_arr;
184 dest_idx = mxnet_op::dot(arr_stride, arr_idx);
185 KERNEL_ASSIGN(out_data[i], req, in_arr[dest_idx]);
186 }
187 }
188 };
189
190 /*!
191 * \brief insert when obj is 'tensor' or 'slice' with more than one element.
192 * \tparam ndim - both 'in_arr', 'in_val' and 'out_data' have same ndim before call this.
193 * \param out_data - output: insert 'value' to 'arr' according to 'index'.
194 * \param in_arr - input: 'arr', original array.
195 * \param in_obj - input: It indicats insert position, ndim may equals to 0.
196 * \param in_val - input: 'value', insert to 'arr' according to 'index'.
197 * \param is_insert - if is_insert[out_idx[axis]] is true, it's from 'values', else from 'arr'.
198 * \param origin_idx - indicate the original position in 'arr' or 'values' in 'axis'.
199 * \param axis - insert 'value' to 'arr' in 'axis'.
200 * \note Different between the two Map:
201 The first one insert a block of data, param 'in_val' is a tensor;
202 The second one insert only a single data, param 'in_val' is a scaler.
203 */
204 template<int ndim>
205 struct InsertSeqIndicesKernel {
206 template<typename DType, typename VType>
MapInsertSeqIndicesKernel207 MSHADOW_XINLINE static void Map(int i, DType* out_data,
208 const VType* in_val, const DType* in_arr,
209 const mshadow::Shape<ndim> outshape,
210 const mshadow::Shape<ndim> valshape,
211 const int* is_insert,
212 const int* origin_idx,
213 const mshadow::Shape<ndim> val_stride,
214 const mshadow::Shape<ndim> arr_stride,
215 const mshadow::Shape<ndim> out_stride,
216 const int axis, const int req) {
217 // i is the global flattened index in the output
218 // out_idx: i -> position in output's shape
219 mshadow::Shape<ndim> out_idx = mxnet_op::unravel(i, outshape);
220 int64_t dest_idx;
221 if (is_insert[out_idx[axis]]) {
222 // the data of output[i] is from 'values'
223 int idx_val = origin_idx[out_idx[axis]];
224 // insert_idx: i -> position in insert's shape
225 mshadow::Shape<ndim> insert_idx(out_idx);
226 insert_idx[axis] = idx_val;
227 // val_idx: i -> position in values's shape
228 mshadow::Shape<ndim> val_idx(insert_idx);
229 for (int j = ndim - 1; j >= 0; --j) { // broadcast
230 if (valshape[j] == 1) {
231 val_idx[j] = 0;
232 }
233 }
234 dest_idx = mxnet_op::dot(val_idx, val_stride);
235 KERNEL_ASSIGN(out_data[i], req, static_cast<DType>(in_val[dest_idx]));
236 } else {
237 // the data of output[i] is from 'arr'
238 int idx_arr = origin_idx[out_idx[axis]];
239 // arr_idx: i -> position in arr's shape
240 mshadow::Shape<ndim> arr_idx(out_idx);
241 arr_idx[axis] = idx_arr;
242 dest_idx = mxnet_op::dot(arr_idx, arr_stride);
243 KERNEL_ASSIGN(out_data[i], req, in_arr[dest_idx]);
244 }
245 }
246 };
247
248 struct ObjToIndices {
MapObjToIndices249 MSHADOW_XINLINE static void Map(int i, int64_t* indices,
250 int N, const int64_t* obj) {
251 indices[i] = obj[i];
252 if (indices[i] < 0) {
253 indices[i] += static_cast<int64_t>(N);
254 }
255 }
256 };
257
258 struct IndicesModify {
MapIndicesModify259 MSHADOW_XINLINE static void Map(int i, int64_t* indices, const int* order) {
260 indices[order[i]] += i;
261 }
262 };
263
264 struct SetIsInsert {
MapSetIsInsert265 MSHADOW_XINLINE static void Map(int i, int64_t* indices, int* is_insert) {
266 is_insert[static_cast<int>(indices[i])] = 1;
267 }
268 };
269
270 struct SetOriginValuesIdx {
MapSetOriginValuesIdx271 MSHADOW_XINLINE static void Map(int i, const int64_t* indices, int* origin_idx) {
272 origin_idx[static_cast<int>(indices[i])] = i;
273 }
274 };
275
276 struct SetOriginArrIdx {
MapSetOriginArrIdx277 MSHADOW_XINLINE static void Map(int i, const int* is_insert,
278 int* origin_idx) {
279 if (!is_insert[i]) {
280 int cnt = 0;
281 for (int j = 0; j < i; ++j) {
282 if (is_insert[j] == 0) {
283 cnt++;
284 }
285 }
286 origin_idx[i] = cnt;
287 }
288 }
289 };
290
291 template<typename xpu, int ndim>
InsertScalerImpl(mshadow::Stream<xpu> * s,const TBlob & output,const TBlob & arr,const TBlob & values,const mshadow::Shape<ndim> & arr_strides,const mshadow::Shape<ndim> & val_strides,const mshadow::Shape<ndim> & old_val_strides,const mshadow::Shape<ndim> & out_strides,const mshadow::Shape<ndim> & k_outshape,const mshadow::Shape<ndim> & k_valshape,const int dtype,const int vtype,const int req,const int axis,const int index,const int numnew,const size_t len,const bool moveaxis)292 void InsertScalerImpl(mshadow::Stream<xpu> *s, const TBlob& output,
293 const TBlob& arr, const TBlob& values,
294 const mshadow::Shape<ndim>& arr_strides,
295 const mshadow::Shape<ndim>& val_strides,
296 const mshadow::Shape<ndim>& old_val_strides,
297 const mshadow::Shape<ndim>& out_strides,
298 const mshadow::Shape<ndim>& k_outshape,
299 const mshadow::Shape<ndim>& k_valshape,
300 const int dtype, const int vtype, const int req,
301 const int axis, const int index, const int numnew,
302 const size_t len, const bool moveaxis) {
303 using namespace mshadow;
304 using namespace mxnet_op;
305 MSHADOW_TYPE_SWITCH(dtype, DType, {
306 MSHADOW_TYPE_SWITCH(vtype, VType, {
307 Kernel<InsertSingleIndexKernel<ndim>, xpu>::Launch(
308 s, len, output.dptr<DType>(),
309 values.dptr<VType>(), arr.dptr<DType>(),
310 k_outshape, k_valshape, index, numnew,
311 val_strides, old_val_strides, arr_strides, out_strides,
312 axis, moveaxis, req);
313 });
314 });
315 }
316
317 template<typename xpu, int ndim>
InsertSizeOneTensorImpl(mshadow::Stream<xpu> * s,const TBlob & output,const TBlob & arr,const TBlob & values,const mshadow::Shape<ndim> & arr_strides,const mshadow::Shape<ndim> & val_strides,const mshadow::Shape<ndim> & old_val_strides,const mshadow::Shape<ndim> & out_strides,const mshadow::Shape<ndim> & k_outshape,const mshadow::Shape<ndim> & k_valshape,const int dtype,const int vtype,const int req,const int axis,const TBlob & index,const int numnew,const int N,const size_t len,const bool moveaxis)318 void InsertSizeOneTensorImpl(mshadow::Stream<xpu> *s, const TBlob& output,
319 const TBlob& arr, const TBlob& values,
320 const mshadow::Shape<ndim>& arr_strides,
321 const mshadow::Shape<ndim>& val_strides,
322 const mshadow::Shape<ndim>& old_val_strides,
323 const mshadow::Shape<ndim>& out_strides,
324 const mshadow::Shape<ndim>& k_outshape,
325 const mshadow::Shape<ndim>& k_valshape,
326 const int dtype, const int vtype, const int req,
327 const int axis, const TBlob& index, const int numnew,
328 const int N, const size_t len, const bool moveaxis) {
329 using namespace mshadow;
330 using namespace mxnet_op;
331 MSHADOW_TYPE_SWITCH(dtype, DType, {
332 MSHADOW_TYPE_SWITCH(vtype, VType, {
333 Kernel<InsertSingleIndexKernel<ndim>, xpu>::Launch(
334 s, len, output.dptr<DType>(),
335 values.dptr<VType>(), arr.dptr<DType>(),
336 k_outshape, k_valshape, N, index.dptr<int64_t>(), numnew,
337 val_strides, old_val_strides, arr_strides, out_strides,
338 axis, moveaxis, req);
339 });
340 });
341 }
342
343 template<typename xpu, int ndim>
InsertSequenceImpl(mshadow::Stream<xpu> * s,const TBlob & output,const TBlob & arr,const TBlob & values,const mshadow::Shape<ndim> & arr_strides,const mshadow::Shape<ndim> & val_strides,const mshadow::Shape<ndim> & out_strides,const mshadow::Shape<ndim> & k_outshape,const mshadow::Shape<ndim> & k_valshape,const int * is_insert,const int * origin_idx,const int dtype,const int vtype,const int req,const int axis,const size_t len)344 void InsertSequenceImpl(mshadow::Stream<xpu> *s, const TBlob& output,
345 const TBlob& arr, const TBlob& values,
346 const mshadow::Shape<ndim>& arr_strides,
347 const mshadow::Shape<ndim>& val_strides,
348 const mshadow::Shape<ndim>& out_strides,
349 const mshadow::Shape<ndim>& k_outshape,
350 const mshadow::Shape<ndim>& k_valshape,
351 const int* is_insert, const int* origin_idx,
352 const int dtype, const int vtype, const int req,
353 const int axis, const size_t len) {
354 using namespace mshadow;
355 using namespace mxnet_op;
356 MSHADOW_TYPE_SWITCH(dtype, DType, {
357 MSHADOW_TYPE_SWITCH(vtype, VType, {
358 Kernel<InsertSeqIndicesKernel<ndim>, xpu>::Launch(
359 s, len, output.dptr<DType>(),
360 values.dptr<VType>(), arr.dptr<DType>(),
361 k_outshape, k_valshape, is_insert, origin_idx,
362 val_strides, arr_strides, out_strides, axis, req);
363 });
364 });
365 }
366
367 } // namespace op
368 } // namespace mxnet
369
370 #endif // MXNET_OPERATOR_NUMPY_NP_INSERT_OP_INL_H_
371