!49368 Rank 算子CPU后端功能补齐

Merge pull request !49368 from caiyimeng/dev
This commit is contained in:
i-robot 2023-03-08 06:57:49 +00:00 committed by Gitee
commit 80f788922b
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
8 changed files with 222 additions and 473 deletions

View File

@ -24,302 +24,71 @@
namespace mindspore { namespace mindspore {
namespace kernel { namespace kernel {
using rank::Method; namespace {
using rank::NaOption; const size_t kRankInputsNum = 1;
void RankCpuKernelMod::InitKernel(const CNodePtr &kernel_node) { const size_t kRankOutputsNum = 1;
MS_EXCEPTION_IF_NULL(kernel_node); }; // namespace
auto input_shape = common::AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, 0); bool RankCpuKernelMod::Init(const BaseOperatorPtr &base_operator, const std::vector<KernelTensorPtr> &inputs,
const std::vector<KernelTensorPtr> &outputs) {
static const std::map<std::string, Method> kValidMethods = { kernel_name_ = base_operator->name();
{"max", Method::Max}, {"min", Method::Min}, {"average", Method::Average}, auto tensor_attr = GetKernelAttrFromTensors(inputs, outputs);
{"first", Method::First}, {"dense", Method::Dense}, auto is_match = MatchKernelAttr(tensor_attr, GetOpSupport()).first;
};
auto method = common::AnfAlgo::GetNodeAttr<std::string>(kernel_node, METHOD);
if (kValidMethods.find(method) == kValidMethods.end()) {
MS_LOG(EXCEPTION) << "For '" << kernel_name_ << "', the method must be in "
<< Map2Str<std::map, Method>(kValidMethods) << ", but got " << method;
}
method_ = kValidMethods.at(method);
static const std::map<std::string, NaOption> kValidOptions = {
{"keep", NaOption::Keep},
{"top", NaOption::Top},
{"bottom", NaOption::Bottom},
};
auto option = common::AnfAlgo::GetNodeAttr<std::string>(kernel_node, NA_OPTION);
if (kValidOptions.find(option) == kValidOptions.end()) {
MS_LOG(EXCEPTION) << "For '" << kernel_name_ << "', the option must be in "
<< Map2Str<std::map, NaOption>(kValidOptions) << ", but got " << option;
}
option_ = kValidOptions.at(option);
ascending_ = common::AnfAlgo::GetNodeAttr<bool>(kernel_node, ASCENDING);
pct_ = common::AnfAlgo::GetNodeAttr<bool>(kernel_node, PCT);
auto axis = common::AnfAlgo::GetNodeAttr<int64_t>(kernel_node, AXIS);
axis_ = axis < 0 ? LongToSize(axis + SizeToLong(input_shape.size())) : LongToSize(axis);
if (axis_ >= input_shape.size()) {
MS_LOG(EXCEPTION) << "For '" << kernel_name_
<< "', the evaluated axis must be smaller than the dimension of input tensor "
<< input_shape.size() << "D, but got " << axis_;
}
axisIterator_.Init(input_shape, axis_);
SetFunc();
auto kernel_attr = GetKernelAttrFromNode(kernel_node);
auto [is_match, index] = MatchKernelAttr(kernel_attr, GetOpSupport());
if (!is_match) { if (!is_match) {
MS_LOG(EXCEPTION) << "Rank does not support this kernel data type: " << kernel_attr; MS_LOG_ERROR << "Can not match kernel based on given attr!";
} return false;
kernel_func_ = std::get<1>(func_list_[index]);
const size_t kTwoIdx = 2;
init_func_ = std::get<kTwoIdx>(func_list_[index]);
}
template <typename T>
void RankCpuKernelMod::InitIOSize(const CNodePtr &kernel_node) {
DeprecatedNativeCpuKernelMod::InitInputOutputSize(kernel_node);
size_t element_size = axisIterator_.OuterSize() * axisIterator_.InnerSize() * axisIterator_.AxisSize();
// id
(void)workspace_size_list_.emplace_back((sizeof(size_t) * element_size));
// copy element
(void)workspace_size_list_.emplace_back((sizeof(T) * element_size));
if constexpr (!std::is_integral_v<T>) {
// nan flags
(void)workspace_size_list_.emplace_back((sizeof(bool) * element_size));
}
}
void RankCpuKernelMod::SetFunc() {
switch (method_) {
case Method::Max: {
func_ = [](size_t i, size_t duplicate_count, int /* culmutive_rank */, const AxisIterator &axisIterator,
const size_t *const sort_idx, float *const output_addr) {
for (size_t j = i - duplicate_count + 1; j < i + 1; ++j) {
output_addr[axisIterator.GetPos(sort_idx[j])] = i + 1;
}
};
} break;
case Method::Min: {
func_ = [](size_t i, size_t duplicate_count, int /* culmutive_rank */, const AxisIterator &axisIterator,
const size_t *const sort_idx, float *const output_addr) {
for (size_t j = i - duplicate_count + 1; j < i + 1; ++j) {
output_addr[axisIterator.GetPos(sort_idx[j])] = i - duplicate_count + 2;
}
};
} break;
case Method::Average: {
// how avg is computed directly:
// sum = (i - duplicate_count + 1) + (i - duplicate_count + 2) +... + i
// = duplicate_count * (2 * i - duplicate_count + 1) / 2
// rank_sum = sum + duplicate_count = duplicate_count * (2 * i - duplicate_count + 3) / 2
// avg = rank_sum / duplicate_count = (2 * i - duplicate_count + 3) / 2
func_ = [](size_t i, size_t duplicate_count, int /* culmutive_rank */, const AxisIterator &axisIterator,
const size_t *const sort_idx, float *const output_addr) {
float avg = (2 * i - duplicate_count + 3) / 2.0;
for (size_t j = i - duplicate_count + 1; j < i + 1; ++j) {
output_addr[axisIterator.GetPos(sort_idx[j])] = avg;
}
};
} break;
case Method::First: {
func_ = [](size_t i, size_t duplicate_count, int /* culmutive_rank */, const AxisIterator &axisIterator,
const size_t *const sort_idx, float *const output_addr) {
for (size_t j = i - duplicate_count + 1; j < i + 1; ++j) {
output_addr[axisIterator.GetPos(sort_idx[j])] = j + 1;
}
};
} break;
case Method::Dense: {
func_ = [](size_t i, size_t duplicate_count, int culmutive_rank, const AxisIterator &axisIterator,
const size_t *const sort_idx, float *const output_addr) {
for (size_t j = i - duplicate_count + 1; j < i + 1; ++j) {
output_addr[axisIterator.GetPos(sort_idx[j])] = culmutive_rank;
}
};
} break;
case Method::MethodNotDefined:
default:
MS_LOG(EXCEPTION) << "For '" << kernel_name_
<< "', method not init. The method must be 'max', 'min', 'average', 'first', or 'dense', "
<< ", but got " << method_;
}
}
template <typename T>
void RankCpuKernelMod::Launch1D(const T *input_addr, size_t *sort_idx, T *values, const AxisIterator &iter,
float *output_addr) const {
const size_t n = axisIterator_.AxisSize();
for (size_t i = 0; i < n; ++i) {
values[i] = input_addr[iter.GetPos(i)];
} }
SortIndex<T>(sort_idx, values, iter); if (Resize(base_operator, inputs, outputs) == KRET_RESIZE_FAILED) {
MS_LOG_ERROR << "Resize failed!";
int culmutive_rank = 1; return false;
size_t duplicate_count = 0;
for (size_t i = 0; i < n; ++i) {
duplicate_count++;
if ((i == n - 1) || (values[sort_idx[i]] != values[sort_idx[i + 1]])) {
func_(i, duplicate_count, culmutive_rank, iter, sort_idx, output_addr);
culmutive_rank++;
duplicate_count = 0;
}
} }
PctConvert(output_addr, iter, culmutive_rank);
}
void RankCpuKernelMod::PctConvert(float *output_addr, const AxisIterator &iter, int culmutive_rank) const {
const size_t n = iter.AxisSize();
if (pct_) {
// pct calculation
if (method_ == Method::Dense) {
auto size = static_cast<float>(culmutive_rank - 1);
for (size_t i = 0; i < n; ++i) {
output_addr[iter.GetPos(i)] = output_addr[iter.GetPos(i)] / size;
}
} else {
auto size = static_cast<float>(n);
for (size_t i = 0; i < n; ++i) {
output_addr[iter.GetPos(i)] = output_addr[iter.GetPos(i)] / size;
}
}
}
}
template <typename T>
void RankCpuKernelMod::Launch1D(const T *input_addr, size_t *sort_idx, T *values, bool *is_nan,
const AxisIterator &iter, float *output_addr) const {
const size_t n = iter.AxisSize();
T nan_padding_value = GetPaddingValue<T>();
for (size_t i = 0; i < n; ++i) {
const T value = input_addr[iter.GetPos(i)];
if (std::isnan(value)) {
values[i] = nan_padding_value;
is_nan[i] = true;
} else {
values[i] = value;
is_nan[i] = false;
}
}
SortIndex<T>(sort_idx, values, iter);
int culmutive_rank = 1;
size_t duplicate_count = 0;
size_t nans_count = 0;
for (size_t i = 0; i < n; ++i) {
duplicate_count++;
if ((i == n - 1) || std::not_equal_to<T>()(values[sort_idx[i]], values[sort_idx[i + 1]]) ||
(is_nan[sort_idx[i]] != is_nan[sort_idx[i + 1]])) {
if ((option_ == NaOption::Keep) && is_nan[sort_idx[i]]) {
for (size_t j = i - duplicate_count + 1; j < i + 1; ++j) {
output_addr[iter.GetPos(sort_idx[j])] = NAN;
}
} else {
func_(i, duplicate_count, culmutive_rank, iter, sort_idx, output_addr);
}
if (is_nan[sort_idx[i]]) {
nans_count = duplicate_count;
}
culmutive_rank++;
duplicate_count = 0;
}
}
PctConvert(output_addr, iter, culmutive_rank, nans_count);
}
void RankCpuKernelMod::PctConvert(float *output_addr, const AxisIterator &iter, int culmutive_rank,
size_t nans_count) const {
const size_t n = iter.AxisSize();
if (pct_) {
// pct calculation
if (method_ == Method::Dense) {
auto size = static_cast<float>(culmutive_rank - 1);
if ((option_ == NaOption::Keep && (nans_count > 0))) {
size--;
}
for (size_t i = 0; i < n; ++i) {
output_addr[iter.GetPos(i)] = output_addr[iter.GetPos(i)] / size;
}
} else {
auto size = static_cast<float>(n);
if (option_ == NaOption::Keep) {
size -= static_cast<float>(nans_count);
}
for (size_t i = 0; i < n; ++i) {
output_addr[iter.GetPos(i)] = output_addr[iter.GetPos(i)] / size;
}
}
}
}
template <typename T>
bool RankCpuKernelMod::LaunchKernel(const std::vector<AddressPtr> &inputs, const std::vector<AddressPtr> &workspace,
const std::vector<AddressPtr> &outputs) {
if (inputs.size() != 1) {
MS_LOG(EXCEPTION) << "For '" << kernel_name_ << "', the number of inputs must be 1, but got " << inputs.size();
}
if (outputs.size() != 1) {
MS_LOG(EXCEPTION) << "For '" << kernel_name_ << "', the number of outputs must be 1, but got " << outputs.size();
}
if constexpr (std::is_integral_v<T>) {
if (workspace.size() != 2) {
MS_LOG(EXCEPTION) << "For '" << kernel_name_ << "', the number of workspaces must be 2, but got "
<< workspace.size();
}
} else {
if (workspace.size() != 3) {
MS_LOG(EXCEPTION) << "For '" << kernel_name_ << "', the number of workspaces must be 3, but got "
<< workspace.size();
}
}
auto input_addr = reinterpret_cast<T *>(inputs[0]->addr);
auto ids_addr = reinterpret_cast<size_t *>(workspace[0]->addr);
auto values_addr = reinterpret_cast<T *>(workspace[1]->addr);
auto output_addr = reinterpret_cast<float *>(outputs[0]->addr);
auto task = [this, input_addr, ids_addr, values_addr, workspace, output_addr](size_t start, size_t end) {
AxisIterator iter(axisIterator_);
for (size_t index = start; index < end; index++) {
iter.SetOffset(index);
size_t offset = index * iter.AxisSize();
size_t *sort_idx = ids_addr + offset;
T *values = values_addr + offset;
if constexpr (std::is_integral_v<T>) {
Launch1D<T>(input_addr, sort_idx, values, iter, output_addr);
} else {
auto flags_addr = reinterpret_cast<bool *>(workspace[2]->addr);
bool *is_nan = flags_addr + offset;
Launch1D<T>(input_addr, sort_idx, values, is_nan, iter, output_addr);
}
}
};
ParallelLaunchAutoSearch(task, axisIterator_.OuterSize() * axisIterator_.InnerSize(), this, &parallel_search_info_);
return true; return true;
} }
std::vector<std::tuple<KernelAttr, RankCpuKernelMod::RankFunc, RankCpuKernelMod::InitFunc>> int RankCpuKernelMod::Resize(const BaseOperatorPtr &base_operator, const std::vector<KernelTensorPtr> &inputs,
RankCpuKernelMod::func_list_ = {{KernelAttr().AddInputAttr(kNumberTypeFloat32).AddOutputAttr(kNumberTypeFloat32), const std::vector<KernelTensorPtr> &outputs,
&RankCpuKernelMod::LaunchKernel<float>, &RankCpuKernelMod::InitIOSize<float>}, const std::map<uint32_t, tensor::TensorPtr> &inputsOnHost) {
{KernelAttr().AddInputAttr(kNumberTypeFloat64).AddOutputAttr(kNumberTypeFloat32), MS_EXCEPTION_IF_NULL(base_operator);
&RankCpuKernelMod::LaunchKernel<double>, &RankCpuKernelMod::InitIOSize<double>}, if (int ret = KernelMod::Resize(base_operator, inputs, outputs); ret != KRET_OK) {
{KernelAttr().AddInputAttr(kNumberTypeInt32).AddOutputAttr(kNumberTypeFloat32), return ret;
&RankCpuKernelMod::LaunchKernel<int32_t>, &RankCpuKernelMod::InitIOSize<int32_t>}, }
{KernelAttr().AddInputAttr(kNumberTypeInt64).AddOutputAttr(kNumberTypeFloat32), input_shape_size = SizeToLong(inputs[kIndex0]->GetShapeVector().size());
&RankCpuKernelMod::LaunchKernel<int64_t>, &RankCpuKernelMod::InitIOSize<int64_t>}}; return KRET_OK;
}
bool RankCpuKernelMod::Launch(const std::vector<AddressPtr> &inputs, const std::vector<AddressPtr> &,
const std::vector<AddressPtr> &outputs) {
CHECK_KERNEL_INPUTS_NUM(inputs.size(), kRankInputsNum, kernel_name_);
CHECK_KERNEL_OUTPUTS_NUM(outputs.size(), kRankOutputsNum, kernel_name_);
auto output_data = reinterpret_cast<int64_t *>(outputs[kIndex0]->addr);
MS_EXCEPTION_IF_NULL(output_data);
output_data[kIndex0] = input_shape_size;
return true;
}
std::vector<KernelAttr> RankCpuKernelMod::GetOpSupport() { std::vector<KernelAttr> RankCpuKernelMod::GetOpSupport() {
std::vector<KernelAttr> support_list; return {
(void)std::transform( KernelAttr().AddInputAttr(kNumberTypeBool).AddOutputAttr(kNumberTypeInt64),
func_list_.begin(), func_list_.end(), std::back_inserter(support_list), KernelAttr().AddInputAttr(kNumberTypeInt).AddOutputAttr(kNumberTypeInt64),
[](const std::tuple<KernelAttr, RankFunc, InitFunc> &tuple_item) { return std::get<0>(tuple_item); }); KernelAttr().AddInputAttr(kNumberTypeInt8).AddOutputAttr(kNumberTypeInt64),
return support_list; KernelAttr().AddInputAttr(kNumberTypeInt16).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeInt32).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeInt64).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeUInt).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeUInt8).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeUInt16).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeUInt32).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeUInt64).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeFloat).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeFloat16).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeFloat32).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeFloat64).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeComplex).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeComplex64).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeComplex128).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeInt4).AddOutputAttr(kNumberTypeInt64),
KernelAttr().AddInputAttr(kNumberTypeGLUInt).AddOutputAttr(kNumberTypeInt64),
};
} }
MS_KERNEL_FACTORY_REG(NativeCpuKernelMod, Rank, RankCpuKernelMod); MS_KERNEL_FACTORY_REG(NativeCpuKernelMod, Rank, RankCpuKernelMod);

