blob: fe9191d02e979407f4d76e5a48e61f57dca12969 [file]
/*
* 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.
*/
/*!
* Copyright (c) 2019 by Contributors
* \file np_pad_op-inl.h
* \brief Function definition of matrix related operators
*/
#ifndef MXNET_OPERATOR_NUMPY_NP_PAD_OP_INL_H_
#define MXNET_OPERATOR_NUMPY_NP_PAD_OP_INL_H_
#include <vector>
#include <algorithm>
#include <string>
#include <utility>
#include "../tensor/matrix_op-inl.h"
#include "../nn/concat-inl.h"
#include "../../common/utils.h"
#include "../mxnet_op.h"
#include "../operator_common.h"
#include "../elemwise_op_common.h"
#include "../tensor/broadcast_reduce_op.h"
namespace mxnet {
namespace op {
namespace pad_enum {
enum PadOpType { kConstant, kEdge, kReflect, kSymmetric, kMinimum, kMaximum };
}
template <int ndim>
MSHADOW_XINLINE index_t rravel(const mshadow::Shape<ndim>& coord,
const index_t* shape) {
index_t ret = 0;
int nndim = ndim;
#pragma unroll
for (int i = 0; i < nndim; ++i) {
ret = ret * shape[i] + (shape[i] > coord[i]) * coord[i];
}
return ret;
}
/* Compute coordinates from flattened index given shape */
template<int ndim>
MSHADOW_XINLINE mshadow::Shape<ndim> uunravel(const index_t idx,
const index_t* shape) {
mshadow::Shape<ndim> ret;
#pragma unroll
for (index_t i = ndim-1, j = idx; i >=0; --i) {
index_t tmp = j / shape[i];
ret[i] = j - tmp*shape[i];
j = tmp;
}
return ret;
}
inline std::string MXNetPadType2String(const int s) {
using namespace op;
if (s == pad_enum::kConstant) {
return "constant";
} else if (s == pad_enum::kEdge) {
return "edge";
} else if (s == pad_enum::kReflect) {
return "reflect";
} else if (s == pad_enum::kSymmetric) {
return "symmetric";
} else if (s == pad_enum::kMaximum) {
return "maximum";
} else if (s == pad_enum::kMinimum) {
return "minimum";
} else {
LOG(FATAL) << "unknown type " << s;
}
LOG(FATAL) << "should not reach here ";
return 0;
}
struct NumpyPadParam : public dmlc::Parameter<NumpyPadParam> {
mxnet::Tuple<mxnet::Tuple<int>> pad_width;
int mode;
double constant_values;
std::string reflect_type;
DMLC_DECLARE_PARAMETER(NumpyPadParam) {
DMLC_DECLARE_FIELD(pad_width)
.describe("Number of values padded to the edges of each axis. "
"((before_1, after_1), … (before_N,"
"after_N)) unique pad widths for each axis. ((before, after),) "
"yields same before and"
"after pad for each axis. "
"(pad,) or int is a shortcut for before = after = pad width for all"
"axes.");
DMLC_DECLARE_FIELD(mode)
.add_enum("constant", pad_enum::kConstant)
.add_enum("edge", pad_enum::kEdge)
.add_enum("reflect", pad_enum::kReflect)
.add_enum("symmetric", pad_enum::kSymmetric)
.add_enum("maximum", pad_enum::kMaximum)
.add_enum("minimum", pad_enum::kMinimum)
.set_default(pad_enum::kConstant)
.describe(
"Padding type to use."
" \"constant\" pads with `constant_values`"
" \"edge\" pads using the edge values of the input array"
" \"reflect\" Pads with the reflection of the vector mirrored"
"on the first and last values of the vector along each axis."
" \"symmetric\" Pads with the reflection of the vector mirrored"
"along the edge of the array."
" \"maximum\" Pads with the maximum value of all or part of the"
"vector along each axis."
" \"minimum\" Pads with the minimum value of all or part of the"
"vector along each axis.");
DMLC_DECLARE_FIELD(constant_values)
.set_default(0.0)
.describe("Used in ‘constant’. The values to set the padded values for each axis."
"((before_1, after_1), ... (before_N, after_N)) unique pad constants for"
"each axis."
"((before, after),) yields same before and after constants for each axis."
"(constant,) or constant is a shortcut for before = after = constant for all"
"axes."
"Default is 0.");
DMLC_DECLARE_FIELD(reflect_type)
.set_default("even")
.describe("Used in ‘reflect’, and ‘symmetric’. "
"The ‘even’ style is the default with an unaltered reflection around "
"the edge value. For the ‘odd’ style,"
"the extended part of the array is created by subtracting the "
"reflected values from two times the edge value.");
}
void SetAttrDict(std::unordered_map<std::string, std::string>* dict) {
std::ostringstream pad_width_s, mode_s, constant_values_s, reflect_type_s;
pad_width_s << pad_width;
constant_values_s << constant_values;
reflect_type_s << reflect_type;
(*dict)["pad_width"] = pad_width_s.str();
(*dict)["mode"] = MXNetPadType2String(mode);
(*dict)["constant_values"] = constant_values_s.str();
(*dict)["reflect_type"] = reflect_type_s.str();
}
};
inline mxnet::TShape NumpyPadShapeImpl(const mxnet::TShape& ishape,
const mxnet::Tuple<Tuple<int>> pad_width) {
if (ishape.ndim() == 1) {
index_t s = ishape[0] + pad_width[0][0] + pad_width[1][0];
return mxnet::TShape({s});
} else if (ishape.ndim() >= 2) {
int i;
mxnet::TShape oshape(ishape.ndim(), -1);
for (i = ishape.ndim() - 1; i >=0; i--) {
index_t base = ishape[i];
base = base + pad_width[i][0] + pad_width[i][1];
oshape[i] = base;
}
return oshape;
}
return mxnet::TShape({-1, -1});
}
template <typename xpu, int req, int ndim>
struct constant_pad {
template <typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width,
double constant_values) {
using namespace mxnet_op;
auto j = uunravel<ndim>(i, oshape);
size_t m;
bool origin = true;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
for (m = 0; m < ndim; m++) {
if (indexshape[m] >= indexwidth[m * 2] && indexshape[m] < indexwidth[m * 2] + ishape[m]) {
continue;
} else {
origin = false;
KERNEL_ASSIGN(out[i], req, constant_values);
}
}
if (origin) {
for (m = 0; m < ndim; m++) {
indexshape[m] = indexshape[m] - indexwidth[m * 2];
}
index_t l = rravel<ndim>(j, ishape);
KERNEL_ASSIGN(out[i], req, a[l]);
}
}
};
template <typename xpu, int req, int ndim>
struct pad_copy {
template<typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width){
using namespace mxnet_op;
auto j = uunravel<ndim>(i, oshape);
size_t m;
bool origin = true;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
// if is origin
for (m = 0; m < ndim; m++) {
if (indexshape[m] >= indexwidth[m * 2] && indexshape[m] < indexwidth[m * 2] + ishape[m]) {
continue;
} else {
origin = false;
break;
}
}
if (origin) {
for (m = 0; m < ndim; m++) {
indexshape[m] = indexshape[m] - indexwidth[m * 2];
}
index_t l = rravel<ndim>(j, ishape);
KERNEL_ASSIGN(out[i], req, a[l]);
} else {
return;
}
}
};
template <typename xpu, int req, int ndim>
struct symmetric_pad {
template<typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width,
size_t index){
using namespace mxnet_op;
auto j = uunravel<ndim>(i, oshape);
size_t m;
bool origin = true;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
for (m = 0; m < index; m++) {
if (indexshape[m] < indexwidth[m * 2] || indexshape[m] >= indexwidth[m * 2] + ishape[m]) {
// we can not do this now
return;
}
}
for (m = 0; m < ndim; m++) {
if (indexshape[m] >= indexwidth[m * 2] && indexshape[m] < indexwidth[m * 2] + ishape[m]) {
continue;
} else {
origin = false;
break;
}
}
if (origin) {
// this thread is in the origin position, then return
return;
}
if (indexshape[index] < indexwidth[index * 2]) {
// we need to do the assignment
index_t distance = indexwidth[index * 2] - indexshape[index];
index_t total = ishape[index];
// the round of this element
index_t round = (distance - 1) / total;
index_t position = distance % total;
if (position == 0) {
position = ishape[index];
}
if (round % 2 == 0) {
indexshape[index] = indexwidth[index * 2] + position - 1;
} else {
indexshape[index] = indexwidth[index * 2] + ishape[index] - 1 - (position - 1);
}
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
} else if (indexshape[index] >= (indexwidth[index * 2]+ishape[index])) {
index_t distance = (indexshape[index]+1) - (indexwidth[index * 2]+ishape[index]);
index_t total = ishape[index];
index_t position = distance % total;
index_t round = (distance - 1) / total;
if (position == 0) {
position = ishape[index];
}
if (round % 2 == 0) {
indexshape[index] = indexwidth[index * 2] + ishape[index] - 1 - (position - 1);
} else {
indexshape[index] = indexwidth[index * 2] + position - 1;
}
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
}
}
};
template <typename xpu, int req, int ndim>
struct edge_pad {
template<typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width,
size_t index){
using namespace mxnet_op;
auto j = uunravel<ndim>(i, oshape);
size_t m;
bool origin = true;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
for (m = 0; m < index; m++) {
if (indexshape[m] < indexwidth[m * 2] ||
indexshape[m] >= indexwidth[m * 2] + ishape[m]) {
// we can not do this now, since this is a former axis
return;
}
}
for (m = 0; m < ndim; m++) {
if (indexshape[m] >= indexwidth[m * 2] &&
indexshape[m] < indexwidth[m * 2] + ishape[m]) {
continue;
} else {
origin = false;
break;
}
}
if (origin) {
// this thread is in the origin position, then return
return;
}
if (indexshape[index] < indexwidth[index * 2]) {
// we need to do the assignment
indexshape[index] = indexwidth[index * 2];
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
} else if (indexshape[index] >= (indexwidth[index * 2]+ishape[index])) {
indexshape[index] = indexwidth[index * 2] + ishape[index] - 1;
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
}
}
};
template <typename xpu, int req, int ndim>
struct reflect_pad {
template<typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width,
size_t index){
using namespace mxnet_op;
auto j = uunravel<ndim>(i, oshape);
size_t m;
bool origin = true;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
for (m = 0; m < index; m++) {
if (indexshape[m] < indexwidth[m * 2] ||
indexshape[m] >= indexwidth[m * 2] + ishape[m]) {
// we can not do this now
return;
}
}
for (m = 0; m < ndim; m++) {
if (indexshape[m] >= indexwidth[m * 2] &&
indexshape[m] < indexwidth[m * 2] + ishape[m]) {
continue;
} else {
origin = false;
break;
}
}
if (origin) {
// this thread is in the origin position, then return
return;
}
if (indexshape[index] < indexwidth[index * 2]) {
// we need to do the assignment
index_t distance = indexwidth[index * 2] - indexshape[index];
index_t total = ishape[index];
if (total == 1) {
indexshape[index] = indexwidth[index * 2];
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
return;
}
index_t round = (distance - 1) / (total - 1);
if (round % 2 == 0) {
index_t position = (distance + round) % total;
indexshape[index] = indexwidth[index * 2] + position;
} else {
index_t position = (distance + round) % total;
indexshape[index] = indexwidth[index * 2] + ishape[index] - 1 - (position);
}
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
} else if (indexshape[index] >= (indexwidth[index * 2] + ishape[index])) {
index_t distance = (indexshape[index]+1) - (indexwidth[index * 2] + ishape[index]);
index_t total = ishape[index];
if (total == 1) {
indexshape[index] = indexwidth[index * 2];
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
return;
}
index_t round = (distance - 1) / (total - 1);
if (round % 2 == 0) {
index_t position = (distance + round) % total;
indexshape[index] = indexwidth[index * 2] + ishape[index] - 1 - (position);
} else {
index_t position = (distance + round) % total;
indexshape[index] = indexwidth[index * 2] + position;
}
index_t l = rravel<ndim>(j, oshape);
KERNEL_ASSIGN(out[i], req, out[l]);
}
}
};
template <typename xpu, int req, int ndim>
struct max_pad {
template<typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width,
size_t index){
using namespace mxnet_op;
auto j = uunravel<ndim>(i, oshape);
size_t m;
bool origin = true;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
for (m = 0; m < index; m++) {
if (indexshape[m] < indexwidth[m * 2] ||
indexshape[m] >= indexwidth[m * 2] + ishape[m]) {
// we can not do this now
return;
}
}
for (m = 0; m < ndim; m++) {
if (indexshape[m] >= indexwidth[m * 2] &&
indexshape[m] < indexwidth[m * 2] + ishape[m]) {
continue;
} else {
origin = false;
break;
}
}
if (origin) {
// this thread is in the origin position, then return
return;
}
if (indexshape[index] < indexwidth[index * 2] ||
indexshape[index] >= indexwidth[index * 2] + ishape[index]) {
indexshape[index] = indexwidth[index * 2];
index_t l = rravel<ndim>(j, oshape);
index_t max_count = 0;
auto max_value = out[l];
for (max_count = 0; max_count < ishape[index]; max_count++) {
indexshape[index] = indexwidth[index * 2] + max_count;
l = rravel<ndim>(j, oshape);
if (out[l] > max_value) {
max_value = out[l];
}
}
KERNEL_ASSIGN(out[i], req, max_value);
}
}
};
template <typename xpu, int req, int ndim>
struct min_pad {
template<typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width,
size_t index){
using namespace mxnet_op;
auto j = uunravel<ndim>(i, oshape);
size_t m;
bool origin = true;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
for (m = 0; m < index; m++) {
if (indexshape[m] < indexwidth[m * 2] ||
indexshape[m] >= indexwidth[m * 2] + ishape[m]) {
// we can not do this now
return;
}
}
for (m = 0; m < ndim; m++) {
if (indexshape[m] >= indexwidth[m * 2] &&
indexshape[m] < indexwidth[m * 2] + ishape[m]) {
continue;
} else {
origin = false;
break;
}
}
if (origin) {
// this thread is in the origin position, then return
return;
}
if (indexshape[index] < indexwidth[index * 2] ||
indexshape[index] >= (indexwidth[index * 2] + ishape[index])) {
indexshape[index] = indexwidth[index * 2];
index_t l = rravel<ndim>(j, oshape);
index_t min_count = 0;
auto min_value = out[l];
for (min_count = 0; min_count < ishape[index]; min_count++) {
indexshape[index] = indexwidth[index * 2] + min_count;
l = rravel<ndim>(j, oshape);
if (out[l] < min_value) {
min_value = out[l];
}
}
j = uunravel<ndim>(i, oshape);
KERNEL_ASSIGN(out[i], req, min_value);
} else {
return;
}
}
};
template <typename xpu, int req, int ndim>
struct pad_grad {
template<typename DType>
MSHADOW_XINLINE static void Map(index_t i, DType *out, const DType *a,
const index_t* ishape,
const index_t* oshape,
mshadow::Shape<ndim*2> width) {
auto j = uunravel<ndim>(i, oshape);
size_t m;
index_t* indexwidth = width.shape_;
index_t* indexshape = j.shape_;
for (m = 0; m < ndim; m++) {
indexshape[m] = indexshape[m] + indexwidth[m * 2];
}
index_t l = rravel<ndim>(j, ishape);
KERNEL_ASSIGN(out[i], req, a[l]);
}
};
template<typename xpu>
void NumpyPadOpImpl(const TBlob& in_data,
const TBlob& out_data,
index_t* ishape,
index_t* oshape,
index_t dsize,
const NumpyPadParam& param,
const std::vector<OpReqType>& req,
mxnet_op::Stream<xpu> *s) {
using namespace mxnet_op;
using namespace mshadow;
int mode = param.mode;
int ndim = in_data.ndim();
MXNET_NDIM_SWITCH(ndim, NDim, {
mshadow::Shape<NDim*2> width;
int dimcounter = 0;
index_t* odptr = reinterpret_cast<index_t*>(oshape);
if (ndim == 1) {
width[0] = param.pad_width[0][0];
width[1] = param.pad_width[1][0];
} else {
for (dimcounter = 0; dimcounter < NDim; dimcounter++) {
width[dimcounter*2] = param.pad_width[dimcounter][0];
width[dimcounter*2 + 1] = param.pad_width[dimcounter][1];
}
}
index_t* idptr = reinterpret_cast<index_t*>(ishape);
switch (mode) {
case pad_enum::kConstant:
{
// constant padding start
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<constant_pad<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width, param.constant_values);
});
});
// constant padding end
break;
}
case pad_enum::kSymmetric:
{
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<pad_copy<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width);
});
});
index_t index;
index_t dim = ndim;
// symmetric padding start
for (index = dim-1; index >= 0; index--) {
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<symmetric_pad<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width, index);
});
});
}
// symmetric padding end
break;
}
case pad_enum::kReflect:
{
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<pad_copy<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width);
});
});
index_t index;
index_t dim = ndim;
// reflect padding start
for (index = dim-1; index >= 0; index--) {
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<reflect_pad<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width, index);
});
});
}
// reflect padding end
break;
}
case pad_enum::kEdge:
{
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<pad_copy<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width);
});
});
index_t index;
index_t dim = ndim;
// edge padding start
for (index = dim-1; index >= 0; index--) {
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<edge_pad<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width, index);
});
});
}
// edge padding end
break;
}
case pad_enum::kMinimum:
{
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<pad_copy<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width);
});
});
index_t index;
index_t dim = ndim;
// minimum padding start
for (index = dim-1; index >= 0; index--) {
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<min_pad<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width, index);
});
});
}
// minimum padding end
break;
}
case pad_enum::kMaximum:
{
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<pad_copy<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width);
});
});
index_t index;
index_t dim = ndim;
// maximum padding start
for (index = dim-1; index >= 0; index--) {
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<max_pad<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width, index);
});
});
}
// maximum padding end
break;
}
default:
LOG(FATAL) << "Other modes are not supported. ";
}
})
}
template<typename xpu>
void NumpyPadOpBackImpl(const TBlob& in_data,
const TBlob& out_data,
index_t* ishape,
index_t* oshape,
index_t dsize,
const NumpyPadParam& param,
const std::vector<OpReqType>& req,
mxnet_op::Stream<xpu> *s) {
using namespace mxnet_op;
using namespace mshadow;
int mode = param.mode;
int ndim = in_data.ndim();
if (mode != 0) {
LOG(FATAL) << "Other modes are not supported. ";
}
MXNET_NDIM_SWITCH(ndim, NDim, {
mshadow::Shape<NDim*2> width;
int dimcounter = 0;
index_t* odptr = reinterpret_cast<index_t*>(oshape);
if (ndim == 1) {
width[0] = param.pad_width[0][0];
width[1] = param.pad_width[1][0];
} else {
for (dimcounter = 0; dimcounter < NDim; dimcounter++) {
width[dimcounter*2] = param.pad_width[dimcounter][0];
width[dimcounter*2 + 1] = param.pad_width[dimcounter][1];
}
}
index_t* idptr = reinterpret_cast<index_t*>(ishape);
MSHADOW_TYPE_SWITCH_WITH_BOOL(out_data.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<pad_grad<xpu, req_type, NDim>, xpu>::Launch(
s, dsize, out_data.dptr<DType>(), in_data.dptr<DType>(),
idptr, odptr, width);
});
});
})
}
template<typename xpu>
void NumpyPadOpForward(const nnvm::NodeAttrs& attrs,
const OpContext& ctx,
const std::vector<TBlob>& inputs,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& outputs) {
MXNET_NDIM_SWITCH(inputs[0].ndim(), NDim, {
using namespace mxnet_op;
using namespace mshadow;
CHECK_EQ(inputs.size(), 1U);
CHECK_EQ(outputs.size(), 1U);
CHECK_EQ(req.size(), 1U);
CHECK(req[0] != kNullOp);
CHECK(req[0] != kWriteInplace);
Stream<xpu> *s = ctx.get_stream<xpu>();
const TBlob& in_data = inputs[0];
const TBlob& out_data = outputs[0];
size_t ts = in_data.ndim();
size_t count;
mshadow::Shape<NDim> inshape;
for (count = 0; count < ts; count++) {
inshape[count] = static_cast<index_t>((in_data.shape_)[count]);
}
Tensor<xpu, 1, index_t> tsp = ctx.requested[0].
get_space_typed<xpu, 1, index_t>(Shape1(2*ts), s);
Tensor<cpu, 1, index_t> ta(reinterpret_cast<index_t*>(inshape.shape_),
Shape1(ts), ctx.get_stream<cpu>());
Tensor<xpu, 1, index_t> ti(reinterpret_cast<index_t*>(tsp.dptr_),
Shape1(ts), ctx.get_stream<xpu>());
mshadow::Copy(ti, ta, ctx.get_stream<xpu>());
mshadow::Shape<NDim> outshape;
for (count = 0; count < ts; count++) {
outshape[count] = static_cast<index_t>((out_data.shape_)[count]);
}
index_t* wcp = tsp.dptr_;
wcp += ts;
Tensor<cpu, 1, index_t> tb(reinterpret_cast<index_t*>(outshape.shape_),
Shape1(ts), ctx.get_stream<cpu>());
Tensor<xpu, 1, index_t> to(reinterpret_cast<index_t*>(wcp), Shape1(ts),
ctx.get_stream<xpu>());
mshadow::Copy(to, tb, ctx.get_stream<xpu>());
const NumpyPadParam& param = nnvm::get<NumpyPadParam>(attrs.parsed);
index_t* wt = reinterpret_cast<index_t*>(to.dptr_);
index_t* wi = reinterpret_cast<index_t*>(ti.dptr_);
NumpyPadOpImpl<xpu>(in_data, out_data, wi,
wt, out_data.Size(), param, req, s);
})
}
template<typename xpu>
void NumpyPadOpBackward(const nnvm::NodeAttrs& attrs,
const OpContext& ctx,
const std::vector<TBlob>& inputs,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& outputs) {
MXNET_NDIM_SWITCH(inputs[0].ndim(), NDim, {
using namespace mxnet_op;
using namespace mshadow;
CHECK_EQ(inputs.size(), 1U);
CHECK_EQ(outputs.size(), 1U);
CHECK_EQ(req.size(), 1U);
CHECK(req[0] != kNullOp);
CHECK(req[0] != kWriteInplace);
Stream<xpu> *s = ctx.get_stream<xpu>();
const TBlob& in_data = inputs[0];
const TBlob& out_data = outputs[0];
size_t ts = in_data.ndim();
size_t count;
mshadow::Shape<NDim> inshape;
for (count = 0; count < ts; count++) {
inshape[count] = static_cast<index_t>((in_data.shape_)[count]);
}
Tensor<xpu, 1, index_t> tsp = ctx.requested[0].
get_space_typed<xpu, 1, index_t>(Shape1(2*ts), s);
Tensor<cpu, 1, index_t> ta(reinterpret_cast<index_t*>(inshape.shape_),
Shape1(ts), ctx.get_stream<cpu>());
Tensor<xpu, 1, index_t> ti(reinterpret_cast<index_t*>(tsp.dptr_),
Shape1(ts), ctx.get_stream<xpu>());
mshadow::Copy(ti, ta, ctx.get_stream<xpu>());
mshadow::Shape<NDim> outshape;
for (count = 0; count < ts; count++) {
outshape[count] = static_cast<index_t>((out_data.shape_)[count]);
}
index_t* wcp = tsp.dptr_;
wcp += ts;
Tensor<cpu, 1, index_t> tb(reinterpret_cast<index_t*>(outshape.shape_),
Shape1(ts), ctx.get_stream<cpu>());
Tensor<xpu, 1, index_t> to(reinterpret_cast<index_t*>(wcp), Shape1(ts),
ctx.get_stream<xpu>());
mshadow::Copy(to, tb, ctx.get_stream<xpu>());
const NumpyPadParam& param = nnvm::get<NumpyPadParam>(attrs.parsed);
index_t* wt = reinterpret_cast<index_t*>(to.dptr_);
index_t* wi = reinterpret_cast<index_t*>(ti.dptr_);
NumpyPadOpBackImpl<xpu>(in_data, out_data, wi,
wt, out_data.Size(), param, req, s);
})
}
} // namespace op
} // namespace mxnet
#endif // MXNET_OPERATOR_NUMPY_NP_PAD_OP_INL_H_