Skip to content
This repository was archived by the owner on Nov 17, 2023. It is now read-only.

Commit b2755af

Browse files
author
Ying
committed
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
1 parent 90091b1 commit b2755af

File tree

7 files changed

+360
-3
lines changed

7 files changed

+360
-3
lines changed

python/mxnet/ndarray/numpy/_op.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor',
3434
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'tensordot',
3535
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'mean',
36-
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign']
36+
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
37+
'flip']
3738

3839

3940
@set_module('mxnet.ndarray.numpy')
@@ -2483,3 +2484,71 @@ def copysign(x1, x2, out=None):
24832484
array([-1., 0., 1.])
24842485
"""
24852486
return _ufunc_helper(x1, x2, _npi.copysign, _np.copysign, _npi.copysign_scalar, _npi.rcopysign_scalar, out)
2487+
2488+
2489+
@set_module('mxnet.ndarray.numpy')
2490+
def flip(x, axis=None, out=None):
2491+
r"""
2492+
flip(x, axis=None, out=None)
2493+
2494+
Reverse the order of elements in an array along the given axis.
2495+
2496+
The shape of the array is preserved, but the elements are reordered.
2497+
2498+
Parameters
2499+
----------
2500+
m : ndarray or scalar
2501+
Input array.
2502+
axis : None or int or tuple of ints, optional
2503+
Axis or axes along which to flip over. The default,
2504+
axis=None, will flip over all of the axes of the input array.
2505+
If axis is negative it counts from the last to the first axis.
2506+
2507+
If axis is a tuple of ints, flipping is performed on all of the axes
2508+
specified in the tuple.
2509+
out : ndarray or scalar, optional
2510+
Alternative output array in which to place the result. It must have
2511+
the same shape and type as the expected output.
2512+
2513+
Returns
2514+
-------
2515+
out : ndarray or scalar
2516+
A view of `m` with the entries of axis reversed. Since a view is
2517+
returned, this operation is done in constant time.
2518+
2519+
Examples
2520+
--------
2521+
>>> A = np.arange(8).reshape((2,2,2))
2522+
>>> A
2523+
array([[[0, 1],
2524+
[2, 3]],
2525+
[[4, 5],
2526+
[6, 7]]])
2527+
>>> np.flip(A, 0)
2528+
array([[[4, 5],
2529+
[6, 7]],
2530+
[[0, 1],
2531+
[2, 3]]])
2532+
>>> np.flip(A, 1)
2533+
array([[[2, 3],
2534+
[0, 1]],
2535+
[[6, 7],
2536+
[4, 5]]])
2537+
>>> np.flip(A)
2538+
array([[[7, 6],
2539+
[5, 4]],
2540+
[[3, 2],
2541+
[1, 0]]])
2542+
>>> np.flip(A, (0, 2))
2543+
array([[[5, 4],
2544+
[7, 6]],
2545+
[[1, 0],
2546+
[3, 2]]])
2547+
"""
2548+
from ...numpy import ndarray
2549+
if isinstance(x, numeric_types):
2550+
return _np.flip(x, axis)
2551+
elif isinstance(x, ndarray):
2552+
return _npi.flip(x, axis, out=out)
2553+
else:
2554+
raise TypeError('type {} not supported'.format(str(type(x))))

python/mxnet/numpy/multiarray.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative',
5353
'fix', 'ceil', 'floor', 'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh',
5454
'tensordot', 'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate',
55-
'stack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign']
55+
'stack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
56+
'flip']
5657

5758
# Return code for dispatching indexing function call
5859
_NDARRAY_UNSUPPORTED_INDEXING = -1
@@ -3986,3 +3987,65 @@ def copysign(x1, x2, out=None):
39863987
array([-1., 0., 1.])
39873988
"""
39883989
return _mx_nd_np.copysign(x1, x2, out=out)
3990+
3991+
3992+
@set_module('mxnet.numpy')
3993+
def flip(x, axis=None, out=None):
3994+
r"""
3995+
flip(x, axis=None, out=None)
3996+
3997+
Reverse the order of elements in an array along the given axis.
3998+
3999+
The shape of the array is preserved, but the elements are reordered.
4000+
4001+
Parameters
4002+
----------
4003+
m : ndarray or scalar
4004+
Input array.
4005+
axis : None or int or tuple of ints, optional
4006+
Axis or axes along which to flip over. The default,
4007+
axis=None, will flip over all of the axes of the input array.
4008+
If axis is negative it counts from the last to the first axis.
4009+
4010+
If axis is a tuple of ints, flipping is performed on all of the axes
4011+
specified in the tuple.
4012+
out : ndarray or scalar, optional
4013+
Alternative output array in which to place the result. It must have
4014+
the same shape and type as the expected output.
4015+
4016+
Returns
4017+
-------
4018+
out : ndarray or scalar
4019+
A view of `m` with the entries of axis reversed. Since a view is
4020+
returned, this operation is done in constant time.
4021+
4022+
Examples
4023+
--------
4024+
>>> A = np.arange(8).reshape((2,2,2))
4025+
>>> A
4026+
array([[[0, 1],
4027+
[2, 3]],
4028+
[[4, 5],
4029+
[6, 7]]])
4030+
>>> np.flip(A, 0)
4031+
array([[[4, 5],
4032+
[6, 7]],
4033+
[[0, 1],
4034+
[2, 3]]])
4035+
>>> np.flip(A, 1)
4036+
array([[[2, 3],
4037+
[0, 1]],
4038+
[[6, 7],
4039+
[4, 5]]])
4040+
>>> np.flip(A)
4041+
array([[[7, 6],
4042+
[5, 4]],
4043+
[[3, 2],
4044+
[1, 0]]])
4045+
>>> np.flip(A, (0, 2))
4046+
array([[[5, 4],
4047+
[7, 6]],
4048+
[[1, 0],
4049+
[3, 2]]])
4050+
"""
4051+
return _mx_nd_np.flip(x, axis, out=out)

