| @@ -0,0 +1,152 @@ | |||
| /** | |||
| * 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/is_close_cpu_kernel.h" | |||
| #include <cmath> | |||
| #include <algorithm> | |||
| #include "abstract/utils.h" | |||
| #include "plugin/device/cpu/hal/device/cpu_device_address.h" | |||
| namespace mindspore { | |||
| namespace kernel { | |||
| namespace { | |||
| constexpr size_t kIsCloseInputsNum = 2; | |||
| constexpr size_t kIsCloseOutputsNum = 1; | |||
| constexpr size_t kIsCloseInputIndex = 0; | |||
| constexpr size_t kIsCloseOtherIndex = 1; | |||
| constexpr size_t kIsCloseOutputIndex = 0; | |||
| constexpr char RTOL[] = "rtol"; | |||
| constexpr char ATOL[] = "atol"; | |||
| constexpr char EQUAL_NAN[] = "equal_nan"; | |||
| template <typename T> | |||
| inline bool compute(T a, T b, float rtol, float atol, bool equal_nan) { | |||
| if (a == b) { | |||
| return true; | |||
| } | |||
| if (equal_nan && std::isnan(a) && std::isnan(b)) { | |||
| return true; | |||
| } | |||
| if (atol == 0 && rtol == 0) { | |||
| return false; | |||
| } | |||
| auto left_side = std::abs(a - b); | |||
| auto right_side = atol + (rtol * std::abs(b)); | |||
| return std::isfinite(left_side) && left_side <= right_side; | |||
| } | |||
| } // namespace | |||
| void IsCloseCpuKernelMod::InitKernel(const CNodePtr &kernel_node) { | |||
| MS_EXCEPTION_IF_NULL(kernel_node); | |||
| kernel_name_ = common::AnfAlgo::GetCNodeName(kernel_node); | |||
| auto kernel_attr = GetKernelAttrFromNode(kernel_node); | |||
| auto [is_match, index] = MatchKernelAttr(kernel_attr, GetOpSupport()); | |||
| if (!is_match) { | |||
| MS_LOG(EXCEPTION) << "IsClose does not support this kernel data type: " << kernel_attr; | |||
| } | |||
| kernel_func_ = func_list_[index].second; | |||
| rtol_ = common::AnfAlgo::GetNodeAttr<float>(kernel_node, RTOL); | |||
| atol_ = common::AnfAlgo::GetNodeAttr<float>(kernel_node, ATOL); | |||
| equal_nan_ = common::AnfAlgo::GetNodeAttr<bool>(kernel_node, EQUAL_NAN); | |||
| input_shape_ = common::AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, kIsCloseInputIndex); | |||
| other_shape_ = common::AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, kIsCloseOtherIndex); | |||
| output_shape_ = common::AnfAlgo::GetOutputInferShape(kernel_node, kIsCloseOutputIndex); | |||
| } | |||
| template <typename T> | |||
| bool IsCloseCpuKernelMod::LaunchKernel(const std::vector<AddressPtr> &inputs, | |||
| const std::vector<kernel::AddressPtr> &outputs) { | |||
| CHECK_KERNEL_INPUTS_NUM(inputs.size(), kIsCloseInputsNum, kernel_name_); | |||
| CHECK_KERNEL_OUTPUTS_NUM(outputs.size(), kIsCloseOutputsNum, kernel_name_); | |||
| auto input = reinterpret_cast<T *>(inputs[kIsCloseInputIndex]->addr); | |||
| auto other = reinterpret_cast<T *>(inputs[kIsCloseOtherIndex]->addr); | |||
| auto output = reinterpret_cast<bool *>(outputs[kIsCloseOutputIndex]->addr); | |||
| CTask task; | |||
| BroadcastIterator base_iter(input_shape_, other_shape_, output_shape_); | |||
| if (input_shape_ == other_shape_) { | |||
| task = [this, &input, &other, &output](size_t start, size_t end) { | |||
| for (size_t i = start; i < end; i++) { | |||
| if constexpr (!std::is_same_v<T, float> && !std::is_same_v<T, double>) { | |||
| auto a = static_cast<float>(input[i]); | |||
| auto b = static_cast<float>(other[i]); | |||
| output[i] = compute<float>(a, b, rtol_, atol_, equal_nan_); | |||
| } else { | |||
| output[i] = compute<T>(input[i], other[i], rtol_, atol_, equal_nan_); | |||
| } | |||
| } | |||
| return common::SUCCESS; | |||
| }; | |||
| } else { | |||
| task = [this, &base_iter, &input, &other, &output](size_t start, size_t end) { | |||
| auto iter = base_iter; | |||
| iter.SetPos(start); | |||
| for (size_t i = start; i < end; i++) { | |||
| auto idx1 = iter.GetInputPosA(); | |||
| auto idx2 = iter.GetInputPosB(); | |||
| if constexpr (!std::is_same_v<T, float> && !std::is_same_v<T, double>) { | |||
| auto a = static_cast<float>(input[idx1]); | |||
| auto b = static_cast<float>(other[idx2]); | |||
| output[i] = compute<float>(a, b, rtol_, atol_, equal_nan_); | |||
| } else { | |||
| output[i] = compute<T>(input[idx1], other[idx2], rtol_, atol_, equal_nan_); | |||
| } | |||
| iter.GenNextPos(); | |||
| } | |||
| return common::SUCCESS; | |||
| }; | |||
| } | |||
| size_t elem_num = outputs[kIsCloseOutputIndex]->size / sizeof(bool); | |||
| ParallelLaunchAutoSearch(task, elem_num, this, ¶llel_search_info_); | |||
| return true; | |||
| } | |||
| std::vector<std::pair<KernelAttr, IsCloseCpuKernelMod::IsCloseFunc>> IsCloseCpuKernelMod::func_list_ = { | |||
| {KernelAttr().AddInputAttr(kNumberTypeBool).AddInputAttr(kNumberTypeBool).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<bool>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeInt8).AddInputAttr(kNumberTypeInt8).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<int8_t>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeInt16).AddInputAttr(kNumberTypeInt16).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<int16_t>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeInt32).AddInputAttr(kNumberTypeInt32).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<int32_t>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeInt64).AddInputAttr(kNumberTypeInt64).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<int64_t>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeFloat16).AddInputAttr(kNumberTypeFloat16).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<float16>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeFloat32).AddInputAttr(kNumberTypeFloat32).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<float>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeFloat64).AddInputAttr(kNumberTypeFloat64).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<double>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeUInt8).AddInputAttr(kNumberTypeUInt8).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<uint8_t>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeUInt16).AddInputAttr(kNumberTypeUInt16).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<uint16_t>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeUInt32).AddInputAttr(kNumberTypeUInt32).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<uint32_t>}, | |||
| {KernelAttr().AddInputAttr(kNumberTypeUInt64).AddInputAttr(kNumberTypeUInt64).AddOutputAttr(kNumberTypeBool), | |||
| &IsCloseCpuKernelMod::LaunchKernel<uint64_t>}}; | |||
| std::vector<KernelAttr> IsCloseCpuKernelMod::GetOpSupport() { | |||
| std::vector<KernelAttr> support_list; | |||
| (void)std::transform(func_list_.begin(), func_list_.end(), std::back_inserter(support_list), | |||
| [](const std::pair<KernelAttr, IsCloseFunc> &pair) { return pair.first; }); | |||
| return support_list; | |||
| } | |||
| MS_KERNEL_FACTORY_REG(NativeCpuKernelMod, IsClose, IsCloseCpuKernelMod); | |||
| } // namespace kernel | |||
| } // namespace mindspore | |||
| @@ -0,0 +1,61 @@ | |||
| /** | |||
| * 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_PLUGIN_DEVICE_CPU_KERNEL_IS_CLOSE_CPU_KERNEL_H_ | |||
| #define MINDSPORE_CCSRC_PLUGIN_DEVICE_CPU_KERNEL_IS_CLOSE_CPU_KERNEL_H_ | |||
| #include <vector> | |||
| #include <map> | |||
| #include <utility> | |||
| #include "plugin/device/cpu/kernel/cpu_kernel.h" | |||
| #include "plugin/factory/ms_factory.h" | |||
| namespace mindspore { | |||
| namespace kernel { | |||
| class IsCloseCpuKernelMod : public NativeCpuKernelMod { | |||
| public: | |||
| IsCloseCpuKernelMod() = default; | |||
| ~IsCloseCpuKernelMod() override = default; | |||
| void InitKernel(const CNodePtr &kernel_node) override; | |||
| bool Launch(const std::vector<AddressPtr> &inputs, const std::vector<AddressPtr> &, | |||
| const std::vector<AddressPtr> &outputs) override { | |||
| return kernel_func_(this, inputs, outputs); | |||
| } | |||
| protected: | |||
| std::vector<KernelAttr> GetOpSupport() override; | |||
| private: | |||
| template <typename T> | |||
| bool LaunchKernel(const std::vector<AddressPtr> &inputs, const std::vector<kernel::AddressPtr> &outputs); | |||
| using IsCloseFunc = std::function<bool(IsCloseCpuKernelMod *, const std::vector<kernel::AddressPtr> &, | |||
| const std::vector<kernel::AddressPtr> &)>; | |||
| static std::vector<std::pair<KernelAttr, IsCloseFunc>> func_list_; | |||
| IsCloseFunc kernel_func_; | |||
| float rtol_; | |||
| float atol_; | |||
| bool equal_nan_; | |||
| std::vector<size_t> input_shape_; | |||
| std::vector<size_t> other_shape_; | |||
| std::vector<size_t> output_shape_; | |||
| }; | |||
| } // namespace kernel | |||
| } // namespace mindspore | |||
| #endif // MINDSPORE_CCSRC_PLUGIN_DEVICE_CPU_KERNEL_IS_CLOSE_CPU_KERNEL_H_ | |||
| @@ -21,6 +21,7 @@ | |||
| #include <set> | |||
| #include <vector> | |||
| #include "ops/op_utils.h" | |||
| #include "utils/ms_context.h" | |||
| #include "utils/check_convert_utils.h" | |||
| #include "abstract/primitive_infer_map.h" | |||
| #include "mindapi/src/helper.h" | |||
| @@ -29,33 +30,38 @@ namespace mindspore { | |||
| namespace ops { | |||
| namespace { | |||
| abstract::ShapePtr IsCloseInferShape(const PrimitivePtr &primitive, const std::vector<AbstractBasePtr> &input_args) { | |||
| const int MAX = 0x3fffffff; | |||
| MS_EXCEPTION_IF_NULL(primitive); | |||
| auto op_name = primitive->name(); | |||
| auto input_shape = CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex0]->BuildShape())[kShape]; | |||
| auto other_shape = CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex1]->BuildShape())[kShape]; | |||
| auto input_rank = SizeToLong(input_shape.size()); | |||
| auto other_rank = SizeToLong(other_shape.size()); | |||
| CheckAndConvertUtils::Check("input rank", input_rank, kEqual, other_rank, op_name); | |||
| int64_t input_size = 1, other_size = 1; | |||
| for (size_t i = 0; i < input_shape.size(); i++) { | |||
| input_size *= input_shape[i]; | |||
| other_size *= other_shape[i]; | |||
| if (input_shape[i] != other_shape[i] && (input_shape[i] != 1 || other_shape[i] != 1)) { | |||
| MS_EXCEPTION(ValueError) << "For '" << op_name | |||
| << "', The size of tensor input must match the size of tensor other at the " << i | |||
| << " dimension, but got input size: " << input_shape[i] | |||
| << ", other size: " << other_shape[i] << "."; | |||
| auto context = MsContext::GetInstance(); | |||
| MS_EXCEPTION_IF_NULL(context); | |||
| bool is_ascend = (context->get_param<std::string>(MS_CTX_DEVICE_TARGET) == kAscendDevice); | |||
| if (is_ascend) { | |||
| const int MAX = 0x3fffffff; | |||
| auto input_shape = CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex0]->BuildShape())[kShape]; | |||
| auto other_shape = CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_args[kInputIndex1]->BuildShape())[kShape]; | |||
| auto input_rank = SizeToLong(input_shape.size()); | |||
| auto other_rank = SizeToLong(other_shape.size()); | |||
| CheckAndConvertUtils::Check("input rank", input_rank, kEqual, other_rank, op_name); | |||
| int64_t input_size = 1, other_size = 1; | |||
| for (size_t i = 0; i < input_shape.size(); i++) { | |||
| input_size *= input_shape[i]; | |||
| other_size *= other_shape[i]; | |||
| if (input_shape[i] != other_shape[i] && (input_shape[i] != 1 || other_shape[i] != 1)) { | |||
| MS_EXCEPTION(ValueError) << "For '" << op_name | |||
| << "', The size of tensor input must match the size of tensor other at the " << i | |||
| << " dimension, but got input size: " << input_shape[i] | |||
| << ", other size: " << other_shape[i] << "."; | |||
| } | |||
| } | |||
| if (input_size > MAX) | |||
| MS_EXCEPTION(ValueError) << "For '" << op_name | |||
| << "', The size of tensor input must should be less than [2147483648], actual is " | |||
| << input_size; | |||
| if (other_size > MAX) | |||
| MS_EXCEPTION(ValueError) << "For '" << op_name | |||
| << "', The size of tensor other must should be less than [2147483648], actual is " | |||
| << other_size; | |||
| } | |||
| if (input_size > MAX) | |||
| MS_EXCEPTION(ValueError) << "For '" << op_name | |||
| << "', The size of tensor input must should be less than [2147483648], actual is " | |||
| << input_size; | |||
| if (other_size > MAX) | |||
| MS_EXCEPTION(ValueError) << "For '" << op_name | |||
| << "', The size of tensor other must should be less than [2147483648], actual is " | |||
| << other_size; | |||
| return BroadCastInferShape(op_name, input_args); | |||
| } | |||
| @@ -5982,8 +5982,8 @@ class IsClose(Primitive): | |||
| Args: | |||
| rtol(float): Relative tolerance. Default: 1e-05. | |||
| atol(float): Absolute tolerance. Default: 1e-08. | |||
| equal_nan(bool): If True, then two NaNs will be considered equal. At present, `equal_nan` must be True, | |||
| we will support False in future version. Default: True. | |||
| equal_nan(bool): If True, then two NaNs will be considered equal. At present, `equal_nan` must be True in Ascend | |||
| platform currently, we will support False in future version. Default: True. | |||
| Inputs: | |||
| -**input**(Tensor) – First tensor to compare, with data type belongs to float32, float16, int32. | |||
| @@ -6004,7 +6004,7 @@ class IsClose(Primitive): | |||
| ValueError: If `equal_nan` is False. | |||
| Supported Platforms: | |||
| ``Ascend`` | |||
| ``Ascend`` ``CPU`` | |||
| Examples: | |||
| >>> input = Tensor(np.array([1.3, 2.1, 3.2, 4.1, 5.1]), mindspore.float16) | |||
| @@ -6020,7 +6020,7 @@ class IsClose(Primitive): | |||
| validator.check_value_type('rtol', rtol, [float], self.name) | |||
| validator.check_value_type('atol', atol, [float], self.name) | |||
| validator.check_value_type('equal_nan', equal_nan, [bool], self.name) | |||
| if not equal_nan: | |||
| if context.get_context("device_target") == "Ascend" and not equal_nan: | |||
| raise ValueError("For IsClose, the `equal_nan` must be True, but got False.") | |||
| validator.check_non_negative_float(rtol, 'rtol', self.name) | |||
| validator.check_non_negative_float(atol, 'atol', self.name) | |||
| @@ -0,0 +1,77 @@ | |||
| # 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. | |||
| # ============================================================================ | |||
| 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 | |||
| class Net(nn.Cell): | |||
| def __init__(self, rtol, atol, equal_nan): | |||
| super(Net, self).__init__() | |||
| self.ops = P.IsClose(rtol=rtol, atol=atol, equal_nan=equal_nan) | |||
| def construct(self, a, b): | |||
| return self.ops(a, b) | |||
| def rand_int(*shape): | |||
| """return an random integer array with parameter shape""" | |||
| res = np.random.randint(low=1, high=5, size=shape) | |||
| if isinstance(res, np.ndarray): | |||
| return res.astype(np.float32) | |||
| return float(res) | |||
| def compare_with_numpy(a, b, rtol=1e-05, atol=1e-08, equal_nan=False): | |||
| # Graph Mode | |||
| context.set_context(mode=context.GRAPH_MODE, device_target='CPU') | |||
| ms_result_graph = Net(rtol, atol, equal_nan)(Tensor(a), Tensor(b)) | |||
| # PyNative Mode | |||
| context.set_context(mode=context.PYNATIVE_MODE, device_target='CPU') | |||
| ms_result_pynative = Net(rtol, atol, equal_nan)(Tensor(a), Tensor(b)) | |||
| np_result = np.isclose(a, b, rtol, atol, equal_nan) | |||
| return np.array_equal(ms_result_graph, np_result) and np.array_equal(ms_result_pynative, np_result) | |||
| @pytest.mark.level0 | |||
| @pytest.mark.platform_x86_cpu | |||
| @pytest.mark.env_onecard | |||
| @pytest.mark.parametrize('equal_nan', [True, False]) | |||
| def test_net(equal_nan): | |||
| """ | |||
| Feature: ALL TO ALL | |||
| Description: test cases for IsClose operator. | |||
| Expectation: the result match numpy isclose. | |||
| """ | |||
| a = [0, 1, 2, float('inf'), float('inf'), float('nan')] | |||
| b = [0, 1, -2, float('-inf'), float('inf'), float('nan')] | |||
| assert compare_with_numpy(a, b, equal_nan=equal_nan) | |||
| a = rand_int(2, 3, 4, 5) | |||
| diff = (np.random.random((2, 3, 4, 5)).astype("float32") - 0.5) / 1000 | |||
| b = a + diff | |||
| assert compare_with_numpy(a, b, atol=1e-3) | |||
| assert compare_with_numpy(a, b, atol=1e-3, rtol=1e-4) | |||
| assert compare_with_numpy(a, b, atol=1e-2, rtol=1e-6) | |||
| a = rand_int(2, 3, 4, 5) | |||
| b = rand_int(4, 5) | |||
| assert compare_with_numpy(a, b, equal_nan=equal_nan) | |||