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

Commit

Permalink
add: numpy op tril_indices (#17904)
Browse files Browse the repository at this point in the history
  • Loading branch information
yijunc authored Apr 10, 2020
1 parent 3dab617 commit af76466
Show file tree
Hide file tree
Showing 7 changed files with 446 additions and 3 deletions.
84 changes: 83 additions & 1 deletion python/mxnet/ndarray/numpy/_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
'absolute', 'exp', 'expm1', 'arcsin', 'arccos', 'arctan', 'sign', 'log', 'degrees', 'log2', 'matmul',
'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', 'fix', 'ceil', 'floor', 'histogram',
'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'argsort', 'all', 'any', 'sort',
'tensordot', 'eye', 'linspace', 'median',
'tensordot', 'eye', 'linspace', 'median', 'tril_indices',
'logspace', 'expand_dims', 'tile', 'arange', 'array_split', 'split', 'hsplit', 'vsplit', 'dsplit',
'concatenate', 'append', 'stack', 'vstack', 'row_stack', 'column_stack', 'hstack', 'dstack',
'average', 'mean', 'maximum', 'fmax', 'minimum', 'fmin', 'around', 'round', 'round_', 'flatnonzero',
Expand Down Expand Up @@ -4530,6 +4530,88 @@ def clip(a, a_min, a_max, out=None):
return _npi.clip(a, a_min, a_max, out=out)


@set_module('mxnet.ndarray.numpy')
def tril_indices(n, k=0, m=None):
"""
Return the indices for the lower-triangle of an (n, m) array.
Parameters
----------
n : int
The row dimension of the arrays for which the returned
indices will be valid.
k : int, optional
Diagonal offset (see `tril` for details).
m : int, optional
.. versionadded:: 1.9.0
The column dimension of the arrays for which the returned
arrays will be valid.
By default `m` is taken equal to `n`.
Returns
-------
inds : tuple of arrays
The indices for the triangle. The returned tuple contains two arrays,
each with the indices along one dimension of the array.
See also
--------
triu_indices : similar function, for upper-triangular.
mask_indices : generic function accepting an arbitrary mask function.
tril, triu
Notes
-----
.. versionadded:: 1.4.0
Examples
--------
Compute two different sets of indices to access 4x4 arrays, one for the
lower triangular part starting at the main diagonal, and one starting two
diagonals further right:
>>> il1 = np.tril_indices(4)
>>> il2 = np.tril_indices(4, 2)
Here is how they can be used with a sample array:
>>> a = np.arange(16).reshape(4, 4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
Both for indexing:
>>> a[il1]
array([ 0, 4, 5, 8, 9, 10, 12, 13, 14, 15])
And for assigning values:
>>> a[il1] = -1
>>> a
array([[-1, 1, 2, 3],
[-1, -1, 6, 7],
[-1, -1, -1, 11],
[-1, -1, -1, -1]])
These cover almost the whole array (two diagonals right of the main one):
>>> a[il2] = -10
>>> a
array([[-10, -10, -10, 3],
[-10, -10, -10, -10],
[-10, -10, -10, -10],
[-10, -10, -10, -10]])
"""
if m is None:
m = n
return tuple(_npi.tril_indices(n, k, m))


@set_module('mxnet.ndarray.numpy')
def argmax(a, axis=None, out=None):
r"""
Expand Down
80 changes: 79 additions & 1 deletion python/mxnet/numpy/multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
'degrees', 'log2', 'log1p', 'rint', 'radians', 'reciprocal', 'square', 'negative', 'histogram',
'fix', 'ceil', 'floor', 'trunc', 'logical_not', 'arcsinh', 'arccosh', 'arctanh', 'append', 'argsort',
'sort', 'tensordot', 'eye', 'linspace', 'logspace', 'expand_dims', 'tile', 'arange',
'array_split', 'split', 'hsplit', 'vsplit', 'dsplit', 'flatnonzero',
'array_split', 'split', 'hsplit', 'vsplit', 'dsplit', 'flatnonzero', 'tril_indices',
'concatenate', 'stack', 'vstack', 'row_stack', 'column_stack', 'hstack', 'dstack',
'average', 'mean', 'maximum', 'fmax', 'minimum', 'fmin',
'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'insert',
Expand Down Expand Up @@ -5535,6 +5535,84 @@ def tril(m, k=0):
return _mx_nd_np.tril(m, k)


@set_module('mxnet.numpy')
def tril_indices(n, k=0, m=None):
"""
Return the indices for the lower-triangle of an (n, m) array.
Parameters
----------
n : int
The row dimension of the arrays for which the returned
indices will be valid.
k : int, optional
Diagonal offset (see `tril` for details).
m : int, optional
.. versionadded:: 1.9.0
The column dimension of the arrays for which the returned
arrays will be valid.
By default `m` is taken equal to `n`.
Returns
-------
inds : tuple of arrays
The indices for the triangle. The returned tuple contains two arrays,
each with the indices along one dimension of the array.
See also
--------
triu_indices : similar function, for upper-triangular.
mask_indices : generic function accepting an arbitrary mask function.
tril, triu
Examples
--------
Compute two different sets of indices to access 4x4 arrays, one for the
lower triangular part starting at the main diagonal, and one starting two
diagonals further right:
>>> il1 = np.tril_indices(4)
>>> il2 = np.tril_indices(4, 2)
Here is how they can be used with a sample array:
>>> a = np.arange(16).reshape(4, 4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
Both for indexing:
>>> a[il1]
array([ 0, 4, 5, 8, 9, 10, 12, 13, 14, 15])
And for assigning values:
>>> a[il1] = -1
>>> a
array([[-1, 1, 2, 3],
[-1, -1, 6, 7],
[-1, -1, -1, 11],
[-1, -1, -1, -1]])
These cover almost the whole array (two diagonals right of the main one):
>>> a[il2] = -10
>>> a
array([[-10, -10, -10, 3],
[-10, -10, -10, -10],
[-10, -10, -10, -10],
[-10, -10, -10, -10]])
"""
if m is None:
m = n
return tuple(_mx_nd_np.tril_indices(n, k, m))


# pylint: disable=redefined-outer-name
@set_module('mxnet.numpy')
def arange(start, stop=None, step=1, dtype=None, ctx=None):
Expand Down
85 changes: 84 additions & 1 deletion python/mxnet/symbol/numpy/_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
'logspace', 'expand_dims', 'tile', 'arange', 'array_split', 'split', 'hsplit', 'vsplit', 'dsplit',
'concatenate', 'append', 'stack', 'vstack', 'row_stack', 'column_stack', 'hstack', 'dstack',
'average', 'mean', 'maximum', 'fmax', 'minimum', 'fmin', 'any', 'all', 'around', 'round', 'round_',
'flatnonzero',
'flatnonzero', 'tril_indices',
'swapaxes', 'clip', 'argmax', 'argmin', 'std', 'var', 'indices', 'copysign', 'ravel', 'unravel_index',
'diag_indices_from', 'hanning', 'hamming', 'blackman', 'flip', 'flipud', 'fliplr',
'hypot', 'bitwise_and', 'bitwise_xor', 'bitwise_or', 'rad2deg', 'deg2rad', 'unique', 'lcm', 'interp',
Expand Down Expand Up @@ -2199,6 +2199,89 @@ def tril(m, k=0):
return _npi.tril(m, k)


@set_module('mxnet.symbol.numpy')
def tril_indices(n, k=0, m=None):
"""
Return the indices for the lower-triangle of an (n, m) array.
Parameters
----------
n : int
The row dimension of the arrays for which the returned
indices will be valid.
k : int, optional
Diagonal offset (see `tril` for details).
m : int, optional
.. versionadded:: 1.9.0
The column dimension of the arrays for which the returned
arrays will be valid.
By default `m` is taken equal to `n`.
Returns
-------
inds : tuple of _Symbol
The indices for the triangle. The returned tuple contains two arrays,
each with the indices along one dimension of the array.
See also
--------
triu_indices : similar function, for upper-triangular.
mask_indices : generic function accepting an arbitrary mask function.
tril, triu
Notes
-----
.. versionadded:: 1.4.0
Examples
--------
Compute two different sets of indices to access 4x4 arrays, one for the
lower triangular part starting at the main diagonal, and one starting two
diagonals further right:
>>> il1 = np.tril_indices(4)
>>> il2 = np.tril_indices(4, 2)
Here is how they can be used with a sample array:
>>> a = np.arange(16).reshape(4, 4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
Both for indexing:
>>> a[il1]
array([ 0, 4, 5, 8, 9, 10, 12, 13, 14, 15])
And for assigning values:
>>> a[il1] = -1
>>> a
array([[-1, 1, 2, 3],
[-1, -1, 6, 7],
[-1, -1, -1, 11],
[-1, -1, -1, -1]])
These cover almost the whole array (two diagonals right of the main one):
>>> a[il2] = -10
>>> a
array([[-10, -10, -10, 3],
[-10, -10, -10, -10],
[-10, -10, -10, -10],
[-10, -10, -10, -10]])
"""
if m is None:
m = n
return _npi.tril_indices(n, k, m)


def _unary_func_helper(x, fn_array, fn_scalar, out=None, **kwargs):
"""Helper function for unary operators.
Expand Down
100 changes: 100 additions & 0 deletions src/operator/numpy/np_matrix_op-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,106 @@ void NumpyVstackBackward(const nnvm::NodeAttrs& attrs,
});
}

struct NumpyTrilindicesParam : public dmlc::Parameter<NumpyTrilindicesParam> {
int n;
int k;
int m;
DMLC_DECLARE_PARAMETER(NumpyTrilindicesParam) {
DMLC_DECLARE_FIELD(n)
.describe("The row dimension of the arrays for which"
"the returned indices will be valid.");
DMLC_DECLARE_FIELD(k)
.set_default(0)
.describe("Diagonal offset");
DMLC_DECLARE_FIELD(m)
.describe("The column dimension of the arrays for "
"which the returned arrays will be valid."
"By default m is taken equal to n.");
}
void SetAttrDict(std::unordered_map<std::string, std::string>* dict) {
std::ostringstream n_s, k_s, m_s;
n_s << n;
k_s << k;
m_s << m;
(*dict)["n"] = n_s.str();
(*dict)["k"] = k_s.str();
(*dict)["m"] = m_s.str();
}
};

template<int req>
struct TrilindicesOpForwardImpl {
template<typename DType>
MSHADOW_XINLINE static void Map(int i, DType* out_data0, DType* out_data1,
int* data, int length) {
KERNEL_ASSIGN(out_data0[i], req, data[i]);
KERNEL_ASSIGN(out_data1[i], req, data[i + length]);
}
};

template<typename xpu>
void TrilindicesOpForward(const nnvm::NodeAttrs& attrs,
const OpContext& ctx,
const std::vector<TBlob>& inputs,
const std::vector<OpReqType>& req,
const std::vector<TBlob>& outputs) {
using namespace mxnet_op;
using namespace mshadow;
CHECK_EQ(inputs.size(), 0U);
CHECK_EQ(outputs.size(), 2U);
Stream<xpu> *s = ctx.get_stream<xpu>();
const NumpyTrilindicesParam& param =
nnvm::get<NumpyTrilindicesParam>(attrs.parsed);

const TBlob& out_data0 = outputs[0];
const TBlob& out_data1 = outputs[1];

CHECK_EQ(out_data0.shape_[0], out_data1.shape_[0]);
int length = out_data0.shape_[0];

std::vector<int> indices_cpu(2 * length, 0);
size_t total_temp_size = 2 * length * sizeof(int);
Tensor<xpu, 1, char> temp_space =
ctx.requested[0].get_space_typed<xpu, 1, char>(Shape1(total_temp_size), s);
int* indices = reinterpret_cast<int*>(temp_space.dptr_);

int n = param.n;
int m = param.m;
int k = param.k;

int end = k;
int idx = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= std::min(end, m - 1); j++) {
indices_cpu[idx] = i;
indices_cpu[idx + length] = j;
idx++;
}
end++;
}

if (ctx.run_ctx.ctx.dev_mask() == gpu::kDevMask) {
#if MXNET_USE_CUDA
cudaMemcpyAsync(indices, indices_cpu.data(),
indices_cpu.size() * sizeof(int),
cudaMemcpyHostToDevice,
Stream<gpu>::GetStream(ctx.get_stream<gpu>()));
#else
LOG(FATAL) << "Illegal attempt to use GPU in a CPU-only build";
#endif
} else {
std::memcpy(indices, indices_cpu.data(), indices_cpu.size() * sizeof(int));
}

MSHADOW_IDX_TYPE_SWITCH(out_data0.type_flag_, DType, {
MXNET_ASSIGN_REQ_SWITCH(req[0], req_type, {
Kernel<TrilindicesOpForwardImpl<req_type>, xpu>::Launch(
s, length, out_data0.dptr<DType>(), out_data1.dptr<DType>(),
indices, length);
});
});
}

struct NumpyRollParam : public dmlc::Parameter<NumpyRollParam> {
dmlc::optional<mxnet::TShape> shift;
dmlc::optional<mxnet::TShape> axis;
Expand Down
Loading

0 comments on commit af76466

Please sign in to comment.