From 9c21f78f160e0437044345b26d0ca5d2d328ee55 Mon Sep 17 00:00:00 2001 From: yanglf1121 Date: Tue, 19 Jan 2021 13:23:05 +0800 Subject: [PATCH] Add cpu op LogicalAnd, LogicalOr, LogicalNot --- .../cpu/arithmetic_cpu_kernel.cc | 26 ++++++ .../cpu/arithmetic_cpu_kernel.h | 10 +++ .../cpu/arithmetic_self_cpu_kernel.cc | 52 ++++++++++++ .../cpu/arithmetic_self_cpu_kernel.h | 6 ++ .../backend/kernel_compiler/cpu/cpu_kernel.h | 3 + mindspore/core/base/core_ops.h | 3 + mindspore/ops/operations/math_ops.py | 8 +- tests/st/ops/cpu/test_logical_op.py | 80 +++++++++++++++++++ 8 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 tests/st/ops/cpu/test_logical_op.py diff --git a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.cc b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.cc index 3f9f582340..6b8397e032 100644 --- a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.cc +++ b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.cc @@ -180,6 +180,24 @@ void ArithmeticCPUKernel::NotEqual(const T *input1, const T *input2, bool *out, } } +template +void ArithmeticCPUKernel::LogicalAnd(const T *input1, const T *input2, bool *out, size_t start, size_t end) { + for (size_t i = start; i < end; i++) { + std::vector idx; + GenIndex(i, &idx); + out[i] = input1[idx[0]] && input2[idx[1]]; + } +} + +template +void ArithmeticCPUKernel::LogicalOr(const T *input1, const T *input2, bool *out, size_t start, size_t end) { + for (size_t i = start; i < end; i++) { + std::vector idx; + GenIndex(i, &idx); + out[i] = input1[idx[0]] || input2[idx[1]]; + } +} + template void ArithmeticCPUKernel::SquaredDifference(const T *input1, const T *input2, T *out, size_t start, size_t end) { for (size_t i = start; i < end; i++) { @@ -248,6 +266,10 @@ void ArithmeticCPUKernel::InitKernel(const CNodePtr &kernel_node) { operate_type_ = GREATEREQUAL; } else if (kernel_name == prim::kPrimLessEqual->name()) { operate_type_ = LESSEQUAL; + } else if (kernel_name == prim::kPrimLogicalAnd->name()) { + operate_type_ = LOGICALAND; + } else if (kernel_name == prim::kPrimLogicalOr->name()) { + operate_type_ = LOGICALOR; } else if (kernel_name == prim::kPrimAssignAdd->name()) { operate_type_ = ASSIGNADD; } else if (kernel_name == prim::kPrimSquaredDifference->name()) { @@ -366,6 +388,10 @@ void ArithmeticCPUKernel::LaunchKernelLogic(const std::vector &input std::thread(&ArithmeticCPUKernel::GreaterEqual, this, input1, input2, output, start, end)); } else if (operate_type_ == LESSEQUAL) { threads.emplace_back(std::thread(&ArithmeticCPUKernel::LessEqual, this, input1, input2, output, start, end)); + } else if (operate_type_ == LOGICALAND) { + threads.emplace_back(std::thread(&ArithmeticCPUKernel::LogicalAnd, this, input1, input2, output, start, end)); + } else if (operate_type_ == LOGICALOR) { + threads.emplace_back(std::thread(&ArithmeticCPUKernel::LogicalOr, this, input1, input2, output, start, end)); } else { MS_LOG(EXCEPTION) << "Not support " << operate_type_; } diff --git a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.h b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.h index ee8ba75fec..2fc2923dfa 100644 --- a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.h +++ b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_cpu_kernel.h @@ -71,6 +71,10 @@ class ArithmeticCPUKernel : public CPUKernel { void GreaterEqual(const T *input1, const T *input2, bool *out, size_t start, size_t end); template void LessEqual(const T *input1, const T *input2, bool *out, size_t start, size_t end); + template + void LogicalAnd(const T *input1, const T *input2, bool *out, size_t start, size_t end); + template + void LogicalOr(const T *input1, const T *input2, bool *out, size_t start, size_t end); std::vector input_shape0_; std::vector input_shape1_; std::vector input_element_num0_; @@ -269,6 +273,12 @@ MS_REG_CPU_KERNEL( LessEqual, KernelAttr().AddInputAttr(kNumberTypeFloat32).AddInputAttr(kNumberTypeFloat32).AddOutputAttr(kNumberTypeBool), ArithmeticCPUKernel); +MS_REG_CPU_KERNEL( + LogicalAnd, KernelAttr().AddInputAttr(kNumberTypeBool).AddInputAttr(kNumberTypeBool).AddOutputAttr(kNumberTypeBool), + ArithmeticCPUKernel); +MS_REG_CPU_KERNEL( + LogicalOr, KernelAttr().AddInputAttr(kNumberTypeBool).AddInputAttr(kNumberTypeBool).AddOutputAttr(kNumberTypeBool), + ArithmeticCPUKernel); } // namespace kernel } // namespace mindspore diff --git a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.cc b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.cc index f7b4ad7bb6..6d52662470 100644 --- a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.cc +++ b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.cc @@ -49,6 +49,13 @@ void Neg(const T *in, T *out, size_t start, size_t end) { } } +template +void LogicalNot(const T *in, T *out, size_t start, size_t end) { + for (size_t i = start; i < end; i++) { + out[i] = !in[i]; + } +} + template void OnesLike(const T *in, T *out, size_t start, size_t end) { for (size_t i = start; i < end; i++) { @@ -99,6 +106,8 @@ void ArithmeticSelfCPUKernel::InitKernel(const CNodePtr &kernel_node) { operate_type_ = ZEROSLIKE; } else if (kernel_name == prim::kPrimNeg->name()) { operate_type_ = NEG; + } else if (kernel_name == prim::kPrimLogicalNot->name()) { + operate_type_ = LOGICALNOT; } else if (kernel_name == prim::kPrimSign->name()) { operate_type_ = SIGN; } else if (kernel_name == prim::kPrimFloor->name()) { @@ -109,6 +118,7 @@ void ArithmeticSelfCPUKernel::InitKernel(const CNodePtr &kernel_node) { operate_type_ = GELU; } dtype_ = AnfAlgo::GetPrevNodeOutputInferDataType(kernel_node, 0); + target_dtype_ = AnfAlgo::GetOutputInferDataType(kernel_node, 0); } bool ArithmeticSelfCPUKernel::Launch(const std::vector &inputs, @@ -118,15 +128,55 @@ bool ArithmeticSelfCPUKernel::Launch(const std::vector &inpu LaunchKernel(inputs, outputs); } else if (dtype_ == kNumberTypeInt32 || dtype_ == kNumberTypeInt16 || dtype_ == kNumberTypeInt64) { LaunchKernel(inputs, outputs); + } else if (dtype_ == kNumberTypeBool) { + LaunchKernelLogic(inputs, outputs); } else { MS_LOG(EXCEPTION) << "Data type is " << TypeIdLabel(dtype_) << "is not support."; } return true; } +template +void ArithmeticSelfCPUKernel::LaunchKernelLogic(const std::vector &inputs, + const std::vector &outputs) { + T *input = reinterpret_cast(inputs[0]->addr); + T *output = reinterpret_cast(outputs[0]->addr); + size_t lens = outputs[0]->size > 0 ? static_cast(outputs[0]->size / sizeof(T)) : 1; + + auto max_thread_num = std::thread::hardware_concurrency(); + size_t thread_num = lens < 128 * max_thread_num ? std::ceil(lens / 128.0) : max_thread_num; + MS_LOG(INFO) << "Lens=" << lens << "; use thread_num=" << thread_num << "; max_thread_num: " << max_thread_num; + std::vector threads; + if (thread_num < 1) { + MS_LOG(ERROR) << "Invalid value: thread_num " << thread_num; + return; + } + threads.reserve(thread_num); + size_t start = 0; + size_t once_compute_size = (lens + thread_num - 1) / thread_num; + if (once_compute_size < 1) { + MS_LOG(ERROR) << "Invalid value: once_compute_size " << once_compute_size; + return; + } + while (start < lens) { + size_t end = (start + once_compute_size) > lens ? lens : (start + once_compute_size); + if (operate_type_ == LOGICALNOT) { + threads.emplace_back(std::thread(LogicalNot, input, output, start, end)); + } + start += once_compute_size; + } + for (size_t i = 0; i < threads.size(); ++i) { + threads[i].join(); + } +} + template void ArithmeticSelfCPUKernel::LaunchKernel(const std::vector &inputs, const std::vector &outputs) { + if (target_dtype_ == kNumberTypeBool) { + LaunchKernelLogic(inputs, outputs); + return; + } T *input = reinterpret_cast(inputs[0]->addr); T *output = reinterpret_cast(outputs[0]->addr); size_t lens = outputs[0]->size > 0 ? static_cast(outputs[0]->size / sizeof(T)) : 1; @@ -152,6 +202,8 @@ void ArithmeticSelfCPUKernel::LaunchKernel(const std::vector &inputs threads.emplace_back(std::thread(Square, input, output, start, end)); } else if (operate_type_ == NEG) { threads.emplace_back(std::thread(Neg, input, output, start, end)); + } else if (operate_type_ == LOGICALNOT) { + threads.emplace_back(std::thread(LogicalNot, input, output, start, end)); } else if (operate_type_ == ONESLIKE) { threads.emplace_back(std::thread(OnesLike, input, output, start, end)); } else if (operate_type_ == ZEROSLIKE) { diff --git a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.h b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.h index db7c99c90b..6a28da1033 100644 --- a/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.h +++ b/mindspore/ccsrc/backend/kernel_compiler/cpu/arithmetic_self_cpu_kernel.h @@ -35,9 +35,13 @@ class ArithmeticSelfCPUKernel : public CPUKernel { template void LaunchKernel(const std::vector &inputs, const std::vector &outputs); + template + void LaunchKernelLogic(const std::vector &inputs, const std::vector &outputs); + private: OperateType operate_type_{SQUARE}; TypeId dtype_{kTypeUnknown}; + TypeId target_dtype_{kTypeUnknown}; }; MS_REG_CPU_KERNEL(Square, KernelAttr().AddInputAttr(kNumberTypeInt32).AddOutputAttr(kNumberTypeInt32), @@ -64,6 +68,8 @@ MS_REG_CPU_KERNEL(Reciprocal, KernelAttr().AddInputAttr(kNumberTypeFloat32).AddO ArithmeticSelfCPUKernel); MS_REG_CPU_KERNEL(Gelu, KernelAttr().AddInputAttr(kNumberTypeFloat32).AddOutputAttr(kNumberTypeFloat32), ArithmeticSelfCPUKernel); +MS_REG_CPU_KERNEL(LogicalNot, KernelAttr().AddInputAttr(kNumberTypeBool).AddOutputAttr(kNumberTypeBool), + ArithmeticSelfCPUKernel); } // namespace kernel } // namespace mindspore diff --git a/mindspore/ccsrc/backend/kernel_compiler/cpu/cpu_kernel.h b/mindspore/ccsrc/backend/kernel_compiler/cpu/cpu_kernel.h index 5459b93aff..0c50d57f6e 100644 --- a/mindspore/ccsrc/backend/kernel_compiler/cpu/cpu_kernel.h +++ b/mindspore/ccsrc/backend/kernel_compiler/cpu/cpu_kernel.h @@ -84,6 +84,9 @@ enum OperateType { EQUAL, NOTEQUAL, LESSEQUAL, + LOGICALAND, + LOGICALOR, + LOGICALNOT, FLOOR, SQUAREDDIFFERENCE, GREATER, diff --git a/mindspore/core/base/core_ops.h b/mindspore/core/base/core_ops.h index fad5653d36..25436e00a3 100644 --- a/mindspore/core/base/core_ops.h +++ b/mindspore/core/base/core_ops.h @@ -61,6 +61,9 @@ inline const PrimitivePtr kPrimLess = std::make_shared("Less"); inline const PrimitivePtr kPrimLessEqual = std::make_shared("LessEqual"); inline const PrimitivePtr kPrimEqual = std::make_shared("Equal"); inline const PrimitivePtr kPrimNotEqual = std::make_shared("NotEqual"); +inline const PrimitivePtr kPrimLogicalAnd = std::make_shared("LogicalAnd"); +inline const PrimitivePtr kPrimLogicalOr = std::make_shared("LogicalOr"); +inline const PrimitivePtr kPrimLogicalNot = std::make_shared("LogicalNot"); inline const PrimitivePtr kPrimDistribute = std::make_shared("distribute"); inline const PrimitivePtr kPrimDot = std::make_shared("dot"); diff --git a/mindspore/ops/operations/math_ops.py b/mindspore/ops/operations/math_ops.py index 62962c716d..7ccddabbf6 100644 --- a/mindspore/ops/operations/math_ops.py +++ b/mindspore/ops/operations/math_ops.py @@ -2057,7 +2057,7 @@ class FloorDiv(_MathBinaryOp): and the data type is the one with higher precision or higher digits among the two inputs. Supported Platforms: - ``Ascend`` ``GPU`` + ``Ascend`` ``GPU`` ``CPU`` Examples: >>> input_x = Tensor(np.array([2, 4, -1]), mindspore.int32) @@ -2870,7 +2870,7 @@ class LogicalNot(PrimitiveWithInfer): Tensor, the shape is the same as the `input_x`, and the dtype is bool. Supported Platforms: - ``Ascend`` ``GPU`` + ``Ascend`` ``GPU`` ``CPU`` Examples: >>> input_x = Tensor(np.array([True, False, True]), mindspore.bool_) @@ -2913,7 +2913,7 @@ class LogicalAnd(_LogicBinaryOp): Tensor, the shape is the same as the one after broadcasting, and the data type is bool. Supported Platforms: - ``Ascend`` ``GPU`` + ``Ascend`` ``GPU`` ``CPU`` Examples: >>> input_x = Tensor(np.array([True, False, True]), mindspore.bool_) @@ -2948,7 +2948,7 @@ class LogicalOr(_LogicBinaryOp): Tensor, the shape is the same as the one after broadcasting,and the data type is bool. Supported Platforms: - ``Ascend`` ``GPU`` + ``Ascend`` ``GPU`` ``CPU`` Examples: >>> input_x = Tensor(np.array([True, False, True]), mindspore.bool_) diff --git a/tests/st/ops/cpu/test_logical_op.py b/tests/st/ops/cpu/test_logical_op.py new file mode 100644 index 0000000000..7b30da049a --- /dev/null +++ b/tests/st/ops/cpu/test_logical_op.py @@ -0,0 +1,80 @@ +# Copyright 2020-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 +import mindspore.nn as nn +from mindspore import Tensor +from mindspore.ops import operations as P + +context.set_context(mode=context.GRAPH_MODE, device_target='CPU') + + +class OpNetWrapper(nn.Cell): + def __init__(self, op): + super(OpNetWrapper, self).__init__() + self.op = op + + def construct(self, *inputs): + return self.op(*inputs) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu +@pytest.mark.env_onecard +def test_logicaland(): + op = P.LogicalAnd() + op_wrapper = OpNetWrapper(op) + + input_x = Tensor(np.array([True, False, False])) + input_y = Tensor(np.array([True, True, False])) + outputs = op_wrapper(input_x, input_y) + + assert np.allclose(outputs.asnumpy(), (True, False, False)) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu +@pytest.mark.env_onecard +def test_logicalor(): + op = P.LogicalOr() + op_wrapper = OpNetWrapper(op) + + input_x = Tensor(np.array([True, False, False])) + input_y = Tensor(np.array([True, True, False])) + outputs = op_wrapper(input_x, input_y) + + assert np.allclose(outputs.asnumpy(), (True, True, False)) + + +@pytest.mark.level0 +@pytest.mark.platform_x86_cpu +@pytest.mark.env_onecard +def test_logicalnot(): + op = P.LogicalNot() + op_wrapper = OpNetWrapper(op) + + input_x = Tensor(np.array([True, False, False])) + outputs = op_wrapper(input_x) + + assert np.allclose(outputs.asnumpy(), (False, True, True)) + + +if __name__ == '__main__': + test_logicaland() + test_logicalor() + test_logicalnot()