Skip to content

Commit 051a464

Browse files
tingying2020larroy
authored andcommitted
[Numpy]flip (apache#15819)
* Numpy flip operator * Implement flip * fix some bug and add gpu test * register param and edit test * add testcase for backward * remove print * optimize 0-dim and 0-shape * adjust format and add doc in _symbol.py * fix bug in symbol * add flip in __all__ * fix format error * import ndarray * move flip implementation to np_matrix_op and remove test in gpu * delate redundant blank line * fix error in review * remove **kwargs and change copy * fix error in review * Fix import * Fix lint
1 parent 027c13e commit 051a464

File tree

7 files changed

+359
-3
lines changed

7 files changed

+359
-3
lines changed

python/mxnet/ndarray/numpy/_op.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'tensordot',
3535
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'vstack', 'mean',
3636
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
37-
'ravel', 'hanning', 'hamming', 'blackman']
37+
'ravel', 'hanning', 'hamming', 'blackman', 'flip']
3838

3939

4040
@set_module('mxnet.ndarray.numpy')
@@ -2828,3 +2828,71 @@ def blackman(M, dtype=_np.float32, ctx=None):
28282828
if ctx is None:
28292829
ctx = current_context()
28302830
return _npi.blackman(M, dtype=dtype, ctx=ctx)
2831+
2832+
2833+
@set_module('mxnet.ndarray.numpy')
2834+
def flip(m, axis=None, out=None):
2835+
r"""
2836+
flip(m, axis=None, out=None)
2837+
2838+
Reverse the order of elements in an array along the given axis.
2839+
2840+
The shape of the array is preserved, but the elements are reordered.
2841+
2842+
Parameters
2843+
----------
2844+
m : ndarray or scalar
2845+
Input array.
2846+
axis : None or int or tuple of ints, optional
2847+
Axis or axes along which to flip over. The default,
2848+
axis=None, will flip over all of the axes of the input array.
2849+
If axis is negative it counts from the last to the first axis.
2850+
2851+
If axis is a tuple of ints, flipping is performed on all of the axes
2852+
specified in the tuple.
2853+
out : ndarray or scalar, optional
2854+
Alternative output array in which to place the result. It must have
2855+
the same shape and type as the expected output.
2856+
2857+
Returns
2858+
-------
2859+
out : ndarray or scalar
2860+
A view of `m` with the entries of axis reversed. Since a view is
2861+
returned, this operation is done in constant time.
2862+
2863+
Examples
2864+
--------
2865+
>>> A = np.arange(8).reshape((2,2,2))
2866+
>>> A
2867+
array([[[0, 1],
2868+
[2, 3]],
2869+
[[4, 5],
2870+
[6, 7]]])
2871+
>>> np.flip(A, 0)
2872+
array([[[4, 5],
2873+
[6, 7]],
2874+
[[0, 1],
2875+
[2, 3]]])
2876+
>>> np.flip(A, 1)
2877+
array([[[2, 3],
2878+
[0, 1]],
2879+
[[6, 7],
2880+
[4, 5]]])
2881+
>>> np.flip(A)
2882+
array([[[7, 6],
2883+
[5, 4]],
2884+
[[3, 2],
2885+
[1, 0]]])
2886+
>>> np.flip(A, (0, 2))
2887+
array([[[5, 4],
2888+
[7, 6]],
2889+
[[1, 0],
2890+
[3, 2]]])
2891+
"""
2892+
from ...numpy import ndarray
2893+
if isinstance(m, numeric_types):
2894+
return _np.flip(m, axis)
2895+
elif isinstance(m, ndarray):
2896+
return _npi.flip(m, axis, out=out)
2897+
else:
2898+
raise TypeError('type {} not supported'.format(str(type(m))))

python/mxnet/numpy/multiarray.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
'fix', 'ceil', 'floor', 'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh',
5454
'tensordot', 'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate',
5555
'stack', 'vstack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices',
56-
'copysign', 'ravel', 'hanning', 'hamming', 'blackman']
56+
'copysign', 'ravel', 'hanning', 'hamming', 'blackman', 'flip']
5757

