forked from huawei/mindspore2022
Add cpu implementation of IsClose operator.
This commit is contained in:
parent
dc1304afee
commit
54d290a813
|
|
@ -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)
|
||||
Loading…
Reference in New Issue