This is an automated email from the ASF dual-hosted git repository.
haibin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/master by this push:
new 1b96fc9 getnnz operator for CSR matrix (#12908)
1b96fc9 is described below
commit 1b96fc951455ddc20b68a1fe259d98d1942256d4
Author: Haibin Lin <[email protected]>
AuthorDate: Wed Oct 24 16:14:47 2018 -0700
getnnz operator for CSR matrix (#12908)
* nnz
* update err msg
* skip nnz test on gpu
---
docs/api/python/ndarray/contrib.md | 2 +
src/operator/contrib/nnz.cc | 188 +++++++++++++++++++++++++++
tests/python/unittest/test_sparse_ndarray.py | 18 +++
3 files changed, 208 insertions(+)
diff --git a/docs/api/python/ndarray/contrib.md
b/docs/api/python/ndarray/contrib.md
index 97f38a5..63f5f13 100644
--- a/docs/api/python/ndarray/contrib.md
+++ b/docs/api/python/ndarray/contrib.md
@@ -55,6 +55,8 @@ In the rest of this document, we list routines provided by
the `ndarray.contrib`
foreach
while_loop
cond
+ index_copy
+ getnnz
```
## API Reference
diff --git a/src/operator/contrib/nnz.cc b/src/operator/contrib/nnz.cc
new file mode 100644
index 0000000..956b222
--- /dev/null
+++ b/src/operator/contrib/nnz.cc
@@ -0,0 +1,188 @@
+/*
+ * 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) 2018 by Contributors
+ * \file nnz.cc
+ * \brief CPU Implementation of nnz operator
+ */
+#include <mxnet/operator_util.h>
+#include <vector>
+#include <limits>
+#include <algorithm>
+#include "../elemwise_op_common.h"
+#include "../tensor/init_op.h"
+#include "../mshadow_op.h"
+#include "../mxnet_op.h"
+
+namespace mxnet {
+namespace op {
+
+struct NNZParam : public dmlc::Parameter<NNZParam> {
+ dmlc::optional<int> axis;
+ DMLC_DECLARE_PARAMETER(NNZParam) {
+ DMLC_DECLARE_FIELD(axis)
+ .set_default(dmlc::optional<int>())
+ .describe("Select between the number of values across the whole matrix, "
+ "in each column, or in each row.");
+ }
+};
+
+static bool NNZType(const nnvm::NodeAttrs& attrs,
+ std::vector<int> *in_attrs,
+ std::vector<int> *out_attrs) {
+ CHECK_EQ(in_attrs->size(), 1U);
+ CHECK_EQ(out_attrs->size(), 1U);
+ // infer int64 for count
+ TYPE_ASSIGN_CHECK(*out_attrs, 0, mshadow::kInt64);
+ return true;
+}
+
+inline bool NNZShape(const nnvm::NodeAttrs& attrs,
+ std::vector<TShape> *in_attrs,
+ std::vector<TShape> *out_attrs) {
+ CHECK_EQ(in_attrs->size(), 1U);
+ CHECK_EQ(out_attrs->size(), 1U);
+ // csr_matrix is 2-D
+ CHECK_EQ(in_attrs->at(0).ndim(), 2);
+ const NNZParam& param = nnvm::get<NNZParam>(attrs.parsed);
+ // whole matrix
+ if (!param.axis.has_value()) {
+ SHAPE_ASSIGN_CHECK(*out_attrs, 0, mshadow::Shape1(1));
+ } else if (param.axis.value() == 0) {
+ // columns
+ SHAPE_ASSIGN_CHECK(*out_attrs, 0, mshadow::Shape1(in_attrs->at(0)[1]));
+ } else if (param.axis.value() == 1) {
+ // rows
+ SHAPE_ASSIGN_CHECK(*out_attrs, 0, mshadow::Shape1(in_attrs->at(0)[0]));
+ } else {
+ LOG(FATAL) << "Unexpected value for axis(" << param.axis.value()
+ << "). Candidates are None, 0, and 1";
+ }
+ return true;
+}
+
+template<typename xpu>
+void NNZComputeCsrImpl(const NNZParam& param,
+ const OpContext& ctx,
+ const NDArray& input,
+ const OpReqType req,
+ const TBlob& output);
+
+struct CsrNNZRowKernel {
+ /*!
+ * \brief Map function for general case of take grad
+ * \param tid global thread id
+ * \param out ptr to output
+ * \param indptr ptr to source csr indptr
+ */
+ template<typename IType, typename DType>
+ MSHADOW_XINLINE static void Map(int tid, DType* out, const IType* indptr) {
+ out[tid] = static_cast<DType>(indptr[tid + 1] - indptr[tid]);
+ }
+};
+
+template<>
+void NNZComputeCsrImpl<cpu>(const NNZParam& param,
+ const OpContext& ctx,
+ const NDArray& input,
+ const OpReqType req,
+ const TBlob& output) {
+ using namespace csr;
+ CHECK_EQ(req, kWriteTo);
+ mshadow::Stream<cpu> *s = ctx.get_stream<cpu>();
+ if (!input.storage_initialized()) {
+ Fill<false>(s, output, kWriteTo, 0);
+ return;
+ }
+ MSHADOW_TYPE_SWITCH(output.type_flag_, DType, {
+ MSHADOW_IDX_TYPE_SWITCH(input.aux_type(kIndPtr), IType, {
+ DType* out_ptr = output.dptr<DType>();
+ const IType* indptr = input.aux_data(kIndPtr).dptr<IType>();
+ const nnvm::dim_t num_rows = input.shape()[0];
+ if (!param.axis.has_value()) {
+ // whole matrix
+ out_ptr[0] = indptr[num_rows];
+ } else if (param.axis.value() == 0) {
+ // column
+ LOG(FATAL) << "getnnz with axis = 0 is not supported yet";
+ } else if (param.axis.value() == 1) {
+ // row
+ mxnet_op::Kernel<CsrNNZRowKernel, cpu>::Launch(s, num_rows, out_ptr,
indptr);
+ }
+ });
+ });
+}
+
+template<typename xpu>
+void NNZComputeEx(const nnvm::NodeAttrs& attrs,
+ const OpContext& ctx,
+ const std::vector<NDArray>& inputs,
+ const std::vector<OpReqType>& req,
+ const std::vector<NDArray>& outputs) {
+ CHECK_EQ(inputs.size(), 1U);
+ CHECK_EQ(outputs.size(), 1U);
+ CHECK_EQ(req.size(), 1U);
+ const auto in_stype = inputs[0].storage_type();
+ const auto out_stype = outputs[0].storage_type();
+ const NNZParam& param = nnvm::get<NNZParam>(attrs.parsed);
+ if (in_stype == kCSRStorage && out_stype == kDefaultStorage) {
+ NNZComputeCsrImpl<xpu>(param, ctx, inputs[0], req[0], outputs[0].data());
+ } else {
+ LogUnimplementedOp(attrs, ctx, inputs, req, outputs);
+ }
+}
+
+bool NNZStorageType(const nnvm::NodeAttrs& attrs,
+ const int dev_mask,
+ DispatchMode* dispatch_mode,
+ std::vector<int> *in_attrs,
+ std::vector<int> *out_attrs) {
+ CHECK_EQ(in_attrs->size(), 1);
+ CHECK_EQ(out_attrs->size(), 1);
+ bool dispatched = false;
+ const auto in_stype = in_attrs->at(0);
+ auto& out_stype = out_attrs->at(0);
+ // only support csr for now
+ if (!dispatched && in_stype == kCSRStorage) {
+ dispatched = storage_type_assign(&out_stype, kDefaultStorage,
+ dispatch_mode, DispatchMode::kFComputeEx);
+ }
+ return dispatched;
+}
+
+DMLC_REGISTER_PARAMETER(NNZParam);
+
+NNVM_REGISTER_OP(_contrib_getnnz)
+.describe(R"code(Number of stored values for a sparse tensor, including
explicit zeros.
+
+This operator only supports CSR matrix on CPU.
+
+)code" ADD_FILELINE)
+.set_num_inputs(1)
+.set_num_outputs(1)
+.set_attr_parser(ParamParser<NNZParam>)
+.set_attr<nnvm::FInferShape>("FInferShape", NNZShape)
+.set_attr<nnvm::FInferType>("FInferType", NNZType)
+.set_attr<FInferStorageType>("FInferStorageType", NNZStorageType)
+.set_attr<FComputeEx>("FComputeEx<cpu>", NNZComputeEx<cpu>)
+.add_argument("data", "NDArray-or-Symbol", "Input");
+
+} // namespace op
+} // namespace mxnet
diff --git a/tests/python/unittest/test_sparse_ndarray.py
b/tests/python/unittest/test_sparse_ndarray.py
index 43d370b..7600ea9 100644
--- a/tests/python/unittest/test_sparse_ndarray.py
+++ b/tests/python/unittest/test_sparse_ndarray.py
@@ -1032,6 +1032,24 @@ def test_sparse_take():
for m in modes:
check_sparse_take(d, m)
+@with_seed()
+def test_sparse_getnnz():
+ if default_context().device_type is 'gpu':
+ return
+ def check_sparse_getnnz(density, axis):
+ shape = rand_shape_2d()
+ data = rand_ndarray(shape, 'csr', density=density)
+ data_sp = data.asscipy()
+ result = mx.nd.contrib.getnnz(data, axis=axis)
+ expected_result = data_sp.getnnz(axis=axis)
+ assert_almost_equal(result.asnumpy(), expected_result)
+
+ densities = [0, 0.5, 1]
+ axis = [1, None]
+ for d in densities:
+ for a in axis:
+ check_sparse_getnnz(d, a)
+
if __name__ == '__main__':
import nose
nose.runmodule()