5858
# Return code for dispatching indexing function call
5959
_NDARRAY_UNSUPPORTED_INDEXING = -1
@@ -4367,3 +4367,65 @@ def blackman(M, dtype=_np.float32, ctx=None):
43674367
>>> plt.show()
43684368
"""
43694369
return _mx_nd_np.blackman(M, dtype=dtype, ctx=ctx)
4370+
4371+
4372+
@set_module('mxnet.numpy')
4373+
def flip(m, axis=None, out=None):
4374+
r"""
4375+
flip(m, axis=None, out=None)
4376+
4377+
Reverse the order of elements in an array along the given axis.
4378+
4379+
The shape of the array is preserved, but the elements are reordered.
4380+
4381+
Parameters
4382+
----------
4383+
m : ndarray or scalar
4384+
Input array.
4385+
axis : None or int or tuple of ints, optional
4386+
Axis or axes along which to flip over. The default,
4387+
axis=None, will flip over all of the axes of the input array.
4388+
If axis is negative it counts from the last to the first axis.
4389+
4390+
If axis is a tuple of ints, flipping is performed on all of the axes
4391+
specified in the tuple.
4392+
out : ndarray or scalar, optional
4393+
Alternative output array in which to place the result. It must have
4394+
the same shape and type as the expected output.
4395+
4396+
Returns
4397+
-------
4398+
out : ndarray or scalar
4399+
A view of `m` with the entries of axis reversed. Since a view is
4400+
returned, this operation is done in constant time.
4401+
4402+
Examples
4403+
--------
4404+
>>> A = np.arange(8).reshape((2,2,2))
4405+
>>> A
4406+
array([[[0, 1],
4407+
[2, 3]],
4408+
[[4, 5],
4409+
[6, 7]]])
4410+
>>> np.flip(A, 0)
4411+
array([[[4, 5],
4412+
[6, 7]],
4413+
[[0, 1],
4414+
[2, 3]]])
4415+
>>> np.flip(A, 1)
4416+
array([[[2, 3],
4417+
[0, 1]],
4418+
[[6, 7],
4419+
[4, 5]]])
4420+
>>> np.flip(A)
4421+
array([[[7, 6],
4422+
[5, 4]],
4423+
[[3, 2],
4424+
[1, 0]]])
4425+
>>> np.flip(A, (0, 2))
4426+
array([[[5, 4],
4427+
[7, 6]],
4428+
[[1, 0],
4429+
[3, 2]]])
4430+
"""
4431+
return _mx_nd_np.flip(m, axis, out=out)

python/mxnet/symbol/numpy/_symbol.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'tensordot',
3737
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'vstack', 'mean',
3838
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
39-
'ravel', 'hanning', 'hamming', 'blackman']
39+
'ravel', 'hanning', 'hamming', 'blackman', 'flip']
4040

4141

4242
def _num_outputs(sym):
@@ -3091,4 +3091,42 @@ def blackman(M, dtype=_np.float32, ctx=None):
30913091
return _npi.blackman(M, dtype=dtype, ctx=ctx)
30923092

30933093

3094+
@set_module('mxnet.symbol.numpy')
3095+
def flip(m, axis=None, out=None):
3096+
r"""
3097+
flip(m, axis=None, out=None)
3098+
3099+
Reverse the order of elements in an array along the given axis.
3100+
3101+
The shape of the array is preserved, but the elements are reordered.
3102+
3103+
Parameters
3104+
----------
3105+
m : _Symbol or scalar
3106+
Input array.
3107+
axis : None or int or tuple of ints, optional
3108+
Axis or axes along which to flip over. The default,
3109+
axis=None, will flip over all of the axes of the input array.
3110+
If axis is negative it counts from the last to the first axis.
3111+
3112+
If axis is a tuple of ints, flipping is performed on all of the axes
3113+
specified in the tuple.
3114+
out : _Symbol or scalar, optional
3115+
Alternative output array in which to place the result. It must have
3116+
the same shape and type as the expected output.
3117+
3118+
Returns
3119+
-------
3120+
out : _Symbol or scalar
3121+
A view of `m` with the entries of axis reversed. Since a view is
3122+
returned, this operation is done in constant time.
3123+
"""
3124+
if isinstance(m, numeric_types):
3125+
return _np.flip(m, axis)
3126+
elif isinstance(m, _Symbol):
3127+
return _npi.flip(m, axis, out=out)
3128+
else:
3129+
raise TypeError('type {} not supported'.format(str(type(m))))
3130+
3131+
30943132
_set_np_symbol_class(_Symbol)

src/operator/numpy/np_matrix_op-inl.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,72 @@ void NumpyRollCompute(const nnvm::NodeAttrs& attrs,
297297
}
298298
}
299299

300+
struct FlipParam : public dmlc::Parameter<FlipParam> {
301+
mxnet::Tuple<int> axis;
302+
DMLC_DECLARE_PARAMETER(FlipParam) {
303+
DMLC_DECLARE_FIELD(axis)
304+
.describe("The axis which to flip elements.");
305+
}
306+
};
307+
308+
#define FLIP_MAX_DIM 10
309+
#define FLIP_MIN_DIM -1
310+
311+
template<typename xpu>
312+
void NumpyFlipForwardImpl(const OpContext& ctx,
313+
const std::vector<TBlob>& inputs,
314+
const std::vector<TBlob>& outputs,
315+
const std::vector<index_t>& stride_,
316+
const std::vector<index_t>& trailing_,
317+
const index_t& flip_index);
318+
319+
template<typename xpu>
320+
void NumpyFlipForward(const nnvm::NodeAttrs& attrs,
321+
const OpContext& ctx,
322+
const std::vector<TBlob>& inputs,
323+
const std::vector<OpReqType>& req,
324+
const std::vector<TBlob>& outputs) {
325+
const FlipParam& param = nnvm::get<FlipParam>(attrs.parsed);
326+
mxnet::Tuple<int> axistemp;
327+
CHECK_EQ(inputs[0].type_flag_, outputs[0].type_flag_);
328+
CHECK_LT(param.axis.ndim(), FLIP_MAX_DIM);
329+
CHECK_GE(param.axis.ndim(), FLIP_MIN_DIM);
330+
if (param.axis.ndim() == FLIP_MIN_DIM) {
331+
if (inputs[0].shape_.ndim() == 0) {
332+
mshadow::Stream<xpu> *s = ctx.get_stream<xpu>();
333+
MSHADOW_TYPE_SWITCH(inputs[0].type_flag_, DType, {
334+
mshadow::Copy(outputs[0].FlatTo1D<xpu, DType>(s), inputs[0].FlatTo1D<xpu, DType>(s), s);
335+
});
336+
return;
337+
}
338+
std::vector<int> temp;
339+
for (int i = 0; i < inputs[0].shape_.ndim(); i++) {
340+
temp.push_back(i);
341+
}
342+
axistemp.assign(temp.begin(), temp.end());
343+
} else {
344+
axistemp = param.axis;
345+
}
346+
347+
const mxnet::TShape& ishape = inputs[0].shape_;
348+
if (ishape.ProdShape(0, ishape.ndim()) == 0) {
349+
return; // zero shape
350+
}
351+
std::vector<index_t> stride_(axistemp.ndim());
352+
std::vector<index_t> trailing_(axistemp.ndim());
353+
index_t flip_index = 0;
354+
for (int axis : axistemp) {
355+
CHECK_LT(axis, ishape.ndim());
356+
stride_[flip_index] = ishape[axis];
357+
trailing_[flip_index] = 1;
358+
for (int i2 = axis + 1; i2 < ishape.ndim(); ++i2) {
359+
trailing_[flip_index] *= ishape[i2];
360+
}
361+
flip_index++;
362+
}
363+
NumpyFlipForwardImpl<xpu>(ctx, inputs, outputs, stride_, trailing_, flip_index);
364+
}
365+
300366
} // namespace op
301367
} // namespace mxnet
302368

src/operator/numpy/np_matrix_op.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,5 +565,52 @@ NNVM_REGISTER_OP(_np_roll)
565565
.add_argument("data", "NDArray-or-Symbol", "Input ndarray")
566566
.add_arguments(NumpyRollParam::__FIELDS__());
567567

568+
template<>
569+
void NumpyFlipForwardImpl<cpu>(const OpContext& ctx,
570+
const std::vector<TBlob>& inputs,
571+
const std::vector<TBlob>& outputs,
572+
const std::vector<index_t>& stride_,
573+
const std::vector<index_t>& trailing_,
574+
const index_t& flip_index) {
575+
mshadow::Stream<cpu> *s = ctx.get_stream<cpu>();
576+
MSHADOW_TYPE_SWITCH(outputs[0].type_flag_, DType, {
577+
mxnet_op::Kernel<reverse, cpu>::Launch(s, inputs[0].Size(), flip_index,
578+
inputs[0].dptr<DType>(), outputs[0].dptr<DType>(),
579+
stride_.data(), trailing_.data());
580+
});
581+
}
582+
583+
DMLC_REGISTER_PARAMETER(FlipParam);
584+
585+
NNVM_REGISTER_OP(_npi_flip)
586+
.set_num_outputs(1)
587+
.set_num_inputs(1)
588+
.set_attr_parser(ParamParser<FlipParam>)
589+
.set_attr<nnvm::FListInputNames>("FListInputNames",
590+
[](const NodeAttrs& attrs) {
591+
return std::vector<std::string> {"data"};
592+
})
593+
.set_attr<FResourceRequest>("FResourceRequest",
594+
[](const NodeAttrs& attrs) {
595+
return std::vector<ResourceRequest> {ResourceRequest::kTempSpace};
596+
})
597+
.set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>)
598+
.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<1, 1>)
599+
.set_attr<FCompute>("FCompute<cpu>", NumpyFlipForward<cpu>)
600+
.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseNone{"_backward_npi_flip"})
601+
.add_argument("data", "NDArray-or-Symbol", "Input data array")
602+
.add_arguments(FlipParam::__FIELDS__());
603+
604+
NNVM_REGISTER_OP(_backward_npi_flip)
605+
.set_num_inputs(1)
606+
.set_num_outputs(1)
607+
.set_attr_parser(ParamParser<FlipParam>)
608+
.set_attr<nnvm::TIsBackward>("TIsBackward", true)
609+
.set_attr<FResourceRequest>("FResourceRequest",
610+
[](const NodeAttrs& attrs) {
611+
return std::vector<ResourceRequest> {ResourceRequest::kTempSpace};
612+
})
613+
.set_attr<FCompute>("FCompute<cpu>", NumpyFlipForward<cpu>);
614+
568615
} // namespace op
569616
} // namespace mxnet

src/operator/numpy/np_matrix_op.cu

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,39 @@ NNVM_REGISTER_OP(_backward_np_vstack)
5656
NNVM_REGISTER_OP(_np_roll)
5757
.set_attr<FCompute>("FCompute<gpu>", NumpyRollCompute<gpu>);
5858

59+
template<>
60+
void NumpyFlipForwardImpl<gpu>(const OpContext& ctx,
61+
const std::vector<TBlob>& inputs,
62+
const std::vector<TBlob>& outputs,
63+
const std::vector<index_t>& stride_,
64+
const std::vector<index_t>& trailing_,
65+
const index_t& flip_index) {
66+
mshadow::Stream<gpu> *s = ctx.get_stream<gpu>();
67+
mshadow::Tensor<gpu, 1, uint8_t> workspace =
68+
ctx.requested[0].get_space_typed<gpu, 1, uint8_t>(
69+
mshadow::Shape1(flip_index * sizeof(index_t) * 2), s);
70+
71+
auto stride_workspace = workspace.dptr_;
72+
auto trailing_workspace = workspace.dptr_ + flip_index * sizeof(index_t);
73+
74+
cudaMemcpyAsync(stride_workspace, thrust::raw_pointer_cast(stride_.data()),
75+
stride_.size() * sizeof(index_t),
76+
cudaMemcpyHostToDevice, mshadow::Stream<gpu>::GetStream(s));
77+
cudaMemcpyAsync(trailing_workspace, thrust::raw_pointer_cast(trailing_.data()),
78+
trailing_.size() * sizeof(index_t),
79+
cudaMemcpyHostToDevice, mshadow::Stream<gpu>::GetStream(s));
80+
81+
MSHADOW_TYPE_SWITCH(outputs[0].type_flag_, DType, {
82+
mxnet_op::Kernel<reverse, gpu>::Launch(s, inputs[0].Size(), flip_index,
83+
inputs[0].dptr<DType>(), outputs[0].dptr<DType>(),
84+
reinterpret_cast<index_t*>(stride_workspace), reinterpret_cast<index_t*>(trailing_workspace));
85+
});
86+
}
87+
88+
NNVM_REGISTER_OP(_npi_flip)
89+
.set_attr<FCompute>("FCompute<gpu>", NumpyFlipForward<gpu>);
90+
91+
NNVM_REGISTER_OP(_backward_npi_flip)
92+
.set_attr<FCompute>("FCompute<gpu>", NumpyFlipForward<gpu>);
5993
} // namespace op
6094
} // namespace mxnet

0 commit comments

Comments
 (0)