python/mxnet/symbol/numpy/_symbol.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor',
3636
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'tensordot',
3737
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'mean',
38-
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign']
38+
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
39+
'flip']
3940

4041

4142
def _num_outputs(sym):
@@ -2778,4 +2779,42 @@ def copysign(x1, x2, out=None):
27782779
return _ufunc_helper(x1, x2, _npi.copysign, _np.copysign, _npi.copysign_scalar, _npi.rcopysign_scalar, out)
27792780

27802781

2782+
@set_module('mxnet.symbol.numpy')
2783+
def flip(x, axis=None, out=None):
2784+
r"""
2785+
flip(x, axis=None, out=None)
2786+
2787+
Reverse the order of elements in an array along the given axis.
2788+
2789+
The shape of the array is preserved, but the elements are reordered.
2790+
2791+
Parameters
2792+
----------
2793+
m : _Symbol or scalar
2794+
Input array.
2795+
axis : None or int or tuple of ints, optional
2796+
Axis or axes along which to flip over. The default,
2797+
axis=None, will flip over all of the axes of the input array.
2798+
If axis is negative it counts from the last to the first axis.
2799+
2800+
If axis is a tuple of ints, flipping is performed on all of the axes
2801+
specified in the tuple.
2802+
out : _Symbol or scalar, optional
2803+
Alternative output array in which to place the result. It must have
2804+
the same shape and type as the expected output.
2805+
2806+
Returns
2807+
-------
2808+
out : _Symbol or scalar
2809+
A view of `m` with the entries of axis reversed. Since a view is
2810+
returned, this operation is done in constant time.
2811+
"""
2812+
if isinstance(x, numeric_types):
2813+
return _np.flip(x, axis)
2814+
elif isinstance(x, _Symbol):
2815+
return _npi.flip(x, axis, out=out)
2816+
else:
2817+
raise TypeError('type {} not supported'.format(str(type(x))))
2818+
2819+
27812820
_set_np_symbol_class(_Symbol)

src/operator/numpy/np_matrix_op-inl.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,71 @@ void NumpyTranspose(const nnvm::NodeAttrs& attrs,
6060
}
6161
}
6262

63+
struct FlipParam : public dmlc::Parameter<FlipParam> {
64+
mxnet::Tuple<int> axis;
65+
DMLC_DECLARE_PARAMETER(FlipParam) {
66+
DMLC_DECLARE_FIELD(axis)
67+
.describe("The axis which to flip elements.");
68+
}
69+
};
70+
71+
#define FLIP_MAX_DIM 10
72+
#define FLIP_MIN_DIM -1
73+
74+
template<typename xpu>
75+
void NumpyFlipForwardImpl(const OpContext& ctx,
76+
const std::vector<TBlob>& inputs,
77+
const std::vector<TBlob>& outputs,
78+
const std::vector<index_t>& stride_,
79+
const std::vector<index_t>& trailing_,
80+
const index_t& flip_index);
81+
82+
template<typename xpu>
83+
void NumpyFlipForward(const nnvm::NodeAttrs& attrs,
84+
const OpContext& ctx,
85+
const std::vector<TBlob>& inputs,
86+
const std::vector<OpReqType>& req,
87+
const std::vector<TBlob>& outputs) {
88+
const FlipParam& param = nnvm::get<FlipParam>(attrs.parsed);
89+
mxnet::Tuple<int> axistemp;
90+
CHECK_EQ(inputs[0].type_flag_, outputs[0].type_flag_);
91+
CHECK_LT(param.axis.ndim(), FLIP_MAX_DIM);
92+
CHECK_GE(param.axis.ndim(), FLIP_MIN_DIM);
93+
if (param.axis.ndim() == FLIP_MIN_DIM) {
94+
if (inputs[0].shape_.ndim() == 0) {
95+
mshadow::Stream<xpu> *s = ctx.get_stream<xpu>();
96+
MSHADOW_TYPE_SWITCH(inputs[0].type_flag_, DType, {
97+
mshadow::Copy(outputs[0].FlatTo1D<xpu, DType>(s), inputs[0].FlatTo1D<xpu, DType>(s), s);
98+
});
99+
return;
100+
}
101+
std::vector<int> temp;
102+
for (int i = 0; i < inputs[0].shape_.ndim(); i++) {
103+
temp.push_back(i);
104+
}
105+
axistemp.assign(temp.begin(), temp.end());
106+
} else {
107+
axistemp = param.axis;
108+
}
109+
110+
const mxnet::TShape& ishape = inputs[0].shape_;
111+
if (ishape.ProdShape(0, ishape.ndim()) == 0) {
112+
return; // zero shape
113+
}
114+
std::vector<index_t> stride_(axistemp.ndim());
115+
std::vector<index_t> trailing_(axistemp.ndim());
116+
index_t flip_index = 0;
117+
for (int axis : axistemp) {
118+
CHECK_LT(axis, ishape.ndim());
119+
stride_[flip_index] = ishape[axis];
120+
trailing_[flip_index] = 1;
121+
for (int i2 = axis + 1; i2 < ishape.ndim(); ++i2) {
122+
trailing_[flip_index] *= ishape[i2];
123+
}
124+
flip_index++;
125+
}
126+
NumpyFlipForwardImpl<xpu>(ctx, inputs, outputs, stride_, trailing_, flip_index);
127+
}
63128
} // namespace op
64129
} // namespace mxnet
65130

