Skip to content

Commit

Permalink
np compatible vstack (apache#15850)
Browse files Browse the repository at this point in the history
  • Loading branch information
hzfan authored and drivanov committed Sep 26, 2019
1 parent 50c6b07 commit 1cf7ed8
Show file tree
Hide file tree
Showing 7 changed files with 419 additions and 4 deletions.
53 changes: 52 additions & 1 deletion python/mxnet/ndarray/numpy/_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p',
'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor',
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'tensordot',
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'mean',
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'vstack', 'mean',
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
'ravel']

Expand Down Expand Up @@ -1990,6 +1990,57 @@ def get_list(arrays):
return _npi.stack(*arrays, axis=axis, out=out)


@set_module('mxnet.ndarray.numpy')
def vstack(arrays, out=None):
r"""Stack arrays in sequence vertically (row wise).
This is equivalent to concatenation along the first axis after 1-D arrays
of shape `(N,)` have been reshaped to `(1,N)`. Rebuilds arrays divided by
`vsplit`.
This function makes most sense for arrays with up to 3 dimensions. For
instance, for pixel-data with a height (first axis), width (second axis),
and r/g/b channels (third axis). The functions `concatenate` and `stack`
provide more general stacking and concatenation operations.
Parameters
----------
tup : sequence of ndarrays
The arrays must have the same shape along all but the first axis.
1-D arrays must have the same length.
Returns
-------
stacked : ndarray
The array formed by stacking the given arrays, will be at least 2-D.
Examples
--------
>>> a = np.array([1, 2, 3])
>>> b = np.array([2, 3, 4])
>>> np.vstack((a, b))
array([[1., 2., 3.],
[2., 3., 4.]])
>>> a = np.array([[1], [2], [3]])
>>> b = np.array([[2], [3], [4]])
>>> np.vstack((a, b))
array([[1.],
[2.],
[3.],
[2.],
[3.],
[4.]])
"""
def get_list(arrays):
if not hasattr(arrays, '__getitem__') and hasattr(arrays, '__iter__'):
raise ValueError("expected iterable for arrays but got {}".format(type(arrays)))
return [arr for arr in arrays]

arrays = get_list(arrays)
return _npi.vstack(*arrays)


@set_module('mxnet.ndarray.numpy')
def maximum(x1, x2, out=None):
"""Returns element-wise maximum of the input arrays with broadcasting.
Expand Down
49 changes: 47 additions & 2 deletions python/mxnet/numpy/multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative',
'fix', 'ceil', 'floor', 'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh',
'tensordot', 'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate',
'stack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
'ravel']
'stack', 'vstack', 'mean', 'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices',
'copysign', 'ravel']

# Return code for dispatching indexing function call
_NDARRAY_UNSUPPORTED_INDEXING = -1
Expand Down Expand Up @@ -3560,6 +3560,51 @@ def stack(arrays, axis=0, out=None):
return _mx_nd_np.stack(arrays, axis=axis, out=out)


@set_module('mxnet.numpy')
def vstack(arrays, out=None):
r"""Stack arrays in sequence vertically (row wise).
This is equivalent to concatenation along the first axis after 1-D arrays
of shape `(N,)` have been reshaped to `(1,N)`. Rebuilds arrays divided by
`vsplit`.
This function makes most sense for arrays with up to 3 dimensions. For
instance, for pixel-data with a height (first axis), width (second axis),
and r/g/b channels (third axis). The functions `concatenate` and `stack`
provide more general stacking and concatenation operations.
Parameters
----------
tup : sequence of ndarrays
The arrays must have the same shape along all but the first axis.
1-D arrays must have the same length.
Returns
-------
stacked : ndarray
The array formed by stacking the given arrays, will be at least 2-D.
Examples
--------
>>> a = np.array([1, 2, 3])
>>> b = np.array([2, 3, 4])
>>> np.vstack((a, b))
array([[1., 2., 3.],
[2., 3., 4.]])
>>> a = np.array([[1], [2], [3]])
>>> b = np.array([[2], [3], [4]])
>>> np.vstack((a, b))
array([[1.],
[2.],
[3.],
[2.],
[3.],
[4.]])
"""
return _mx_nd_np.vstack(arrays)


@set_module('mxnet.numpy')
def maximum(x1, x2, out=None):
"""Returns element-wise maximum of the input arrays with broadcasting.
Expand Down
35 changes: 34 additions & 1 deletion python/mxnet/symbol/numpy/_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'log1p',
'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor',
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'tensordot',
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'mean',
'linspace', 'expand_dims', 'tile', 'arange', 'split', 'concatenate', 'stack', 'vstack', 'mean',
'maximum', 'minimum', 'swapaxes', 'clip', 'argmax', 'std', 'var', 'indices', 'copysign',
'ravel']

