[core]Migrate Convert operator to new API (#20961)
* Migrate Convert operator to new API * Unify Convert reference for non-LP types * Correct evaluate when output tensor not allocated * Correct input type name * Restore static_cast for bool as char
This commit is contained in:
1 parent
aee4865b2f
commit
78bbe03ae8
3 files changed
+171
-223
No files matched your search
@@ -40,9 +40,7 @@ public:
|
||||
m_destination_type = destination_type;
|
||||
}
|
||||
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
bool evaluate(const HostTensorVector& outputs, const HostTensorVector& inputs) const override;
|
||||
OPENVINO_SUPPRESS_DEPRECATED_END
|
||||
bool evaluate(TensorVector& outputs, const TensorVector& inputs) const override;
|
||||
bool has_evaluate() const override;
|
||||
bool evaluate_lower(TensorVector& outputs) const override;
|
||||
bool evaluate_upper(TensorVector& outputs) const override;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
#include "openvino/core/type/element_type.hpp"
|
||||
@@ -94,13 +95,21 @@ void lp_convert(const TI* arg, TO* out, size_t count, element::Type_t src_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TI, typename TO>
|
||||
typename std::enable_if<!std::is_same<TO, char>::value, TO>::type convert(const TI v) {
|
||||
return static_cast<TO>(v);
|
||||
}
|
||||
|
||||
template <typename TI, typename TO>
|
||||
typename std::enable_if<std::is_same<TO, char>::value, TO>::type convert(const TI v) {
|
||||
return static_cast<char>(static_cast<bool>(v));
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename TI, typename TO>
|
||||
typename std::enable_if<!std::is_same<TO, char>::value>::type convert(const TI* arg, TO* out, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
out[i] = static_cast<TO>(arg[i]);
|
||||
}
|
||||
void convert(const TI* arg, TO* out, const size_t count) {
|
||||
std::transform(arg, arg + count, out, detail::convert<TI, TO>);
|
||||
}
|
||||
|
||||
#if defined(OPENVINO_ARCH_X86) || defined(OPENVINO_ARCH_X86_64)
|
||||
@@ -123,14 +132,5 @@ size_t count_out_of_f16_range(const float* arg, size_t count);
|
||||
|
||||
// Convert values from f32 to f16 with claming to f16 min/max when value is out of normal finite numbers range
|
||||
void convert_from_f32_to_f16_with_clamp(const float* arg, float16* out, size_t count);
|
||||
|
||||
// overload to handle ov::boolean (it is stored as char)
|
||||
template <typename TI, typename TO>
|
||||
typename std::enable_if<std::is_same<TO, char>::value>::type convert(const TI* arg, TO* out, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
out[i] = static_cast<char>(static_cast<bool>(arg[i]));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reference
|
||||
} // namespace ov
|
||||
+157
-207
@@ -2,248 +2,198 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ngraph/op/convert.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <ngraph/validation_util.hpp>
|
||||
#include "openvino/op/convert.hpp"
|
||||
|
||||
#include "element_visitor.hpp"
|
||||
#include "itt.hpp"
|
||||
#include "ngraph/op/equal.hpp"
|
||||
#include "ngraph/op/select.hpp"
|
||||
#include "openvino/op/equal.hpp"
|
||||
#include "openvino/op/select.hpp"
|
||||
#include "openvino/reference/convert.hpp"
|
||||
#include "validation_util.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
namespace ov {
|
||||
namespace op {
|
||||
namespace convert {
|
||||
|
||||
op::Convert::Convert(const Output<Node>& arg, const element::Type& destination_type)
|
||||
constexpr bool is_lp_type(const element::Type_t et) {
|
||||
return (et == element::i4) || (et == element::u1) || (et == element::u4) || (et == element::nf4);
|
||||
}
|
||||
|
||||
#define CONVERT_ET_LIST boolean, bf16, f16, f32, f64, i4, i8, i16, i32, i64, u1, u4, u8, u16, u32, u64, nf4
|
||||
|
||||
struct Evaluate : public element::NoAction<bool> {
|
||||
using element::NoAction<bool>::visit;
|
||||
template <element::Type_t ET, class TI = fundamental_type_for<ET>>
|
||||
static result_type visit(const Tensor& arg, Tensor& out, const size_t count) {
|
||||
using namespace ov::element;
|
||||
return IfTypeOf<CONVERT_ET_LIST>::apply<EvalByOutputType<is_lp_type(ET)>>(
|
||||
out.get_element_type(),
|
||||
reinterpret_cast<const TI*>(arg.data()),
|
||||
out,
|
||||
count,
|
||||
ET);
|
||||
}
|
||||
|
||||
private:
|
||||
template <bool IS_ARG_ET_LP>
|
||||
struct EvalByOutputType : public element::NoAction<bool> {
|
||||
using element::NoAction<bool>::visit;
|
||||
|
||||
template <element::Type_t ET,
|
||||
class T,
|
||||
class T_ET,
|
||||
class U = ov::fundamental_type_for<ET>,
|
||||
typename std::enable_if<is_lp_type(ET) || IS_ARG_ET_LP>::type* = nullptr>
|
||||
static result_type visit(const T* arg, Tensor& out, const size_t count, T_ET&& arg_et) {
|
||||
reference::detail::lp_convert(arg, reinterpret_cast<U*>(out.data()), count, arg_et, ET);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <element::Type_t ET,
|
||||
class T,
|
||||
class T_ET,
|
||||
class U = ov::fundamental_type_for<ET>,
|
||||
typename std::enable_if<!is_lp_type(ET) && !IS_ARG_ET_LP>::type* = nullptr>
|
||||
static result_type visit(const T* arg, Tensor& out, const size_t count, T_ET&&) {
|
||||
reference::convert(arg, out.data<U>(), count);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
namespace {
|
||||
bool evaluate_bound(const Node* const node, TensorVector& output_values, const Tensor& input_bound) {
|
||||
OPENVINO_ASSERT(node, output_values.size() == 1);
|
||||
|
||||
if (input_bound) {
|
||||
const auto& in_bound_shape = input_bound.get_shape();
|
||||
if (is_vector(in_bound_shape) && (in_bound_shape[0] == 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto is_integral_up_to_16_bits = [](const element::Type& et) -> bool {
|
||||
return et.is_integral() && et.bitwidth() <= 16;
|
||||
};
|
||||
const auto& input_et = input_bound.get_element_type();
|
||||
const auto& output_et = output_values[0].get_element_type();
|
||||
|
||||
const auto status = node->evaluate(output_values, {input_bound});
|
||||
if (!status || is_integral_up_to_16_bits(input_et) || is_integral_up_to_16_bits(output_et)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// constants for dynamic values translation
|
||||
const auto input_max = ov::util::make_tensor_of_max_value(input_et);
|
||||
const auto output_max = ov::util::make_tensor_of_max_value(output_et);
|
||||
if (!input_max || !output_max)
|
||||
return false;
|
||||
|
||||
// dynamic values translation
|
||||
auto input_dynamic_mask = Tensor(element::boolean, in_bound_shape);
|
||||
auto outputs = TensorVector{input_dynamic_mask};
|
||||
|
||||
return v1::Equal().evaluate(outputs, {input_bound, input_max}) &&
|
||||
v1::Select().evaluate(output_values, {input_dynamic_mask, output_max, output_values[0]});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace convert
|
||||
namespace v0 {
|
||||
|
||||
Convert::Convert(const Output<Node>& arg, const element::Type& destination_type)
|
||||
: Op({arg}),
|
||||
m_destination_type(destination_type) {
|
||||
constructor_validate_and_infer_types();
|
||||
}
|
||||
|
||||
void op::Convert::validate_and_infer_types() {
|
||||
void Convert::validate_and_infer_types() {
|
||||
OV_OP_SCOPE(v0_Convert_validate_and_infer_types);
|
||||
|
||||
set_output_type(0, m_destination_type, get_input_partial_shape(0));
|
||||
}
|
||||
|
||||
bool op::Convert::visit_attributes(AttributeVisitor& visitor) {
|
||||
bool Convert::visit_attributes(AttributeVisitor& visitor) {
|
||||
OV_OP_SCOPE(v0_Convert_visit_attributes);
|
||||
visitor.on_attribute("destination_type", m_destination_type);
|
||||
return true;
|
||||
}
|
||||
|
||||
shared_ptr<Node> op::Convert::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
std::shared_ptr<Node> Convert::clone_with_new_inputs(const OutputVector& new_args) const {
|
||||
OV_OP_SCOPE(v0_Convert_clone_with_new_inputs);
|
||||
check_new_args_count(this, new_args);
|
||||
return make_shared<Convert>(new_args.at(0), m_destination_type);
|
||||
return std::make_shared<Convert>(new_args.at(0), m_destination_type);
|
||||
}
|
||||
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
namespace convert {
|
||||
namespace {
|
||||
template <element::Type_t INPUT_ET, element::Type_t OUTPUT_ET>
|
||||
bool evaluate(const HostTensorPtr& arg, const HostTensorPtr& out) {
|
||||
out->set_shape(arg->get_shape());
|
||||
size_t element_count = shape_size(out->get_shape());
|
||||
|
||||
if ((INPUT_ET != arg->get_element_type()) || OUTPUT_ET != out->get_element_type()) {
|
||||
return false;
|
||||
}
|
||||
if (((INPUT_ET == element::u1) || (OUTPUT_ET == element::u1)) ||
|
||||
((INPUT_ET == element::u4) || (OUTPUT_ET == element::u4)) ||
|
||||
((INPUT_ET == element::i4) || (OUTPUT_ET == element::i4)) ||
|
||||
((INPUT_ET == element::nf4) || (OUTPUT_ET == element::nf4))) {
|
||||
ov::reference::detail::lp_convert(arg->get_data_ptr<INPUT_ET>(),
|
||||
out->get_data_ptr<OUTPUT_ET>(),
|
||||
element_count,
|
||||
INPUT_ET,
|
||||
OUTPUT_ET);
|
||||
} else {
|
||||
ov::reference::convert(arg->get_data_ptr<INPUT_ET>(), out->get_data_ptr<OUTPUT_ET>(), element_count);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define TYPE_OUT_CASE(a, ...) \
|
||||
case element::Type_t::a: { \
|
||||
OV_OP_SCOPE(OV_PP_CAT3(evaluate_covert_out, _, a)); \
|
||||
rc = evaluate<INPUT_ET, element::Type_t::a>(__VA_ARGS__); \
|
||||
} break
|
||||
|
||||
template <element::Type_t INPUT_ET>
|
||||
bool evaluate(const HostTensorPtr& arg, const HostTensorPtr& out) {
|
||||
bool rc = true;
|
||||
|
||||
switch (out->get_element_type()) {
|
||||
TYPE_OUT_CASE(i4, arg, out);
|
||||
TYPE_OUT_CASE(i8, arg, out);
|
||||
TYPE_OUT_CASE(i16, arg, out);
|
||||
TYPE_OUT_CASE(i32, arg, out);
|
||||
TYPE_OUT_CASE(i64, arg, out);
|
||||
TYPE_OUT_CASE(u1, arg, out);
|
||||
TYPE_OUT_CASE(u4, arg, out);
|
||||
TYPE_OUT_CASE(u8, arg, out);
|
||||
TYPE_OUT_CASE(u16, arg, out);
|
||||
TYPE_OUT_CASE(u32, arg, out);
|
||||
TYPE_OUT_CASE(u64, arg, out);
|
||||
TYPE_OUT_CASE(bf16, arg, out);
|
||||
TYPE_OUT_CASE(f16, arg, out);
|
||||
TYPE_OUT_CASE(f32, arg, out);
|
||||
TYPE_OUT_CASE(f64, arg, out);
|
||||
TYPE_OUT_CASE(boolean, arg, out);
|
||||
TYPE_OUT_CASE(nf4, arg, out);
|
||||
default:
|
||||
rc = false;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool evaluate_convert(const HostTensorPtr& arg, const HostTensorPtr& out) {
|
||||
bool rc = true;
|
||||
switch (arg->get_element_type()) {
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, u1, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, u4, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, u8, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, u16, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, u32, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, u64, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, i4, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, i8, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, i16, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, i32, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, i64, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, bf16, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, f16, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, f32, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, f64, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, boolean, arg, out);
|
||||
OPENVINO_TYPE_CASE(evaluate_convert, nf4, arg, out);
|
||||
default:
|
||||
rc = false;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool evaluate_bound(const Node* node, ov::TensorVector& output_values, bool is_upper) {
|
||||
OPENVINO_ASSERT(node, output_values.size() == 1);
|
||||
const auto& input = node->input_value(0);
|
||||
if (const auto& value = is_upper ? input.get_tensor().get_upper_value() : input.get_tensor().get_lower_value()) {
|
||||
if (is_vector(value.get_shape()) && (value.get_shape().front() == 0)) {
|
||||
return true;
|
||||
}
|
||||
bool status = node->evaluate(output_values, {value});
|
||||
|
||||
if (!status)
|
||||
return status;
|
||||
|
||||
const auto& input_element_type = input.get_element_type();
|
||||
const auto& output_element_type = output_values[0].get_element_type();
|
||||
if ((input_element_type.is_integral() && input_element_type.bitwidth() <= 16) ||
|
||||
(output_element_type.is_integral() && output_element_type.bitwidth() <= 16)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// constants for dynamic values translation
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
auto input_maximum_value = get_constant_max_of_type(input_element_type);
|
||||
auto output_maximum_value = get_constant_max_of_type(output_values[0].get_element_type());
|
||||
OPENVINO_SUPPRESS_DEPRECATED_END
|
||||
if (input_maximum_value == nullptr || output_maximum_value == nullptr)
|
||||
return false;
|
||||
|
||||
auto input_max = ov::Tensor(input_maximum_value->get_element_type(), input_maximum_value->get_shape());
|
||||
memcpy(input_max.data(), input_maximum_value->get_data_ptr(), input_max.get_byte_size());
|
||||
|
||||
auto output_max = ov::Tensor(output_maximum_value->get_element_type(), output_maximum_value->get_shape());
|
||||
memcpy(output_max.data(), output_maximum_value->get_data_ptr(), output_max.get_byte_size());
|
||||
|
||||
// dynamic values translation
|
||||
auto input_dynamic_mask = ov::Tensor(element::boolean, input.get_shape());
|
||||
auto outputs = ov::TensorVector{input_dynamic_mask};
|
||||
|
||||
status = op::v1::Equal().evaluate(outputs, {value, input_max});
|
||||
if (!status)
|
||||
return status;
|
||||
|
||||
status = op::v1::Select().evaluate(output_values, {input_dynamic_mask, output_max, output_values[0]});
|
||||
return status;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace convert
|
||||
bool op::v0::Convert::evaluate(const HostTensorVector& output_values, const HostTensorVector& input_values) const {
|
||||
bool Convert::evaluate(TensorVector& outputs, const TensorVector& inputs) const {
|
||||
OV_OP_SCOPE(v0_Convert_evaluate);
|
||||
OPENVINO_SUPPRESS_DEPRECATED_START
|
||||
OPENVINO_ASSERT(validate_host_tensor_vector(input_values, 1));
|
||||
OPENVINO_ASSERT(validate_host_tensor_vector(output_values, 1));
|
||||
OPENVINO_SUPPRESS_DEPRECATED_END
|
||||
return convert::evaluate_convert(input_values[0], output_values[0]);
|
||||
OPENVINO_ASSERT(outputs.size() == 1);
|
||||
OPENVINO_ASSERT(inputs.size() == 1);
|
||||
|
||||
if (auto& out = outputs[0]) {
|
||||
const auto& in = inputs[0];
|
||||
const auto& in_shape = in.get_shape();
|
||||
out.set_shape(in_shape);
|
||||
|
||||
using namespace ov::element;
|
||||
return IfTypeOf<CONVERT_ET_LIST>::apply<convert::Evaluate>(in.get_element_type(),
|
||||
in,
|
||||
out,
|
||||
shape_size(in_shape));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool op::v0::Convert::has_evaluate() const {
|
||||
bool Convert::has_evaluate() const {
|
||||
OV_OP_SCOPE(v0_Convert_has_evaluate);
|
||||
|
||||
switch (get_input_element_type(0)) {
|
||||
case ngraph::element::u1:
|
||||
case ngraph::element::u4:
|
||||
case ngraph::element::u8:
|
||||
case ngraph::element::u16:
|
||||
case ngraph::element::u32:
|
||||
case ngraph::element::u64:
|
||||
case ngraph::element::i4:
|
||||
case ngraph::element::i8:
|
||||
case ngraph::element::i16:
|
||||
case ngraph::element::i32:
|
||||
case ngraph::element::i64:
|
||||
case ngraph::element::bf16:
|
||||
case ngraph::element::f16:
|
||||
case ngraph::element::f32:
|
||||
case ngraph::element::f64:
|
||||
case ngraph::element::boolean:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch (get_output_element_type(0)) {
|
||||
case ngraph::element::i4:
|
||||
case ngraph::element::i8:
|
||||
case ngraph::element::i16:
|
||||
case ngraph::element::i32:
|
||||
case ngraph::element::i64:
|
||||
case ngraph::element::u1:
|
||||
case ngraph::element::u4:
|
||||
case ngraph::element::u8:
|
||||
case ngraph::element::u16:
|
||||
case ngraph::element::u32:
|
||||
case ngraph::element::u64:
|
||||
case ngraph::element::bf16:
|
||||
case ngraph::element::f16:
|
||||
case ngraph::element::f32:
|
||||
case ngraph::element::f64:
|
||||
case ngraph::element::boolean:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
const auto is_valid_type = [](const element::Type& et) -> bool {
|
||||
switch (et) {
|
||||
case element::boolean:
|
||||
case element::bf16:
|
||||
case element::f16:
|
||||
case element::f32:
|
||||
case element::f64:
|
||||
case element::i4:
|
||||
case element::i8:
|
||||
case element::i16:
|
||||
case element::i32:
|
||||
case element::i64:
|
||||
case element::u1:
|
||||
case element::u4:
|
||||
case element::u8:
|
||||
case element::u16:
|
||||
case element::u32:
|
||||
case element::u64:
|
||||
case element::nf4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
return is_valid_type(get_input_element_type(0)) && is_valid_type(get_output_element_type(0));
|
||||
}
|
||||
|
||||
bool op::v0::Convert::evaluate_lower(ov::TensorVector& output_values) const {
|
||||
return convert::evaluate_bound(this, output_values, false);
|
||||
bool Convert::evaluate_lower(TensorVector& output_values) const {
|
||||
return convert::evaluate_bound(this, output_values, get_input_tensor(0).get_lower_value());
|
||||
}
|
||||
|
||||
bool op::v0::Convert::evaluate_upper(ov::TensorVector& output_values) const {
|
||||
return convert::evaluate_bound(this, output_values, true);
|
||||
bool Convert::evaluate_upper(TensorVector& output_values) const {
|
||||
return convert::evaluate_bound(this, output_values, get_input_tensor(0).get_upper_value());
|
||||
}
|
||||
|
||||
bool op::v0::Convert::evaluate_label(TensorLabelVector& output_labels) const {
|
||||
bool Convert::evaluate_label(TensorLabelVector& output_labels) const {
|
||||
const auto input_labels = get_input_tensor(0).get_value_label();
|
||||
if (input_labels.empty())
|
||||
if (input_labels.empty()) {
|
||||
return false;
|
||||
output_labels[0] = input_labels;
|
||||
return true;
|
||||
} else {
|
||||
output_labels[0] = input_labels;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} // namespace v0
|
||||
} // namespace op
|
||||
} // namespace ov
|
||||
Reference in new issue
Block a user