From a61f9e5c19b5912c4b6dc916d6f673bd6b114cd0 Mon Sep 17 00:00:00 2001 From: peiing Date: Sun, 13 Mar 2022 09:20:12 +0800 Subject: [PATCH] [feat] [assistant] [I48O5B] Add SparseSparseMinimum --- mindspore/ccsrc/include/common/utils/utils.h | 4 +- .../sparse_sparse_minimum_cpu_kernel.cc | 211 ++++++++++++++++++ .../kernel/sparse_sparse_minimum_cpu_kernel.h | 59 +++++ mindspore/core/ops/core_ops.h | 2 + mindspore/core/ops/sparse_sparse_minimum.cc | 142 ++++++++++++ mindspore/core/ops/sparse_sparse_minimum.h | 42 ++++ .../mindspore/ops/_op_impl/aicpu/__init__.py | 1 + .../_op_impl/aicpu/sparse_sparse_minimum.py | 53 +++++ .../mindspore/ops/operations/sparse_ops.py | 68 ++++++ tests/ut/python/ops/test_ops.py | 10 + 10 files changed, 591 insertions(+), 1 deletion(-) create mode 100644 mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.cc create mode 100644 mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.h create mode 100644 mindspore/core/ops/sparse_sparse_minimum.cc create mode 100644 mindspore/core/ops/sparse_sparse_minimum.h create mode 100644 mindspore/python/mindspore/ops/_op_impl/aicpu/sparse_sparse_minimum.py diff --git a/mindspore/ccsrc/include/common/utils/utils.h b/mindspore/ccsrc/include/common/utils/utils.h index 9cab427065f..21d2fba31dc 100644 --- a/mindspore/ccsrc/include/common/utils/utils.h +++ b/mindspore/ccsrc/include/common/utils/utils.h @@ -298,6 +298,7 @@ constexpr auto kLessEqualOpName = "LessEqual"; constexpr auto kSquareOpName = "Square"; constexpr auto kSelectOpName = "Select"; constexpr auto kCSRSparseMatrixToSparseTensorOpName = "CSRSparseMatrixToSparseTensor"; +constexpr auto kSparseSparseMinimumOpName = "SparseSparseMinimum"; constexpr auto kReduceSumOpName = "ReduceSum"; constexpr auto kReduceMinOpName = "ReduceMin"; constexpr auto kReduceMaxOpName = "ReduceMax"; @@ -898,7 +899,8 @@ const std::set kComputeDepend = {kUniqueOpName, kResizeAreaOpName, kSegmentMeanOpName, kSegmentProdOpName, - kNonZeroOpName}; + kNonZeroOpName, + kSparseSparseMinimumOpName}; const std::set k3DFormatSet = {kOpFormat_NCDHW, kOpFormat_NDC1HWC0, kOpFormat_FRACTAL_Z_3D, kOpFormat_NDHWC, kOpFormat_DHWCN, kOpFormat_DHWNC}; diff --git a/mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.cc b/mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.cc new file mode 100644 index 00000000000..b604ba9b77b --- /dev/null +++ b/mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.cc @@ -0,0 +1,211 @@ +/** + * Copyright 2022 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 "plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.h" +#include "plugin/device/cpu/hal/device/cpu_device_address.h" +#include "Eigen/Core" + +namespace mindspore { +namespace kernel { +namespace { +constexpr int64_t kSparseSparseMinimumInputsNum = 6; +constexpr int64_t kSparseSparseMinimumOutputsNum = 2; +constexpr char kKernelName[] = "SparseSparseMinimum"; +} // namespace + +void SparseSparseMinimumCpuKernelMod::CheckParam(const CNodePtr &kernel_node) { + int64_t input_num = common::AnfAlgo::GetInputTensorNum(kernel_node); + CHECK_KERNEL_INPUTS_NUM(input_num, kSparseSparseMinimumInputsNum, kKernelName); + int64_t output_num = common::AnfAlgo::GetOutputTensorNum(kernel_node); + CHECK_KERNEL_OUTPUTS_NUM(output_num, kSparseSparseMinimumOutputsNum, kKernelName); +} + +bool SparseSparseMinimumCpuKernelMod::Launch(const std::vector &inputs, + const std::vector &, + const std::vector &outputs) { + if (dtype_ == kNumberTypeUInt8) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeUInt16) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeInt8) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeInt16) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeInt32) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeInt64) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeFloat16) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeFloat32) { + LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeFloat64) { + LaunchKernel(inputs, outputs); + } else { + MS_LOG(EXCEPTION) << "For SparseSparseMinimum, data type is " << TypeIdLabel(dtype_) << " which is not supported."; + } + auto node_ = node_wpt_.lock(); + if (!node_) { + MS_LOG(EXCEPTION) << "For SparseSparseMinimum, node_wpt_ is expired. Error no: " << node_; + } + int64_t output_nm = common::AnfAlgo::GetOutputTensorNum(node_); + std::vector dtypes(output_nm); + for (int64_t i = 0; i < output_nm; i++) { + dtypes[i] = AnfAlgo::GetOutputDeviceDataType(node_, i); + } + std::vector dims; + (void)dims.emplace_back(y_nnz_); + (void)dims.emplace_back(num_dims_); + std::vector dim; + (void)dim.emplace_back(y_nnz_); + common::AnfAlgo::SetOutputInferTypeAndShape(dtypes, {dims, dim}, node_.get()); + + return true; +} + +void SparseSparseMinimumCpuKernelMod::InitKernel(const CNodePtr &kernel_node) { + MS_EXCEPTION_IF_NULL(kernel_node); + CheckParam(kernel_node); + constexpr int kzero = 0; + constexpr int kone = 1; + constexpr int kthree = 3; + node_wpt_ = kernel_node; + dtype_ = AnfAlgo::GetInputDeviceDataType(kernel_node, kone); + auto x1_indices_shape = common::AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, kzero); + auto x2_indices_shape = common::AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, kthree); + x1_nnz_ = x1_indices_shape[0]; + x2_nnz_ = x2_indices_shape[0]; + num_dims_ = x1_indices_shape[1]; +} + +template +void SparseSparseMinimumCpuKernelMod::LaunchKernel(const std::vector &inputs, + const std::vector &outputs) { + auto x1_indices_addr = reinterpret_cast(inputs[0]->addr); + auto x1_values_addr = reinterpret_cast(inputs[1]->addr); + auto x1_shape_addr = reinterpret_cast(inputs[2]->addr); + auto x2_indices_addr = reinterpret_cast(inputs[3]->addr); + auto x2_values_addr = reinterpret_cast(inputs[4]->addr); + auto x2_shape_addr = reinterpret_cast(inputs[5]->addr); + auto y_indices_addr = reinterpret_cast(outputs[0]->addr); + auto y_values_addr = reinterpret_cast(outputs[1]->addr); + + for (int64_t n = 0; n < num_dims_; n++) { + if (x1_shape_addr[n] != x2_shape_addr[n]) { + MS_EXCEPTION(ValueError) << "For SparseSparseMinimum, operands' shapes do not match."; + } + } + + std::vector> entries_to_copy; + entries_to_copy.reserve(x1_nnz_ + x2_nnz_); + std::vector out_values; + int64_t i = 0, j = 0; + T s; + while (i < x1_nnz_ && j < x2_nnz_) { + int64_t index_cmp = 0; + for (int64_t n = 0; n < num_dims_; n++) { + if (x1_indices_addr[i * num_dims_ + n] < x2_indices_addr[j * num_dims_ + n]) { + index_cmp = -1; + break; + } + if (x1_indices_addr[i * num_dims_ + n] > x2_indices_addr[j * num_dims_ + n]) { + index_cmp = 1; + break; + } + } + switch (index_cmp) { + case -1: + s = std::min(x1_values_addr[i], T(0)); + entries_to_copy.emplace_back(true, i); + out_values.push_back(s); + ++i; + break; + case 0: + s = std::min(x1_values_addr[i], x2_values_addr[j]); + (void)entries_to_copy.emplace_back(true, i); + out_values.push_back(s); + ++i; + ++j; + break; + case 1: + s = std::min(T(0), x2_values_addr[j]); + entries_to_copy.emplace_back(false, j); + out_values.push_back(s); + ++j; + break; + default: + MS_EXCEPTION(ValueError) << "For SparseSparseMinimum, some inner errors happen in the computation."; + } + } + +#define HANDLE_LEFTOVERS(X1_OR_X2, IDX, IS_A) \ + while (IDX < X1_OR_X2##_nnz_) { \ + s = std::min(X1_OR_X2##_values_addr[IDX], T(0)); \ + entries_to_copy.emplace_back(IS_A, IDX); \ + out_values.push_back(s); \ + ++IDX; \ + } + HANDLE_LEFTOVERS(x1, i, true); + HANDLE_LEFTOVERS(x2, j, false); +#undef HANDLE_LEFTOVERS + + y_nnz_ = out_values.size(); + for (int64_t k = 0; k < y_nnz_; ++k) { + const bool from_x1 = entries_to_copy[k].first; + const int64_t idx = entries_to_copy[k].second; + if (from_x1) { + for (int64_t n = 0; n < num_dims_; n++) { + y_indices_addr[k * num_dims_ + n] = x1_indices_addr[idx * num_dims_ + n]; + } + } else { + for (int64_t n = 0; n < num_dims_; n++) { + y_indices_addr[k * num_dims_ + n] = x2_indices_addr[idx * num_dims_ + n]; + } + } + } + + for (int64_t n = 0; n < y_nnz_; n++) { + y_values_addr[n] = out_values[n]; + } +} +#define ADD_KERNEL(t1, t2, t3, t4, t5, t6, t7, t8) \ + KernelAttr() \ + .AddInputAttr(kNumberType##t1) \ + .AddInputAttr(kNumberType##t2) \ + .AddInputAttr(kNumberType##t3) \ + .AddInputAttr(kNumberType##t4) \ + .AddInputAttr(kNumberType##t5) \ + .AddInputAttr(kNumberType##t6) \ + .AddOutputAttr(kNumberType##t7) \ + .AddOutputAttr(kNumberType##t8) + +std::vector SparseSparseMinimumCpuKernelMod::GetOpSupport() { + static std::vector kernel_attr_list = { + ADD_KERNEL(Int64, UInt8, Int64, Int64, UInt8, Int64, Int64, UInt8), + ADD_KERNEL(Int64, UInt16, Int64, Int64, UInt16, Int64, Int64, UInt16), + ADD_KERNEL(Int64, Int8, Int64, Int64, Int8, Int64, Int64, Int8), + ADD_KERNEL(Int64, Int16, Int64, Int64, Int16, Int64, Int64, Int16), + ADD_KERNEL(Int64, Int32, Int64, Int64, Int32, Int64, Int64, Int32), + ADD_KERNEL(Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64), + ADD_KERNEL(Int64, Float16, Int64, Int64, Float16, Int64, Int64, Float16), + ADD_KERNEL(Int64, Float32, Int64, Int64, Float32, Int64, Int64, Float32), + ADD_KERNEL(Int64, Float64, Int64, Int64, Float64, Int64, Int64, Float64)}; + + return kernel_attr_list; +} +MS_KERNEL_FACTORY_REG(NativeCpuKernelMod, SparseSparseMinimum, SparseSparseMinimumCpuKernelMod); +} // namespace kernel +} // namespace mindspore diff --git a/mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.h b/mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.h new file mode 100644 index 00000000000..4ea8a7e9cbe --- /dev/null +++ b/mindspore/ccsrc/plugin/device/cpu/kernel/sparse_sparse_minimum_cpu_kernel.h @@ -0,0 +1,59 @@ +/** + * Copyright 2022 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_SPARSE_SPARSE_MINIMUM_CPU_KERNEL_H_ +#define MINDSPORE_CCSRC_BACKEND_KERNEL_COMPILER_CPU_SPARSE_SPARSE_MINIMUM_CPU_KERNEL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "plugin/device/cpu/kernel/cpu_kernel.h" +#include "plugin/factory/ms_factory.h" + +namespace mindspore { +namespace kernel { +class SparseSparseMinimumCpuKernelMod : public DeprecatedNativeCpuKernelMod { + public: + SparseSparseMinimumCpuKernelMod() = default; + ~SparseSparseMinimumCpuKernelMod() override = default; + void InitKernel(const CNodePtr &kernel_node) override; + bool Launch(const std::vector &inputs, const std::vector &workspace, + const std::vector &outputs) override; + + protected: + std::vector GetOpSupport() override; + + private: + template + void LaunchKernel(const std::vector &inputs, const std::vector &outputs); + void CheckParam(const CNodePtr &kernel_node); + TypeId dtype_{kTypeUnknown}; + int64_t x1_nnz_; + int64_t x2_nnz_; + int64_t num_dims_; + int64_t y_nnz_; + CNodeWeakPtr node_wpt_; +}; +} // namespace kernel +} // namespace mindspore +#endif // MINDSPORE_CCSRC_BACKEND_KERNEL_COMPILER_CPU_SPARSE_SPARSE_MINIMUM_CPU_KERNEL_H_ diff --git a/mindspore/core/ops/core_ops.h b/mindspore/core/ops/core_ops.h index 98cf23bf068..15f7c661c4d 100644 --- a/mindspore/core/ops/core_ops.h +++ b/mindspore/core/ops/core_ops.h @@ -197,6 +197,7 @@ constexpr auto kGatherDGradV2 = "GatherDGradV2"; constexpr auto kSparseTensorToCSRSparseMatrix = "SparseTensorToCSRSparseMatrix"; constexpr auto kSparseSplit = "SparseSplit"; constexpr auto kReverseV2 = "ReverseV2"; +constexpr auto kSparseSparseMinimum = "SparseSparseMinimum"; // NN constexpr auto kApplyAddSign = "ApplyAddSign"; @@ -572,6 +573,7 @@ GVAR_DEF(PrimitivePtr, kPrimSegmentSum, std::make_shared(kSegmentSum) GVAR_DEF(PrimitivePtr, kPrimAffineGrid, std::make_shared(kAffineGrid)); GVAR_DEF(PrimitivePtr, kPrimSegmentMean, std::make_shared(kSegmentMean)); GVAR_DEF(PrimitivePtr, kPrimSegmentProd, std::make_shared(kSegmentProd)); +GVAR_DEF(PrimitivePtr, kPrimSparseSparseMinimum, std::make_shared(kSparseSparseMinimum)); // image GVAR_DEF(PrimitivePtr, kPrimCropAndResizeGradBoxes, std::make_shared(kCropAndResizeGradBoxes)); diff --git a/mindspore/core/ops/sparse_sparse_minimum.cc b/mindspore/core/ops/sparse_sparse_minimum.cc new file mode 100644 index 00000000000..04f985344e5 --- /dev/null +++ b/mindspore/core/ops/sparse_sparse_minimum.cc @@ -0,0 +1,142 @@ +/** + * Copyright 2022 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 +#include +#include +#include +#include + +#include "ops/sparse_sparse_minimum.h" +#include "abstract/dshape.h" +#include "abstract/ops/primitive_infer_map.h" +#include "mindapi/src/helper.h" +#include "ops/op_utils.h" +#include "utils/check_convert_utils.h" +#include "utils/tensor_construct_utils.h" + +namespace mindspore { +namespace ops { +namespace { +const size_t kone = 1; +const size_t ktwo = 2; + +TuplePtr SparseSparseMinimumInferType(const PrimitivePtr &prim, const std::vector &input_args) { + MS_EXCEPTION_IF_NULL(prim); + auto x1_indices_type = input_args[kInputIndex0]->BuildType(); + auto x1_values_type = input_args[kInputIndex1]->BuildType(); + auto x1_shape_type = input_args[kInputIndex2]->BuildType(); + auto x2_indices_type = input_args[kInputIndex3]->BuildType(); + auto x2_values_type = input_args[kInputIndex4]->BuildType(); + auto x2_shape_type = input_args[kInputIndex5]->BuildType(); + const std::set common_valid_types = {kFloat32, kFloat16, kInt8, kInt16, kUInt16, + kUInt8, kInt32, kInt64, kFloat64}; + std::map types; + (void)types.emplace("x1_values", x1_values_type); + (void)types.emplace("x2_values", x2_values_type); + (void)CheckAndConvertUtils::CheckTensorTypeValid("x1_indices", x1_indices_type, {kInt64}, prim->name()); + (void)CheckAndConvertUtils::CheckTensorTypeValid("x1_shape", x1_shape_type, {kInt64}, prim->name()); + (void)CheckAndConvertUtils::CheckTensorTypeValid("x2_indices", x2_indices_type, {kInt64}, prim->name()); + (void)CheckAndConvertUtils::CheckTensorTypeValid("x2_shape", x2_shape_type, {kInt64}, prim->name()); + (void)CheckAndConvertUtils::CheckTensorTypeSame(types, common_valid_types, prim->name()); + std::vector types_list = {input_args[kInputIndex0]->BuildType(), input_args[kInputIndex1]->BuildType()}; + return std::make_shared(types_list); +} + +abstract::TupleShapePtr SparseSparseMinimumInferShape(const PrimitivePtr &primitive, + const std::vector &input_args) { + MS_EXCEPTION_IF_NULL(primitive); + auto prim_name = primitive->name(); + auto x1_indices_shape = + CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex0]->BuildShape())[kShape]; + auto x1_values_shape = + CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex1]->BuildShape())[kShape]; + auto x1_shape_shape = CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex2]->BuildShape())[kShape]; + auto x2_indices_shape = + CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex3]->BuildShape())[kShape]; + auto x2_values_shape = + CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex4]->BuildShape())[kShape]; + auto x2_shape_shape = CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex5]->BuildShape())[kShape]; + if (x1_indices_shape.size() != ktwo || x1_values_shape.size() != kone || x1_shape_shape.size() != kone) { + MS_EXCEPTION(ValueError) << "For SparseSparseMinimum, input x1_indices should be a 2-D tensor" + << ", but got " << x1_indices_shape.size() << "-D" + << ", input x1_values should be a 1-D tensor" + << ", but got " << x1_values_shape.size() << "-D" + << ", input x1_shape should be a 1-D tensor" + << ", but got " << x1_shape_shape.size() << "-D"; + } + if (x1_indices_shape[0] != x1_values_shape[0]) { + MS_EXCEPTION(ValueError) << "For " << prim_name << ", x1_indices.shape[0] and x1_values.shape[0] should be the same" + << ", but got x1_indices.shape[0] = " << x1_indices_shape[0] + << ", x1_values.shape[0] = " << x1_values_shape[0]; + } + if (x1_indices_shape[1] != x1_shape_shape[0]) { + MS_EXCEPTION(ValueError) << "For " << prim_name << ", x1_indices.shape[1] and x1_shape.shape[0] should be the same" + << ", but got x1_indices.shape[1] = " << x1_indices_shape[1] + << ", x1_shape.shape[0] = " << x1_shape_shape[0]; + } + if (x2_indices_shape.size() != ktwo || x2_values_shape.size() != kone || x2_shape_shape.size() != kone) { + MS_EXCEPTION(ValueError) << "For SparseSparseMinimum, input x2_indices should be a 2-D tensor" + << ", but got " << x2_indices_shape.size() << "-D" + << ", input x2_values should be a 1-D tensor" + << ", but got " << x2_values_shape.size() << "-D" + << ", input x2_shape should be a 1-D tensor" + << ", but got " << x2_shape_shape.size() << "-D"; + } + if (x2_indices_shape[0] != x2_values_shape[0]) { + MS_EXCEPTION(ValueError) << "For " << prim_name << ", x2_indices.shape[0] and x2_values.shape[0] should be the same" + << ", but got x2_indices.shape[0] = " << x2_indices_shape[0] + << ", x2_values.shape[0] = " << x2_values_shape[0]; + } + if (x2_indices_shape[1] != x2_shape_shape[0]) { + MS_EXCEPTION(ValueError) << "For " << prim_name << ", x2_indices.shape[1] and x2_shape.shape[0] should be the same" + << ", but got x2_indices.shape[1] = " << x2_indices_shape[1] + << ", x2_shape.shape[0] = " << x2_shape_shape[0]; + } + if (x1_shape_shape[0] != x2_shape_shape[0]) { + MS_EXCEPTION(ValueError) << "For " << prim_name << ", x1_shape.shape[0] and x2_shape.shape[0] should be the same" + << ", but got x1_shape.shape[0] = " << x1_shape_shape[0] + << ", x2_shape.shape[0] = " << x2_shape_shape[0]; + } + ShapeVector y_indices_shape = {-1, x1_shape_shape[0]}; + ShapeVector y_indices_min_shape = {0, x1_shape_shape[0]}; + ShapeVector y_indices_max_shape = {x1_indices_shape[0] + x2_indices_shape[0], x1_shape_shape[0]}; + ShapeVector y_values_shape = {-1}; + ShapeVector y_values_min_shape = {0}; + ShapeVector y_values_max_shape = {x1_indices_shape[0] + x2_indices_shape[0]}; + abstract::ShapePtr y_indices_shape_list = + std::make_shared(y_indices_shape, y_indices_min_shape, y_indices_max_shape); + abstract::ShapePtr y_values_shape_list = + std::make_shared(y_values_shape, y_values_min_shape, y_values_max_shape); + return std::make_shared( + std::vector{y_indices_shape_list, y_values_shape_list}); +} +} // namespace + +MIND_API_OPERATOR_IMPL(SparseSparseMinimum, BaseOperator); +AbstractBasePtr SparseSparseMinimumInfer(const abstract::AnalysisEnginePtr &, const PrimitivePtr &primitive, + const std::vector &input_args) { + MS_EXCEPTION_IF_NULL(primitive); + const int64_t input_num = 6; + (void)CheckAndConvertUtils::CheckInputArgs(input_args, kEqual, input_num, primitive->name()); + auto infer_type = SparseSparseMinimumInferType(primitive, input_args); + auto infer_shape = SparseSparseMinimumInferShape(primitive, input_args); + return abstract::MakeAbstract(infer_shape, infer_type); +} +REGISTER_PRIMITIVE_EVAL_IMPL(SparseSparseMinimum, prim::kPrimSparseSparseMinimum, SparseSparseMinimumInfer, nullptr, + true); +} // namespace ops +} // namespace mindspore diff --git a/mindspore/core/ops/sparse_sparse_minimum.h b/mindspore/core/ops/sparse_sparse_minimum.h new file mode 100644 index 00000000000..e6e635849d8 --- /dev/null +++ b/mindspore/core/ops/sparse_sparse_minimum.h @@ -0,0 +1,42 @@ +/** + * Copyright 2022 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_CORE_OPS_SPARSE_SPARSE_MINIMUM_H_ +#define MINDSPORE_CORE_OPS_SPARSE_SPARSE_MINIMUM_H_ +#include +#include + +#include "ops/base_operator.h" +#include "mindapi/base/types.h" + +namespace mindspore { +namespace ops { +constexpr auto kNameSparseSparseMinimum = "SparseSparseMinimum"; +class MIND_API SparseSparseMinimum : public BaseOperator { + public: + MIND_API_BASE_MEMBER(SparseSparseMinimum); + SparseSparseMinimum() : BaseOperator(kNameSparseSparseMinimum) { + InitIOName({"x1_indices", "x1_values", "x1_shape", "x2_indices", "x2_values", "x2_shape"}, + {"y_indices", "y_values"}); + } +}; +abstract::AbstractBasePtr SparseSparseMinimumInfer(const abstract::AnalysisEnginePtr &, const PrimitivePtr &primitive, + const std::vector &input_args); +using PrimSparseSparseMinimumPtr = std::shared_ptr; +} // namespace ops +} // namespace mindspore + +#endif // MINDSPORE_CORE_OPS_SPARSE_SPARSE_MINIMUM_H_ diff --git a/mindspore/python/mindspore/ops/_op_impl/aicpu/__init__.py b/mindspore/python/mindspore/ops/_op_impl/aicpu/__init__.py index 9e5fcbdd653..304326b7deb 100644 --- a/mindspore/python/mindspore/ops/_op_impl/aicpu/__init__.py +++ b/mindspore/python/mindspore/ops/_op_impl/aicpu/__init__.py @@ -285,3 +285,4 @@ from .sparse_apply_proximal_gradient_descent import _sparse_apply_proximal_gradi from .sparse_apply_momentum import _sparse_apply_momentum_aicpu from .linear_sum_assignment import _linear_sum_assignment_aicpu from .orgqr import _orgqr_aicpu +from .sparse_sparse_minimum import _sparse_sparse_minimum_aicpu diff --git a/mindspore/python/mindspore/ops/_op_impl/aicpu/sparse_sparse_minimum.py b/mindspore/python/mindspore/ops/_op_impl/aicpu/sparse_sparse_minimum.py new file mode 100644 index 00000000000..156338c760f --- /dev/null +++ b/mindspore/python/mindspore/ops/_op_impl/aicpu/sparse_sparse_minimum.py @@ -0,0 +1,53 @@ +# Copyright 2022 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. +# ============================================================================ + +"""SparseSparseMinimum op""" +from mindspore.ops.op_info_register import op_info_register, AiCPURegOp, DataType + +sparse_sparse_minimum_op_info = AiCPURegOp("SparseSparseMinimum") \ + .fusion_type("OPAQUE") \ + .input(0, "x1_indices", "required") \ + .input(1, "x1_values", "required") \ + .input(2, "x1_shape", "required") \ + .input(3, "x2_indices", "required") \ + .input(4, "x2_values", "required") \ + .input(5, "x2_shape", "required") \ + .output(0, "y_indices", "required") \ + .output(1, "y_values", "required") \ + .dtype_format(DataType.I64_Default, DataType.F32_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.F32_Default, DataType.I64_Default, DataType.I64_Default, DataType.F32_Default) \ + .dtype_format(DataType.I64_Default, DataType.F16_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.F16_Default, DataType.I64_Default, DataType.I64_Default, DataType.F16_Default) \ + .dtype_format(DataType.I64_Default, DataType.I8_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.I8_Default, DataType.I64_Default, DataType.I64_Default, DataType.I8_Default) \ + .dtype_format(DataType.I64_Default, DataType.I16_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.I16_Default, DataType.I64_Default, DataType.I64_Default, DataType.I16_Default) \ + .dtype_format(DataType.I64_Default, DataType.U16_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.U16_Default, DataType.I64_Default, DataType.I64_Default, DataType.U16_Default) \ + .dtype_format(DataType.I64_Default, DataType.U8_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.U8_Default, DataType.I64_Default, DataType.I64_Default, DataType.U8_Default) \ + .dtype_format(DataType.I64_Default, DataType.I32_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.I32_Default, DataType.I64_Default, DataType.I64_Default, DataType.I32_Default) \ + .dtype_format(DataType.I64_Default, DataType.I64_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.I64_Default, DataType.I64_Default, DataType.I64_Default, DataType.I64_Default) \ + .dtype_format(DataType.I64_Default, DataType.F64_Default, DataType.I64_Default, DataType.I64_Default, \ + DataType.F64_Default, DataType.I64_Default, DataType.I64_Default, DataType.F64_Default) \ + .get_op_info() + + +@op_info_register(sparse_sparse_minimum_op_info) +def _sparse_sparse_minimum_aicpu(): + """SparseSparseMinimum AiCPU register""" + return diff --git a/mindspore/python/mindspore/ops/operations/sparse_ops.py b/mindspore/python/mindspore/ops/operations/sparse_ops.py index 088d1595217..fe2bff1cd21 100644 --- a/mindspore/python/mindspore/ops/operations/sparse_ops.py +++ b/mindspore/python/mindspore/ops/operations/sparse_ops.py @@ -907,6 +907,74 @@ class SparseMatrixTranspose(Primitive): 'y_col_indices', 'y_values']) +class SparseSparseMinimum(Primitive): + r""" + Returns the element-wise min of two SparseTensors. + + Inputs: + - **x1_indices** (Tensor) - A 2-D Tensor. It represents the position of the non-zero element + in the first sparse tensor. + - **x1_values** (Tensor) - A 1-D Tensor. It represents the value corresponding to the position + in the `x1_indices`, the shape of which should be :math:`(N,)`. + - **x1_shape** (Tensor) - A 1-D Tensor. It represents the shape of the input sparse tensor, + the shape of which should be :math:`(N,)`. + - **x2_indices** (Tensor) - A 2-D Tensor. It represents the position of the non-zero element + in the second sparse tensor. + - **x2_values** (Tensor) - A 1-D Tensor. It represents the value corresponding to the position + in the `x2_indices`, the shape of which should be :math:`(N,)`. + - **x2_shape** (Tensor) - A 1-D Tensor. It represents the shape of the input sparse tensor, + the shape of which should be :math:`(N,)`. + + Outputs: + - **y_indices** (Tensor) - A 2-D Tensor. It represents the position of the element-wise min of + two input tensors. + - **y_values** (Tensor) - A 1-D Tensor. It represents the value corresponding to the position + in the `y_indices`. + + Raises: + TypeError: The dtype of `x1_indices`, `x1_shape`, `x2_indices` or `x2_shape` is wrong. + TypeError: The dtype of `x1_values` or `x2_values` is wrong. + TypeError: If `x1_indices`, `x1_values`, `x1_shape`, `x2_indices`, `x2_values`, `x2_shape` + is not a tensor. + TypeError: If `x1_indices` is not a 2-D tensor. + TypeError: If `x2_indices` is not a 2-D tensor. + ValueError: If any of `x1_values` and `x1_shape` is not a 1-D tensor. + ValueError: If shape[0] of `x1_indices` is not corresponding to shape[0] of `x1_values`. + ValueError: If shape[1] of `x1_indices` is not corresponding to shape[0] of `x1_shape`. + ValueError: If any of `x2_values` and `x2_shape` is not a 1-D tensor. + ValueError: If shape[0] of `x2_indices` is not corresponding to shape[0] of `x2_values`. + ValueError: If shape[1] of `x2_indices` is not corresponding to shape[0] of `x2_shape`. + ValueError: If shape[0] of `x1_shape` is not corresponding to shape[0] of `x2_shape`. + + Supported Platforms: + ``Ascend`` ``CPU`` + + Examples: + >>> from mindspore.ops.operations.sparse_ops import SparseSparseMinimum + >>> x1_indices = Tensor(np.array([[0, 0, 0], [0, 1, 0], [0, 1, 1]]).astype(np.int64)) + >>> x1_values = Tensor([1, 2, 3], dtype=mstype.float32) + >>> x1_shape = Tensor(np.array([2, 2, 2]).astype(np.int64)) + >>> x2_indices = Tensor(np.array([[0, 0, 0], [0, 1, 0], [1, 0, 0]]).astype(np.int64)) + >>> x2_values = Tensor([2, 4, 5], dtype=mstype.float32) + >>> x2_shape = Tensor(np.array([2, 2, 2]).astype(np.int64)) + >>> sparse_sparse_minimum = ops.SparseSparseMinimum() + >>> out = sparse_sparse_minimum(x1_indices, x1_values, x1_shape, x2_indices, x2_values, x2_shape) + >>> print(out[0]) + [[0 0 0] + [0 1 0] + [0 1 1] + [1 0 0]] + >>> print(out[1]) + [1. 2. 0. 0.] + """ + + @prim_attr_register + def __init__(self): + """Initialize SparseSparseMinimum.""" + self.init_prim_io_names(inputs=['x1_indices', 'x1_values', 'x1_shape', 'x2_indices', 'x2_values', 'x2_shape'], + outputs=['y_indices', 'y_values']) + + class SparseTensorToCSRSparseMatrix(Primitive): """ Converts a sparse tensor to its CSR sparse matrix(maybe batched) form. diff --git a/tests/ut/python/ops/test_ops.py b/tests/ut/python/ops/test_ops.py index 1085acad4ca..c7e237cd38d 100755 --- a/tests/ut/python/ops/test_ops.py +++ b/tests/ut/python/ops/test_ops.py @@ -115,6 +115,7 @@ from mindspore.ops.operations.sparse_ops import SparseTensorDenseAdd from mindspore.ops.operations.sparse_ops import SparseMatrixTranspose from mindspore.ops.operations.sparse_ops import CSRSparseMatrixToSparseTensor from mindspore.ops.operations.sparse_ops import SparseTensorToCSRSparseMatrix +from mindspore.ops.operations.sparse_ops import SparseSparseMinimum from mindspore.ops.operations.other_ops import BlackmanWindow from mindspore.ops.operations.nn_ops import SparseApplyCenteredRMSProp from mindspore.ops.operations.nn_ops import SparseApplyProximalGradientDescent @@ -2693,6 +2694,15 @@ test_case_nn_ops = [ Tensor(np.array([0, 1, 3, 0, 1, 3]).astype(np.int64)), Tensor(np.array([1, 2, 3, 1, 2, 3]).astype(np.int64)), Tensor(np.array([1, 4, 3, 1, 4, 3]).astype(np.float32))]}), + ('SparseSparseMinimum', { + 'block': SparseSparseMinimum(), + 'desc_inputs': [Tensor(np.array([[0, 0, 0], [0, 1, 0], [0, 1, 1]]).astype(np.int64)), + Tensor(np.array([1, 2, 3]).astype(np.float32)), + Tensor(np.array([2, 2, 2]).astype(np.int64)), + Tensor(np.array([[0, 0, 0], [0, 1, 0], [1, 0, 0]]).astype(np.int64)), + Tensor(np.array([2, 4, 5]).astype(np.float32)), + Tensor(np.array([2, 2, 2]).astype(np.int64))], + 'skip': ['backward']}), ('SparseApplyAdagrad', { 'block': SparseApplyAdagradNet(), 'desc_inputs': [[3, 3], Tensor(np.ones((3,), np.int32))],