Expand Down Expand Up @@ -2396,6 +2396,39 @@ def get_list(arrays):
return _npi.stack(*arrays, axis=axis, out=out)


@set_module('mxnet.symbol.numpy')
def vstack(arrays, out=None):
r"""Stack arrays in sequence vertically (row wise).
This is equivalent to concatenation along the first axis after 1-D arrays
of shape `(N,)` have been reshaped to `(1,N)`. Rebuilds arrays divided by
`vsplit`.
This function makes most sense for arrays with up to 3 dimensions. For
instance, for pixel-data with a height (first axis), width (second axis),
and r/g/b channels (third axis). The functions `concatenate` and `stack`
provide more general stacking and concatenation operations.
Parameters
----------
tup : sequence of _Symbol
The arrays must have the same shape along all but the first axis.
1-D arrays must have the same length.
Returns
-------
stacked : _Symbol
The array formed by stacking the given arrays, will be at least 2-D.
"""
def get_list(arrays):
if not hasattr(arrays, '__getitem__') and hasattr(arrays, '__iter__'):
raise ValueError("expected iterable for arrays but got {}".format(type(arrays)))
return [arr for arr in arrays]

arrays = get_list(arrays)
return _npi.vstack(*arrays)


@set_module('mxnet.symbol.numpy')
def maximum(x1, x2, out=None):
return _ufunc_helper(x1, x2, _npi.maximum, _np.maximum, _npi.maximum_scalar, None, out)
Expand Down
80 changes: 80 additions & 0 deletions src/operator/numpy/np_matrix_op-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ struct NumpyTransposeParam : public dmlc::Parameter<NumpyTransposeParam> {
}
};

struct NumpyVstackParam : public dmlc::Parameter<NumpyVstackParam> {
int num_args;
DMLC_DECLARE_PARAMETER(NumpyVstackParam) {
DMLC_DECLARE_FIELD(num_args).set_lower_bound(1)
.describe("Number of inputs to be vstacked.");
}
};

template<typename xpu>
void NumpyTranspose(const nnvm::NodeAttrs& attrs,
const OpContext& ctx,
Expand All @@ -60,6 +68,78 @@ void NumpyTranspose(const nnvm::NodeAttrs& attrs,
}
}

template<typename xpu>
void NumpyVstackForward(const nnvm::NodeAttrs& attrs,
const OpContext& ctx,
const std::vector<TBlob>& inputs,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& outputs) {
using namespace mshadow;
using namespace mshadow_op;

const NumpyVstackParam& param = nnvm::get<NumpyVstackParam>(attrs.parsed);
CHECK_EQ(inputs.size(), param.num_args);
CHECK_EQ(outputs.size(), 1);
CHECK_EQ(req.size(), 1);

// reshape if necessary
std::vector<TBlob> data(param.num_args);
for (int i = 0; i < param.num_args; i++) {
if (inputs[i].shape_.ndim() == 0 || inputs[i].shape_.ndim() == 1) {
TShape shape = Shape2(1, inputs[i].shape_.Size());
data[i] = inputs[i].reshape(shape);
} else {
data[i] = inputs[i];
}
}

// initialize ConcatOp
ConcatParam cparam;
cparam.num_args = param.num_args;
cparam.dim = 0;
MSHADOW_TYPE_SWITCH(inputs[0].type_flag_, DType, {
ConcatOp<xpu, DType> op;
op.Init(cparam);
op.Forward(ctx, data, req, outputs);
});
}

template<typename xpu>
void NumpyVstackBackward(const nnvm::NodeAttrs& attrs,
const OpContext& ctx,
const std::vector<TBlob>& inputs,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& outputs) {
using namespace mshadow;
using namespace mshadow_op;

const NumpyVstackParam& param = nnvm::get<NumpyVstackParam>(attrs.parsed);
CHECK_EQ(inputs.size(), 1);
CHECK_EQ(outputs.size(), param.num_args);
CHECK_EQ(req.size(), param.num_args);

// reshape if necessary
std::vector<TBlob> data(param.num_args);
for (int i = 0; i < param.num_args; i++) {
if (outputs[i].shape_.ndim() == 0 || outputs[i].shape_.ndim() == 1) {
TShape shape = Shape2(1, outputs[i].shape_.Size());
data[i] = outputs[i].reshape(shape);
} else {
data[i] = outputs[i];
}
}

// initialize ConcatOp
ConcatParam cparam;
cparam.num_args = param.num_args;
cparam.dim = 0;
MSHADOW_TYPE_SWITCH(inputs[0].type_flag_, DType, {
ConcatOp<xpu, DType> op;
op.Init(cparam);
op.Backward(ctx, inputs[0], req, data);
});
}

} // namespace op
} // namespace mxnet

Expand Down
Loading

0 comments on commit 1cf7ed8

Please sign in to comment.