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