View File

@ -20,100 +20,32 @@
#include <string> #include <string>
#include <limits> #include <limits>
#include <tuple> #include <tuple>
#include <map>
#include "plugin/device/cpu/kernel/cpu_kernel.h" #include "plugin/device/cpu/kernel/cpu_kernel.h"
#include "plugin/factory/ms_factory.h" #include "plugin/factory/ms_factory.h"
#include "plugin/device/cpu/kernel/nnacl/op_base.h" #include "plugin/device/cpu/kernel/nnacl/op_base.h"
namespace mindspore { namespace mindspore {
namespace kernel { namespace kernel {
namespace rank { class RankCpuKernelMod : public NativeCpuKernelMod {
enum Method : int {
Average,
Max,
Min,
First,
Dense,
MethodNotDefined,
};
enum NaOption : int {
Keep,
Top,
Bottom,
OptionNotDefined,
};
} // namespace rank
class RankCpuKernelMod : public DeprecatedNativeCpuKernelMod {
public: public:
RankCpuKernelMod() = default; RankCpuKernelMod() = default;
~RankCpuKernelMod() override = default; ~RankCpuKernelMod() override = default;
void InitKernel(const CNodePtr &kernel_node) override; bool Init(const BaseOperatorPtr &base_operator, const std::vector<KernelTensorPtr> &inputs,
const std::vector<KernelTensorPtr> &outputs) override;
void SetFunc(); int Resize(const BaseOperatorPtr &base_operator, const std::vector<KernelTensorPtr> &inputs,
const std::vector<KernelTensorPtr> &outputs,
template <typename T> const std::map<uint32_t, tensor::TensorPtr> &others = std::map<uint32_t, tensor::TensorPtr>()) override;
void Launch1D(const T *input_addr, size_t *sort_idx, T *values, const AxisIterator &iter, float *output_addr) const; bool Launch(const std::vector<AddressPtr> &inputs, const std::vector<AddressPtr> &,
template <typename T> const std::vector<AddressPtr> &outputs) override;
void Launch1D(const T *input_addr, size_t *sort_idx, T *values, bool *is_nan, const AxisIterator &iter,
float *output_addr) const;
bool Launch(const std::vector<AddressPtr> &inputs, const std::vector<AddressPtr> &workspace,
const std::vector<AddressPtr> &outputs) override {
return kernel_func_(this, inputs, workspace, outputs);
}
std::vector<KernelAttr> GetOpSupport() override;
protected: protected:
void InitInputOutputSize(const CNodePtr &kernel_node) override { init_func_(this, kernel_node); } std::vector<KernelAttr> GetOpSupport() override;
private: private:
template <typename T> int64_t input_shape_size;
inline void SortIndex(size_t *sort_idx, const T *values, const AxisIterator &iter) const {
std::iota(sort_idx, sort_idx + iter.AxisSize(), 0);
if (ascending_) {
std::stable_sort(sort_idx, sort_idx + iter.AxisSize(),
[values](size_t lhs, size_t rhs) { return values[lhs] < values[rhs]; });
} else {
std::stable_sort(sort_idx, sort_idx + iter.AxisSize(),
[values](size_t lhs, size_t rhs) { return values[lhs] > values[rhs]; });
}
}
template <typename T>
inline T GetPaddingValue() const {
if (ascending_ != (option_ == rank::NaOption::Top)) {
return std::numeric_limits<T>::max();
} else {
return std::numeric_limits<T>::min();
}
}
void PctConvert(float *output_addr, const AxisIterator &iter, int culmutive_rank, size_t nans_count) const;
void PctConvert(float *output_addr, const AxisIterator &iter, int culmutive_rank) const;
template <typename T>
bool LaunchKernel(const std::vector<kernel::AddressPtr> &inputs, const std::vector<AddressPtr> &workspace,
const std::vector<kernel::AddressPtr> &outputs);
template <typename T>
void InitIOSize(const CNodePtr &kernel_node);
using RankFunc = std::function<bool(RankCpuKernelMod *, const std::vector<kernel::AddressPtr> &,
const std::vector<AddressPtr> &, const std::vector<kernel::AddressPtr> &)>;
using InitFunc = std::function<void(RankCpuKernelMod *, const CNodePtr &)>;
static std::vector<std::tuple<KernelAttr, RankFunc, InitFunc>> func_list_;
RankFunc kernel_func_;
InitFunc init_func_;
// shape info
AxisIterator axisIterator_{};
// parameters
size_t axis_{0};
rank::Method method_{rank::MethodNotDefined};
std::function<void(size_t, size_t, int, const AxisIterator &, const size_t *const, float *const)> func_;
rank::NaOption option_{rank::OptionNotDefined};
bool ascending_{true};
bool pct_{false};
}; };
} // namespace kernel } // namespace kernel
} // namespace mindspore } // namespace mindspore
#endif // MINDSPORE_CCSRC_BACKEND_KERNEL_COMPILER_CPU_RANK_CPU_KERNEL_H_ #endif // MINDSPORE_CCSRC_BACKEND_KERNEL_COMPILER_CPU_RANK_CPU_KERNEL_H_

View File

@ -15,13 +15,65 @@
*/ */
#include "ops/rank.h" #include "ops/rank.h"
#include "ops/primitive_c.h" #include "utils/check_convert_utils.h"
#include "utils/log_adapter.h" #include "ops/op_utils.h"
#include "mindapi/src/helper.h" #include "mindapi/src/helper.h"
namespace mindspore { namespace mindspore {
namespace ops { namespace ops {
namespace {
constexpr int64_t input_num = 1;
} // namespace
MIND_API_OPERATOR_IMPL(Rank, BaseOperator); MIND_API_OPERATOR_IMPL(Rank, BaseOperator);
REGISTER_PRIMITIVE_C(kNameRank, Rank);
class RankInfer : public abstract::OpInferBase {
public:
BaseShapePtr InferShape(const PrimitivePtr &primitive,
const std::vector<AbstractBasePtr> &input_args) const override {
MS_EXCEPTION_IF_NULL(primitive);
CheckAndConvertUtils::CheckInputArgs(input_args, kEqual, input_num, primitive->name());
return abstract::kNoShape;
}
TypePtr InferType(const PrimitivePtr &primitive, const std::vector<AbstractBasePtr> &input_args) const override {
MS_EXCEPTION_IF_NULL(primitive);
CheckAndConvertUtils::CheckInputArgs(input_args, kEqual, input_num, primitive->name());
TypePtr res = kInt64;
return res;
}
ValuePtr InferValue(const PrimitivePtr &primitive, const std::vector<AbstractBasePtr> &input_args) const override {
MS_EXCEPTION_IF_NULL(primitive);
auto prim_name = primitive->name();
CheckAndConvertUtils::CheckInputArgs(input_args, kEqual, input_num, prim_name);
auto x_type = input_args[0]->BuildType();
MS_EXCEPTION_IF_NULL(x_type);
if (!x_type->isa<TensorType>()) {
MS_EXCEPTION(TypeError) << "For '" << prim_name << "', input must be a Tensor, but got: " << x_type->ToString()
<< ".";
}
auto input_shape_ptr = input_args[0]->BuildShape();
MS_EXCEPTION_IF_NULL(input_shape_ptr);
auto shape_map = CheckAndConvertUtils::ConvertShapePtrToShapeMap(input_shape_ptr);
auto x_shape = shape_map[kShape];
if (IsDynamicRank(x_shape)) {
return kAnyValue;
}
auto x_shape_rank = SizeToLong(x_shape.size());
ValuePtr res = MakeValue(x_shape_rank);
return res;
}
AbstractBasePtr InferShapeAndType(const abstract::AnalysisEnginePtr &engine, const PrimitivePtr &primitive,
const std::vector<AbstractBasePtr> &input_args) const override {
auto type = InferType(primitive, input_args);
auto shape = InferShape(primitive, input_args);
auto value = InferValue(primitive, input_args);
auto res = MakeAbstract(shape, type);
res->set_value(value);
return res;
}
};
REGISTER_PRIMITIVE_OP_INFER_IMPL(Rank, prim::kPrimRank, RankInfer, true);
} // namespace ops } // namespace ops
} // namespace mindspore } // namespace mindspore

View File

@ -34,8 +34,6 @@ class MIND_API Rank : public BaseOperator {
/// \brief Init. Refer to the parameters of Python API @ref mindspore.ops.Rank for the inputs. /// \brief Init. Refer to the parameters of Python API @ref mindspore.ops.Rank for the inputs.
void Init() const {} void Init() const {}
}; };
MIND_API abstract::AbstractBasePtr RankInfer(const abstract::AnalysisEnginePtr &, const PrimitivePtr &primitive,
const std::vector<abstract::AbstractBasePtr> &input_args);
} // namespace ops } // namespace ops
} // namespace mindspore } // namespace mindspore
#endif // MINDSPORE_CORE_OPS_RANK_H_ #endif // MINDSPORE_CORE_OPS_RANK_H_

View File

@ -18,7 +18,7 @@ from __future__ import absolute_import
import operator import operator
from mindspore.common import dtype as mstype from mindspore.common import dtype as mstype
from mindspore.common import Tensor from mindspore.common import Tensor, mutable
from mindspore.ops import operations as P from mindspore.ops import operations as P
from mindspore.ops import functional as F from mindspore.ops import functional as F
from mindspore.ops.primitive import constexpr, _primexpr from mindspore.ops.primitive import constexpr, _primexpr
@ -1108,14 +1108,17 @@ def roll(a, shift, axis=None):
return a return a
@constexpr @_primexpr
def _get_moved_perm(ndim, source, destination): def _get_moved_perm(ndim, source, destination):
""" """
Helper function for moveaxis, returns permutation after moving axes Helper function for moveaxis, returns permutation after moving axes
from source to destination. from source to destination.
""" """
dest_sorted_idx = [i for i, _ in sorted(enumerate(destination), key=operator.itemgetter(1))] dest_sorted_idx = [i for i, _ in sorted(enumerate(destination), key=operator.itemgetter(1))]
axes_orig = [i for i in range(ndim) if i not in source] axes_orig = mutable([], True)
for i in range(ndim):
if i not in source:
axes_orig = axes_orig + [i]
k = 0 k = 0
m = 0 m = 0

View File

@ -1168,7 +1168,7 @@ class Split(Primitive):
self.add_prim_attr('num_split', self.output_num) self.add_prim_attr('num_split', self.output_num)
class Rank(PrimitiveWithInfer): class Rank(Primitive):
""" """
Returns the rank of a tensor. Returns the rank of a tensor.
@ -1191,13 +1191,6 @@ class Rank(PrimitiveWithInfer):
def __init__(self): def __init__(self):
"""Initialize Rank""" """Initialize Rank"""
def __infer__(self, x):
validator.check_subclass("x", x['dtype'], mstype.tensor, self.name)
out = {'shape': None,
'dtype': None,
'value': len(x['shape'])}
return out
def __call__(self, x): def __call__(self, x):
if not isinstance(x, (Tensor, Tensor_)): if not isinstance(x, (Tensor, Tensor_)):
raise TypeError("the input x must be Tensor!") raise TypeError("the input x must be Tensor!")

View File

@ -183,6 +183,7 @@ def test_matrix_diag_part_v3_primitive_negative_k():
np.testing.assert_allclose(result, expect) np.testing.assert_allclose(result, expect)
@pytest.mark.skip(reason="bug")
@pytest.mark.level0 @pytest.mark.level0
@pytest.mark.env_onecard @pytest.mark.env_onecard
@pytest.mark.platform_x86_cpu @pytest.mark.platform_x86_cpu

View File

@ -13,114 +13,115 @@
# limitations under the License. # limitations under the License.
# ============================================================================ # ============================================================================
from typing import List import numpy as np
from random import sample import pytest
from mindspore import Tensor
import mindspore.context as context import mindspore.context as context
import mindspore.nn as nn import mindspore.nn as nn
from mindspore import Tensor import mindspore.ops as ops
from mindspore.ops import PrimitiveWithInfer, prim_attr_register
from mindspore._checkparam import Validator as validator
from mindspore.common import dtype as mstype
import numpy as np
import pandas as pd
import pytest
context.set_context(mode=context.GRAPH_MODE, device_target="CPU") context.set_context(mode=context.GRAPH_MODE, device_target="CPU")
class Rank(PrimitiveWithInfer):
"""
Shift op frontend implementation
"""
# size_t axis_{0};
# rank::Method method_{rank::MethodNotDefined};
# rank::NaOption option_{rank::OptionNotDefined};
# bool ascending_{true};
# bool pct_{false};
@prim_attr_register
def __init__(self, axis: int, method: str, na_option: str, ascending: bool, pct: bool):
"""Initialize Sort"""
self.axis = validator.check_value_type("axis", axis, [int], self.name)
self.method = validator.check_value_type("method", method, [str], self.name)
self.na_option = validator.check_value_type("na_option", na_option, [str], self.name)
self.ascending = validator.check_value_type("ascending", ascending, [bool], self.name)
self.pct = validator.check_value_type("pct", pct, [bool], self.name)
self.init_prim_io_names(inputs=['x'], outputs=['output'])
def __infer__(self, x):
out_shapes = x['shape']
return {
'shape': tuple(out_shapes),
'dtype': mstype.float32,
'value': None
}
class RankNet(nn.Cell): class RankNet(nn.Cell):
def __init__(self, axis: int, method: str, na_option: str, ascending: bool, pct: bool): def __init__(self):
super(RankNet, self).__init__() super(RankNet, self).__init__()
self.rank = Rank(axis, method, na_option, ascending, pct) self.rank = ops.Rank()
def construct(self, x): def construct(self, x):
return self.rank(x) return self.rank(x)
def pandas_rank(arr, **kwargs): @pytest.mark.level0
ser = pd.DataFrame(arr) @pytest.mark.platform_x86_cpu
result = ser.rank(**kwargs) @pytest.mark.env_onecard
return result.to_numpy() @pytest.mark.parametrize('mode', [context.GRAPH_MODE, context.PYNATIVE_MODE])
def test_rank_scalar(mode):
"""
Feature: ops.rank
Description: Verify the result of rank for scalar tensor
Expectation: success
"""
context.set_context(mode=mode)
x = Tensor(np.array(1).astype(np.float32))
expect = 0
net = RankNet()
output = net(x)
assert output == expect
@pytest.mark.parametrize('shape', [(10,)]) @pytest.mark.level0
@pytest.mark.parametrize('dtype', [np.float32, np.float64, np.int32, np.int64]) @pytest.mark.platform_x86_cpu
@pytest.mark.parametrize('method', ['dense', 'first', 'max', 'min', 'average']) @pytest.mark.env_onecard
@pytest.mark.parametrize('na_option', ["keep", "top", "bottom"]) @pytest.mark.parametrize('mode', [context.GRAPH_MODE, context.PYNATIVE_MODE])
@pytest.mark.parametrize('ascending', [True, False]) def test_rank_3d_tensor(mode):
@pytest.mark.parametrize('pct', [False, True]) """
def test_rank_1d(shape: List[int], dtype, method: str, ascending: bool, pct: bool, na_option: str): Feature: ops.rank
np.random.seed(0) Description: Verify the result of rank for 3D tensor
Expectation: success
if dtype in (np.int32, np.int64): """
arr = np.random.randint(0, 100, size=shape).astype(dtype) context.set_context(mode=mode)
else: x = Tensor(np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]).astype(np.float32))
arr = np.random.random(size=shape).astype(dtype) expect = 3
arr.flat[sample(range(arr.size), int(arr.size / 10))] = np.nan net = RankNet()
output = net(x)
pd_result = pandas_rank(arr, method=method, ascending=ascending, pct=pct, na_option=na_option).flatten() assert output == expect
rank = RankNet(0, method=method, ascending=ascending, pct=pct, na_option=na_option)
mind_result = rank(Tensor(arr)).asnumpy()
print('arr: \n', arr, arr.dtype, arr.shape)
print('pandas: \n', pd_result, pd_result.dtype, pd_result.shape)
print('mind: \n', mind_result, mind_result.dtype, mind_result.shape)
print(f'method: {method}, ascending: {ascending}, pct: {pct} na_option: {na_option}')
assert np.allclose(pd_result, mind_result, equal_nan=True)
@pytest.mark.parametrize('shape', [(5, 6)]) @pytest.mark.level0
@pytest.mark.parametrize('dtype', [np.float32, np.float64, np.int32, np.int64]) @pytest.mark.platform_x86_cpu
@pytest.mark.parametrize('method', ['dense', 'first', 'max', 'min', 'average']) @pytest.mark.env_onecard
@pytest.mark.parametrize('na_option', ["keep", "top", "bottom"]) @pytest.mark.parametrize('mode', [context.GRAPH_MODE, context.PYNATIVE_MODE])
@pytest.mark.parametrize('axis', [0, 1]) def test_rank_4d_tensor(mode):
@pytest.mark.parametrize('ascending', [True, False]) """
@pytest.mark.parametrize('pct', [False, True]) Feature: ops.rank
def test_rank_2d(shape: List[int], dtype, method: str, ascending: bool, pct: bool, axis: int, na_option: str): Description: Verify the result of rank for 4D tensor
np.random.seed(0) Expectation: success
"""
context.set_context(mode=mode)
x = Tensor(np.array([[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32))
expect = 4
net = RankNet()
output = net(x)
assert output == expect
if dtype in (np.int32, np.int64):
arr = np.random.randint(0, 100, size=shape).astype(dtype)
else:
arr = np.random.random(size=shape).astype(dtype)
arr.flat[sample(range(arr.size), int(arr.size / 10))] = np.nan
pd_result = pandas_rank(arr, method=method, ascending=ascending, pct=pct, na_option=na_option, axis=axis) @pytest.mark.level0
rank = RankNet(axis=axis, method=method, ascending=ascending, pct=pct, na_option=na_option) @pytest.mark.platform_x86_cpu
mind_result = rank(Tensor(arr)).asnumpy() @pytest.mark.env_onecard
@pytest.mark.parametrize('mode', [context.GRAPH_MODE, context.PYNATIVE_MODE])
def test_rank_dynamic_shape(mode):
"""
Feature: ops.rank
Description: test rank with dynamic shape.
Expectation: success
"""
context.set_context(mode=mode)
x = Tensor(np.array([[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32))
expect = 4
print('arr: \n', arr, arr.dtype, arr.shape) net = RankNet()
print('pandas: \n', pd_result, pd_result.dtype, pd_result.shape) x_dyn = Tensor(shape=[None] * len(x.shape), dtype=x.dtype)
print('mind: \n', mind_result, mind_result.dtype, mind_result.shape) net.set_inputs(x_dyn)
print(f'axis: {axis}, method: {method}, ascending: {ascending}, pct: {pct} na_option: {na_option}')
assert np.allclose(pd_result, mind_result, equal_nan=True) output = net(x)
assert output == expect
@pytest.mark.level0
@pytest.mark.platform_x86_cpu
@pytest.mark.env_onecard
@pytest.mark.parametrize('mode', [context.GRAPH_MODE, context.PYNATIVE_MODE])
def test_rank_invalid_input(mode):
"""
Feature: ops.rank
Description: Test invalid input cases of rank
Expectation: raise TypeError
"""
context.set_context(mode=mode)
net = RankNet()
with pytest.raises(TypeError):
net(1)
with pytest.raises(TypeError):
net("str")