diff --git a/mindspore/ccsrc/backend/kernel_compiler/cpu/sparse_to_dense_cpu_kernal.cc b/mindspore/ccsrc/backend/kernel_compiler/cpu/sparse_to_dense_cpu_kernal.cc new file mode 100644 index 00000000000..a48f4c94189 --- /dev/null +++ b/mindspore/ccsrc/backend/kernel_compiler/cpu/sparse_to_dense_cpu_kernal.cc @@ -0,0 +1,98 @@ +/** + * Copyright 2021 Huawei Technologies Co., Ltd + * + * Licensed 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. + */ + +#include "backend/kernel_compiler/cpu/sparse_to_dense_cpu_kernal.h" +#include +#include "runtime/device/cpu/cpu_device_address.h" + +namespace mindspore { +namespace kernel { +template +void SparseToDenseCPUKernel::InitKernel(const CNodePtr &kernel_node) { + CheckParam(kernel_node); + indices_shape_ = AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, 0); + values_shape_ = AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, 1); + dense_shape_shape_ = AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, 2); + output_shape_ = AnfAlgo::GetOutputInferShape(kernel_node, 0); + if (!indices_shape_.size() || !values_shape_.size() || !output_shape_.size()) { + MS_LOG(EXCEPTION) << "Input NULL"; + } + if (indices_shape_.size() > 2 || indices_shape_[0] != values_shape_[0]) { + MS_LOG(EXCEPTION) << "Input Error"; + } +} + +size_t DenseGetTensorLen(const std::vector &shape) { + size_t len = 1; + for (size_t i = 0; i < shape.size(); i++) { + len *= shape[i]; + } + return len; +} + +template +bool SparseToDenseCPUKernel::Launch(const std::vector &inputs, + const std::vector & /*workspace*/, + const std::vector &outputs) { + auto indices_addr = reinterpret_cast(inputs[0]->addr); + auto values_addr = reinterpret_cast(inputs[1]->addr); + auto output_addr = reinterpret_cast(outputs[0]->addr); + + size_t output_len = DenseGetTensorLen(output_shape_); + memset(output_addr, 0, output_len * sizeof(T)); + std::vector cargo(output_shape_.size(), 0); + + size_t i = output_shape_.size() - 1; + switch (indices_shape_.size()) { + case 1: + for (i = 0; i < indices_shape_[0]; i++) { + output_addr[indices_addr[i]] = values_addr[i]; + } + break; + + case 2: + cargo[i] = 1; + for (; i >= 1; i--) { + cargo[i - 1] = cargo[i] * output_shape_[i]; + } + for (i = 0; i < indices_shape_[0]; i++) { + size_t out_index = 0; + for (size_t j = 0; j < indices_shape_[1]; j++) { + out_index += (*(indices_addr + i * indices_shape_[1] + j)) * cargo[j]; + } + output_addr[out_index] = values_addr[i]; + } + break; + + default: + break; + } + return true; +} + +template +void SparseToDenseCPUKernel::CheckParam(const CNodePtr &kernel_node) { + size_t input_num = AnfAlgo::GetInputTensorNum(kernel_node); + if (input_num != 3) { + MS_LOG(EXCEPTION) << "Input number is " << input_num << ", but SparseToDenseCPUKernel needs 3 input."; + } + size_t output_num = AnfAlgo::GetOutputTensorNum(kernel_node); + if (output_num != 1) { + MS_LOG(EXCEPTION) << "Output number is " << output_num << ", but SparseToDenseCPUKernel needs 1 output."; + } +} +} // namespace kernel +} // namespace mindspore diff --git a/mindspore/ccsrc/backend/kernel_compiler/cpu/sparse_to_dense_cpu_kernal.h b/mindspore/ccsrc/backend/kernel_compiler/cpu/sparse_to_dense_cpu_kernal.h new file mode 100644 index 00000000000..d15820549a1 --- /dev/null +++ b/mindspore/ccsrc/backend/kernel_compiler/cpu/sparse_to_dense_cpu_kernal.h @@ -0,0 +1,207 @@ +/** + * Copyright 2021 Huawei Technologies Co., Ltd + * + * Licensed 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. + */ + +#ifndef MINDSPORE_CCSRC_BACKEND_KERNEL_COMPILER_CPU_SPARSETODENSE_CPU_KERNEL_H_ +#define MINDSPORE_CCSRC_BACKEND_KERNEL_COMPILER_CPU_SPARSETODENSE_CPU_KERNEL_H_ +#include +#include +#include +#include "backend/kernel_compiler/cpu/cpu_kernel.h" +#include "backend/kernel_compiler/cpu/cpu_kernel_factory.h" + +namespace mindspore { +namespace kernel { +template +class SparseToDenseCPUKernel : public CPUKernel { + public: + SparseToDenseCPUKernel() = default; + ~SparseToDenseCPUKernel() override = default; + + void InitKernel(const CNodePtr &kernel_node) override; + + bool Launch(const std::vector &inputs, const std::vector &workspace, + const std::vector &outputs) override; + + private: + void CheckParam(const CNodePtr &kernel_node); + std::vector indices_shape_; + std::vector values_shape_; + std::vector dense_shape_shape_; + std::vector output_shape_; +}; + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeInt32), + SparseToDenseCPUKernel, int32_t, int32_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeInt8) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeInt8), + SparseToDenseCPUKernel, int32_t, int8_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeUInt8) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeUInt8), + SparseToDenseCPUKernel, int32_t, uint8_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeInt16) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeInt16), + SparseToDenseCPUKernel, int32_t, int16_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeUInt16) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeUInt16), + SparseToDenseCPUKernel, int32_t, uint16_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeInt64), + SparseToDenseCPUKernel, int32_t, int64_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeFloat16) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeFloat16), + SparseToDenseCPUKernel, int32_t, float16); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeFloat) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeFloat), + SparseToDenseCPUKernel, int32_t, float); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeFloat64) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeFloat64), + SparseToDenseCPUKernel, int32_t, double); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeBool) + .AddInputAttr(kNumberTypeInt32) + .AddOutputAttr(kNumberTypeBool), + SparseToDenseCPUKernel, int32_t, bool); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeInt32) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeInt32), + SparseToDenseCPUKernel, int64_t, int32_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeInt64), + SparseToDenseCPUKernel, int64_t, int64_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeInt8) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeInt8), + SparseToDenseCPUKernel, int64_t, int8_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeUInt8) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeUInt8), + SparseToDenseCPUKernel, int64_t, uint8_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeInt16) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeInt16), + SparseToDenseCPUKernel, int64_t, int16_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeUInt16) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeUInt16), + SparseToDenseCPUKernel, int64_t, uint16_t); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeFloat16) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeFloat16), + SparseToDenseCPUKernel, int64_t, float16); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeFloat) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeFloat), + SparseToDenseCPUKernel, int64_t, float); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeFloat64) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeFloat64), + SparseToDenseCPUKernel, int64_t, double); + +MS_REG_CPU_KERNEL_T_S(SparseToDense, + KernelAttr() + .AddInputAttr(kNumberTypeInt64) + .AddInputAttr(kNumberTypeBool) + .AddInputAttr(kNumberTypeInt64) + .AddOutputAttr(kNumberTypeBool), + SparseToDenseCPUKernel, int64_t, bool); +} // namespace kernel +} // namespace mindspore +#endif // MINDSPORE_CCSRC_BACKEND_KERNEL_COMPILER_CPU_SPARSETODENSE_CPU_KERNEL_H_ diff --git a/tests/st/ops/cpu/test_sparse_to_dense_op.py b/tests/st/ops/cpu/test_sparse_to_dense_op.py new file mode 100644 index 00000000000..702eb7928c5 --- /dev/null +++ b/tests/st/ops/cpu/test_sparse_to_dense_op.py @@ -0,0 +1,198 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed 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. +# ============================================================================ + +import numpy as np +import pytest +import mindspore.context as context +from mindspore import Tensor +from mindspore.nn import Cell +from mindspore.ops import operations as P + +context.set_context(mode=context.GRAPH_MODE, enable_graph_kernel=True, device_target="CPU") + + +class SparseToDenseNet(Cell): + def __init__(self): + super(SparseToDenseNet, self).__init__() + self.sparse_to_dense = P.SparseToDense() + + def construct(self, indices, values, dense_shape): + return self.sparse_to_dense(indices, values, dense_shape) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_A(): + np.random.seed(0) + indices = np.array([[0, 1], [1, 2]]).astype(np.int32) + values = np.array([7, 8]).astype(np.int32) + dense_shape = (3, 4) + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + expect_output = np.array([[0, 7, 0, 0], + [0, 0, 8, 0], + [0, 0, 0, 0]]).astype(np.int32) + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_B(): + np.random.seed(0) + indices = np.array([[0, 1], [1, 2], [2, 3]]).astype(np.int32) + values = np.array([6.5, 7.5, 9.5]).astype(np.float64) + dense_shape = (3, 4) + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + expect_output = np.array([[0, 6.5, 0, 0], + [0, 0, 7.5, 0], + [0, 0, 0, 9.5]]).astype(np.float64) + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_C(): + np.random.seed(0) + indices = np.array([[0, 1, 0, 0], + [1, 0, 0, 2], + [2, 0, 3, 0], + [4, 2, 3, 5]]).astype(np.int32) + values = np.array([26.5, 17.5, 39.5, 11.5]).astype(np.float16) + dense_shape = (10, 8, 5, 10) + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + expect_output = np.zeros([10, 8, 5, 10]).astype(np.float16) + for i in range(0, indices.shape[0]): + j = indices[i][0] + k = indices[i][1] + l = indices[i][2] + m = indices[i][3] + expect_output[j][k][l][m] = values[i] + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_D(): + np.random.seed(0) + indices = np.array([[0, 1, 0, 0, 2, 1], + [9, 0, 0, 8, 0, 0], + [2, 0, 4, 0, 1, 1], + [4, 2, 3, 5, 0, 2], + [7, 4, 3, 9, 0, 1]]).astype(np.int32) + values = np.array([1, 1, 1, 1, 1]).astype(np.bool) + dense_shape = (10, 5, 5, 10, 3, 3) + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + expect_output = np.zeros([10, 5, 5, 10, 3, 3]).astype(np.bool) + for i in range(0, indices.shape[0]): + j = indices[i][0] + k = indices[i][1] + l = indices[i][2] + m = indices[i][3] + u = indices[i][4] + v = indices[i][5] + expect_output[j][k][l][m][u][v] = values[i] + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_E(): + indices = np.array([2, 5, 7]).astype(np.int32) + values = np.array([17, 18, 19]).astype(np.int8) + dense_shape = ([10]) + expect_output = np.zeros([10]).astype(np.int8) + for i in range(0, indices.shape[0]): + j = indices[i] + expect_output[j] = values[i] + + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_F(): + indices = np.array([2, 4, 18]).astype(np.int32) + values = np.array([-23, 18, -1]).astype(np.int16) + dense_shape = ([20]) + expect_output = np.zeros([20]).astype(np.int16) + for i in range(0, indices.shape[0]): + j = indices[i] + expect_output[j] = values[i] + + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_G(): + indices = np.array([2, 5, 7]).astype(np.int32) + values = np.array([17, 18, 19]).astype(np.uint8) + dense_shape = ([10]) + expect_output = np.zeros([10]).astype(np.uint8) + for i in range(0, indices.shape[0]): + j = indices[i] + expect_output[j] = values[i] + + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_H(): + indices = np.array([2, 5, 7]).astype(np.int32) + values = np.array([17, 18, 19]).astype(np.uint16) + dense_shape = ([10]) + expect_output = np.zeros([10]).astype(np.uint16) + for i in range(0, indices.shape[0]): + j = indices[i] + expect_output[j] = values[i] + + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu_training +@pytest.mark.env_onecard +def test_sparse_to_dense_I(): + indices = np.array([2, 5, 7]).astype(np.int64) + values = np.array([17, 18, 19]).astype(np.float16) + dense_shape = ([10]) + expect_output = np.zeros([10]).astype(np.float16) + for i in range(0, indices.shape[0]): + j = indices[i] + expect_output[j] = values[i] + + net = SparseToDenseNet() + result = net(Tensor(indices), Tensor(values), dense_shape) + assert np.allclose(result.asnumpy(), expect_output, rtol=1.e-4, atol=1.e-8, equal_nan=True)