src/operator/numpy/np_matrix_op.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,5 +345,51 @@ Examples::
345345
.add_argument("data", "NDArray-or-Symbol[]", "List of arrays to stack")
346346
.add_arguments(StackParam::__FIELDS__());
347347

348+
template<>
349+
void NumpyFlipForwardImpl<cpu>(const OpContext& ctx,
350+
const std::vector<TBlob>& inputs,
351+
const std::vector<TBlob>& outputs,
352+
const std::vector<index_t>& stride_,
353+
const std::vector<index_t>& trailing_,
354+
const index_t& flip_index) {
355+
mshadow::Stream<cpu> *s = ctx.get_stream<cpu>();
356+
MSHADOW_TYPE_SWITCH(outputs[0].type_flag_, DType, {
357+
mxnet_op::Kernel<reverse, cpu>::Launch(s, inputs[0].Size(), flip_index,
358+
inputs[0].dptr<DType>(), outputs[0].dptr<DType>(),
359+
stride_.data(), trailing_.data());
360+
});
361+
}
362+
363+
DMLC_REGISTER_PARAMETER(FlipParam);
364+
365+
NNVM_REGISTER_OP(_npi_flip)
366+
.set_num_outputs(1)
367+
.set_num_inputs(1)
368+
.set_attr_parser(ParamParser<FlipParam>)
369+
.set_attr<nnvm::FListInputNames>("FListInputNames",
370+
[](const NodeAttrs& attrs) {
371+
return std::vector<std::string> {"data"};
372+
})
373+
.set_attr<FResourceRequest>("FResourceRequest",
374+
[](const NodeAttrs& attrs) {
375+
return std::vector<ResourceRequest> {ResourceRequest::kTempSpace};
376+
})
377+
.set_attr<mxnet::FInferShape>("FInferShape", ElemwiseShape<1, 1>)
378+
.set_attr<nnvm::FInferType>("FInferType", ElemwiseType<1, 1>)
379+
.set_attr<FCompute>("FCompute<cpu>", NumpyFlipForward<cpu>)
380+
.set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseNone{"_backward_npi_flip"})
381+
.add_argument("data", "NDArray-or-Symbol", "Input data array")
382+
.add_arguments(FlipParam::__FIELDS__());
383+
384+
NNVM_REGISTER_OP(_backward_npi_flip)
385+
.set_num_inputs(1)
386+
.set_num_outputs(1)
387+
.set_attr_parser(ParamParser<FlipParam>)
388+
.set_attr<nnvm::TIsBackward>("TIsBackward", true)
389+
.set_attr<FResourceRequest>("FResourceRequest",
390+
[](const NodeAttrs& attrs) {
391+
return std::vector<ResourceRequest> {ResourceRequest::kTempSpace};
392+
})
393+
.set_attr<FCompute>("FCompute<cpu>", NumpyFlipForward<cpu>);
348394
} // namespace op
349395
} // namespace mxnet

src/operator/numpy/np_matrix_op.cu

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,39 @@ NNVM_REGISTER_OP(_backward_np_concat)
4646
NNVM_REGISTER_OP(_npi_stack)
4747
.set_attr<FCompute>("FCompute<gpu>", StackOpForward<gpu>);
4848

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

0 commit comments

Comments
 (0)