From 15ff4e65967ae64197bbceb8ce5f2bab86d31ca4 Mon Sep 17 00:00:00 2001 From: Pawel Raasz Date: Mon, 4 Dec 2023 10:57:47 +0100 Subject: [PATCH] [core]Correct Constant creation from string (#21099) * Improve Constant creation from string values * Optimize Constant creation from vector binary size reduction * Fix code style * Add tests to check there is no precision los * Fix conversion for string -> integral numbers --------- Co-authored-by: Michal Lukaszewski --- src/core/include/openvino/op/constant.hpp | 25 ++++++++--- src/core/src/op/constant.cpp | 53 +++++++++++++---------- src/core/tests/constant.cpp | 48 ++++++++++++++++++++ 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/src/core/include/openvino/op/constant.hpp b/src/core/include/openvino/op/constant.hpp index 2033b5b9de7..fe91da44baf 100644 --- a/src/core/include/openvino/op/constant.hpp +++ b/src/core/include/openvino/op/constant.hpp @@ -70,19 +70,22 @@ public: template Constant(const element::Type& type, const Shape& shape, const std::vector& values) : Constant(false, type, shape) { + const auto this_shape_size = shape_size(m_shape); + const auto values_size = values.size(); + const auto has_single_value = (values_size == 1); NODE_VALIDATION_CHECK(this, - values.size() == 1 || values.size() == shape_size(m_shape), + has_single_value || values_size == this_shape_size, "Did not get the expected number of literals for a constant of shape ", m_shape, " (got ", - values.size(), + values_size, ", expected ", - (shape_size(m_shape) == 1 ? "" : "1 or "), - shape_size(m_shape), + (this_shape_size == 1 ? "" : "1 or "), + this_shape_size, ")."); - if (values.size() == 1) { - fill_data(type, values.front()); + if (has_single_value) { + fill_data(type, values[0]); } else { write_values(values); } @@ -890,6 +893,16 @@ private: # pragma GCC diagnostic pop #endif } + + template + void fill_or_write(const bool fill, const element::Type& et, const std::vector& values) { + if (fill) { + fill_data(et, values[0]); + } else { + write_values(values); + } + } + template from_string_vector(const std::vector& str_values) { - std::vector values; +template ::value>::type* = nullptr> +T str_to_value(const std::string& s, size_t* pos) { + return static_cast(std::is_signed::value ? std::stoll(s, pos) : std::stoull(s, pos)); +} + +template ::value>::type* = nullptr> +T str_to_value(const std::string& s, size_t* pos) { + return static_cast(std::stod(s, pos)); +} + +template +std::vector from_string_vector(const std::vector& str_values) { + std::vector values; values.reserve(str_values.size()); std::transform(str_values.cbegin(), str_values.cend(), std::back_inserter(values), [](const std::string& s) { size_t pos; - auto v = std::stold(s, &pos); + auto v = str_to_value(s, &pos); OPENVINO_ASSERT(s.size() == pos, "Could not parse literal '", s, "'"); return v; }); @@ -97,33 +108,30 @@ Constant::Constant(const Tensor& tensor) Constant::Constant(const element::Type& type, const Shape& shape, const std::vector& values) : Constant(false, type, shape) { + const auto this_shape_size = shape_size(m_shape); + const auto values_size = values.size(); + const auto has_single_value = (values_size == 1); NODE_VALIDATION_CHECK(this, - values.size() == 1 || values.size() == shape_size(m_shape), + has_single_value || values_size == this_shape_size, "Did not get the expected number of literals for a constant of shape ", m_shape, " (got ", - values.size(), + values_size, ", expected ", - (shape_size(m_shape) == 1 ? "" : "1 or "), - shape_size(m_shape), + (this_shape_size == 1 ? "" : "1 or "), + this_shape_size, ")."); + const auto is_checked_and_identical = has_single_value && (this_shape_size != 1); if (type == element::string) { - if (values.size() == 1) { - fill_data(type, values.front()); - } else { - write_values(values); - } + fill_or_write(is_checked_and_identical, type, values); + } else if (type.is_real()) { + fill_or_write(is_checked_and_identical, type, from_string_vector(values)); + } else if (type.is_signed()) { + fill_or_write(is_checked_and_identical, type, from_string_vector(values)); } else { - auto parsed_values = from_string_vector(values); - if (parsed_values.size() == 1) { - fill_data(type, parsed_values.front()); - } else { - write_values(parsed_values); - } + fill_or_write(is_checked_and_identical, type, from_string_vector(values)); } - const auto is_checked_and_identical = (values.size() == 1) && (shape_size(m_shape) != 1); - update_identical_flags(is_checked_and_identical, is_checked_and_identical); } Constant::Constant(const element::Type& type, const Shape& shape) : Constant(true, type, shape) {} @@ -385,10 +393,11 @@ bool Constant::evaluate(TensorVector& outputs, const TensorVector& inputs) const outputs.emplace_back(m_element_type, m_shape); else outputs[0].set_shape(m_shape); + if (m_element_type == ov::element::string) { auto num_elements = shape_size(m_shape); - const std::string* src_strings = static_cast(get_data_ptr()); - std::string* dst_strings = static_cast(outputs[0].data()); + auto src_strings = static_cast(get_data_ptr()); + auto dst_strings = static_cast(outputs[0].data()); std::copy_n(src_strings, num_elements, dst_strings); } else { std::memcpy(outputs[0].data(), get_data_ptr(), outputs[0].get_byte_size()); diff --git a/src/core/tests/constant.cpp b/src/core/tests/constant.cpp index f0f69d49517..1481f94f0e8 100644 --- a/src/core/tests/constant.cpp +++ b/src/core/tests/constant.cpp @@ -701,6 +701,29 @@ TEST(constant, int64_vector_broadcast) { EXPECT_EQ(p[3], 1); } +TEST(constant, int64_string_max) { + Shape shape{4}; + vector input{"9223372036854775807", "9223372036854775807", "9223372036854775807", "9223372036854775807"}; + + constexpr auto exp_value = std::numeric_limits::max(); + ov::op::v0::Constant c(element::i64, shape, input); + auto v = c.get_vector(); + ASSERT_EQ(v.size(), shape_size(shape)); + EXPECT_THAT(v, testing::Each(exp_value)); + + const auto p = c.get_data_ptr(); + EXPECT_EQ(p[0], exp_value); + EXPECT_EQ(p[1], exp_value); + EXPECT_EQ(p[2], exp_value); + EXPECT_EQ(p[3], exp_value); + + EXPECT_EQ(input, c.get_value_strings()); + + for (unsigned i = 0; i != input.size(); ++i) { + EXPECT_EQ(input[i], c.convert_value_to_string(i)); + } +} + // // uint1 // @@ -1184,6 +1207,31 @@ TEST(constant, uint64_vector_broadcast) { EXPECT_EQ(p[3], 1); } +TEST(constant, uint64_string_max) { + Shape shape{4}; + vector input{"18446744073709551615", + "18446744073709551615", + "18446744073709551615", + "18446744073709551615"}; + ov::op::v0::Constant c(element::u64, shape, input); + constexpr auto exp_value = std::numeric_limits::max(); + auto v = c.get_vector(); + ASSERT_EQ(v.size(), shape_size(shape)); + EXPECT_THAT(v, testing::Each(exp_value)); + + const auto p = c.get_data_ptr(); + EXPECT_EQ(p[0], exp_value); + EXPECT_EQ(p[1], exp_value); + EXPECT_EQ(p[2], exp_value); + EXPECT_EQ(p[3], exp_value); + + EXPECT_EQ(input, c.get_value_strings()); + + for (unsigned i = 0; i != input.size(); ++i) { + EXPECT_EQ(input[i], c.convert_value_to_string(i)); + } +} + // // bfloat16 //