diff --git a/cmake/developer_package/ncc_naming_style/openvino.style b/cmake/developer_package/ncc_naming_style/openvino.style index 6608795381e4a1..141f02b9ab5808 100644 --- a/cmake/developer_package/ncc_naming_style/openvino.style +++ b/cmake/developer_package/ncc_naming_style/openvino.style @@ -18,7 +18,7 @@ VariableReference: '^\w+$' EnumName: '^[A-Z][\w]+$' # excepts element_type -EnumConstantName: '^([A-Z\d_]+|undefined|dynamic|boolean|bf16|f16|f32|f64|i4|i8|i16|i32|i64|u1|u4|u8|u16|u32|u64|nf4|f8e4m3|f8e5m2|string|asymmetric|align_corners|round_prefer_floor|round_prefer_ceil|floor|ceil|simple|nearest|linear|linear_onnx|cubic|area|scales|sizes|half_pixel|tf_half_pixel_for_nn|pytorch_half_pixel|asymetric)$' +EnumConstantName: '^([A-Z\d_]+|undefined|dynamic|boolean|bf16|f16|f32|f64|i4|i8|i16|i32|i64|u1|u2|u3|u4|u6|u8|u16|u32|u64|nf4|f8e4m3|f8e5m2|string|asymmetric|align_corners|round_prefer_floor|round_prefer_ceil|floor|ceil|simple|nearest|linear|linear_onnx|cubic|area|scales|sizes|half_pixel|tf_half_pixel_for_nn|pytorch_half_pixel|asymetric)$' # TODO: align UsingDeclaration: '^.*$' TypedefName: '^.*$' diff --git a/src/core/dev_api/openvino/core/type/element_iterator.hpp b/src/core/dev_api/openvino/core/type/element_iterator.hpp new file mode 100644 index 00000000000000..331bd3684e576e --- /dev/null +++ b/src/core/dev_api/openvino/core/type/element_iterator.hpp @@ -0,0 +1,502 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/core/type/element_type_traits.hpp" + +namespace ov { +namespace util { + +/** + * @brief Make bit mask by setting N less significant bits. + * + * @tparam T Type of value. + * @param n Number of bits to set. + * @return Bit-mask value with N bits set. + */ +template +constexpr T make_n_bit_mask(const T n) { + return (1ULL << n) - 1ULL; +} +} // namespace util + +namespace element { + +/** + * @brief Checks if element type is N in-raw bits type. + * + * @param et Element type to check + * @return True if element type is bit type otherwise false. + */ +constexpr bool is_bit_type(Type_t et) { + return et == u1 || et == u2; +} + +/** + * @brief Checks if element type is 4-bits type. + * + * @param et Element type to check + * @return True if element type is nibble type otherwise false. + */ +constexpr bool is_nibble_type(Type_t et) { + return et == u4 || et == i4 || et == nf4; +} + +/** + * @brief Checks if element type is split bit type. + * + * The value is stored in byte(s) like [b0, b1, x, .., x, b2, b3]. + * + * @param et Element type to check + * @return True if element type is split bit type otherwise false. + */ +constexpr bool is_split_bit_type(Type_t et) { + return et == u3 || et == u6; +} + +/** + * @brief Checks element type is using only N bytes as value. + * + * @param et Element type to check. + * @return True if element type use byte(s) for its value, false otherwise. + */ +constexpr bool is_byte_type(Type_t et) { + return !is_bit_type(et) && !is_split_bit_type(et) && !is_nibble_type(et) && et != string; +} + +/** + * @brief Gets bit width of ov::element::Type_t. + * + * @return Number of bits representing the Type_t. + */ +template +constexpr size_t bit_width() { + return sizeof(typename ov::fundamental_type_for()); +} + +template <> +constexpr size_t bit_width() { + return 1; +} + +template <> +constexpr size_t bit_width() { + return 2; +} + +template <> +constexpr size_t bit_width() { + return 3; +} + +template <> +constexpr size_t bit_width() { + return 4; +} + +template <> +constexpr size_t bit_width() { + return 4; +} + +template <> +constexpr size_t bit_width() { + return 6; +} + +/** + * @brief The BitProxy value class used by ov::element::Iterator to access values which has no standard byte(s) layout. + * + * It used by iterator to access values represented by precisions like u2, i4, u6 etc. in the way like stored + * on bytes. + * The R/W access is done via conversion and copy assignment operators. + * The public members are used to work on sub-byte value like on its fundamental type defined by T. + * + * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. + * @tparam ET OpenVINO element type. + * @tparam Enable Type to enable/disable this class. + */ +template +class BitProxy {}; + +/** + * @brief The BitProxy specialization for types which are represented by N in-raw bits in byte. + * + * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. + * @tparam ET OpenVINO element type. + */ +template +class BitProxy::type> { +private: + template + friend class Iterator; //!< Iterator class is friend to access private members to manipulate pointer. + + static constexpr size_t m_bits = bit_width(); //!< Number of bit for single value. + static constexpr size_t m_num_values = 8 / m_bits; //!< Number values in byte. + static constexpr size_t m_shift_init = is_nibble_type(ET) ? 0 : 8 - m_bits; //!< Initial value for bit shift. + + T* m_ptr; //!< Pointer to T used to get value. + size_t m_bit_shift; //!< Current bit shift to get value. + + constexpr BitProxy(T* ptr) noexcept : m_ptr{ptr}, m_bit_shift{m_shift_init} {} + + uint8_t get_bit_value() const { + constexpr auto value_mask = util::make_n_bit_mask(m_bits); + return (*m_ptr >> m_bit_shift) & value_mask; + } + +public: + using value_type = typename std::decay::type; //!< Fundamental type of bound to BitProxy. + + /** + * @brief Compare proxy value with other provided value. + * @param rhs Value to compare. + * @return True if equal otherwise false. + */ + template + constexpr bool operator==(const U& rhs) const { + return static_cast(*this) == rhs; + } + + /** + * @brief Compare proxy value is less than rhs. + * + * @tparam U Type of value to compare. + * @param rhs Value to compare. + * @return True if less otherwise false. + */ + template + constexpr bool operator<(const U& rhs) const { + return static_cast(*this) < rhs; + } + + /** + * @brief Converts to fundamental type. + * + * @return Value of BitProxy. + */ + template ::type* = nullptr> + operator value_type() const { + return static_cast(get_bit_value()); + } + + /** + * @brief Converts to fundamental type. + * + * @return Value of BitProxy. + */ + template ::type* = nullptr> + operator value_type() const { + constexpr auto value_mask = util::make_n_bit_mask(m_bits); + constexpr auto value_msb_mask = (1U << (m_bits - 1U)); + + auto v = get_bit_value(); + if (v & value_msb_mask) { + // If N bit value MSB bit is set then value is negative. + // As v is byte then all bits above N must be set to be two's complement. + v |= ~value_mask; + } + return static_cast(v); + } + + /** + * @brief Sets current ProxyBit to value. + * @param v Value to be set. + */ + BitProxy& operator=(const value_type v) { + constexpr auto value_mask = util::make_n_bit_mask(m_bits); + *m_ptr &= ~(value_mask << m_bit_shift); + *m_ptr |= (static_cast(v) & value_mask) << m_bit_shift; + return *this; + } +}; + +/** + * @brief The BitProxy specialization for u3, u6 precisions. + * + * @note The input pointer must point on buffer which has got 3 * n bytes. + * + * @tparam T Fundamental type of sub-byte value which must be same as fundamental type of element::Type_t. + * @tparam ET OpenVINO element type. + */ +template +class BitProxy::type> { +private: + template + friend class Iterator; //!< Iterator class is friend to access private members to manipulate pointer. + + static constexpr size_t m_bits = bit_width(); //!< Number of bit for single value. + static constexpr size_t m_num_values = (3 * 8) / m_bits; //!< Number values in byte. + static constexpr size_t m_shift_init = m_num_values - 1; //!< Initial value for bit shift. + + struct ByteValue { + uint8_t b0; + uint8_t b1; + uint8_t b2; + }; + + union { + T* m_ptr; //!< Pointer to T buffer. + ByteValue* m_bytes; //!< Pointer to buffer as 3 bytes representation. + }; + + size_t m_bit_shift; //!< Current bit shift to get value. + + constexpr BitProxy(T* ptr) noexcept : m_ptr{ptr}, m_bit_shift{m_shift_init} {} + +public: + using value_type = typename std::decay::type; //!< Fundamental type of sub-byte. + + /** + * @brief Compare proxy value is equal than rhs. + * + * @tparam U Type of value to compare. + * @param rhs Value to compare. + * @return True if equal, false otherwise. + */ + template + constexpr bool operator==(const U& rhs) const { + return static_cast(*this) == rhs; + } + + /** + * @brief Compare proxy value is less than rhs. + * + * @tparam U Type of value to compare. + * @param rhs Value to compare. + * @return True if less otherwise false. + */ + template + constexpr bool operator<(const U& rhs) const { + return static_cast(*this) < rhs; + } + + /** + * @brief Converts to fundamental type. + * + * @return Value of BitProxy. + */ + operator value_type() const { + constexpr uint16_t lower_mask_bits = 16 / m_num_values; + constexpr uint16_t upper_mask_bits = 8 / m_num_values; + constexpr uint16_t mask_lower = util::make_n_bit_mask(lower_mask_bits); + constexpr uint16_t mask_upper = util::make_n_bit_mask(upper_mask_bits) << lower_mask_bits; + + // get lower part of value + uint16_t v = ((m_bytes->b0 << 8U) | m_bytes->b1) >> (lower_mask_bits * m_bit_shift); + v &= mask_lower; + // get upper part of value + v |= ((m_bytes->b2 << lower_mask_bits) >> (upper_mask_bits * m_bit_shift)) & mask_upper; + return static_cast(v); + } + + /** + * @brief Sets current ProxyBit to value. + * @param v Value to be set. + */ + BitProxy& operator=(const value_type v) { + constexpr uint16_t lower_mask_bits = 16 / m_num_values; + constexpr uint16_t upper_mask_bits = 8 / m_num_values; + constexpr uint16_t mask_lower = util::make_n_bit_mask(lower_mask_bits); + constexpr uint16_t mask_upper = util::make_n_bit_mask(upper_mask_bits) << lower_mask_bits; + + uint16_t tmp = (m_bytes->b0 << 8U) | m_bytes->b1; + tmp &= ~(mask_lower << (lower_mask_bits * m_bit_shift)); + tmp |= (v & mask_lower) << (lower_mask_bits * m_bit_shift); + m_bytes->b0 = tmp >> 8U; + m_bytes->b1 = tmp & 0x00ff; + + tmp = m_bytes->b2 & ~((mask_upper >> lower_mask_bits) << (upper_mask_bits * m_bit_shift)); + tmp |= (((v & mask_upper) >> lower_mask_bits) << (upper_mask_bits * m_bit_shift)); + m_bytes->b2 = tmp & 0x00ff; + return *this; + } +}; + +/** + * @brief Put BitProxy value to output stream. + * + * @param os Reference to output stream. + * @param value Value to print. + * @return return output stream. + */ +template +std::ostream& operator<<(std::ostream& os, const BitProxy& value) { + os << +static_cast(value); + return os; +} + +/** + * @brief Bidirectional iterator of specified precision. + * + * The iterator supports low precisions using BitProxy to access values via conversion. + * + * @tparam ET Type of OpenVINO element type (ov::element::Type_t). + * @tparam T Must be fundamental type for specified ET. + */ +template +class Iterator { + using proxy_type = BitProxy; + +public: + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using reference = typename std::conditional::value, const proxy_type&, proxy_type&>::type; + using pointer = typename std::conditional::value, const proxy_type*, proxy_type*>::type; + + static_assert(std::is_same::type, ov::fundamental_type_for>::value, + "Iterator value_type must be same as fundamental type of ET"); + + constexpr Iterator(T* ptr) noexcept : m_et_ptr{ptr} {} + + // Iteration operators + template + typename std::enable_if>::type& operator++() { + m_et_ptr.m_bit_shift -= m_et_ptr.m_bits; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % (m_et_ptr.m_num_values * m_et_ptr.m_bits); + m_et_ptr.m_ptr += static_cast(m_et_ptr.m_bit_shift == m_et_ptr.m_shift_init); + return *this; + } + + template + typename std::enable_if>::type& operator++() { + m_et_ptr.m_bit_shift ^= m_et_ptr.m_bits; + m_et_ptr.m_ptr += static_cast(m_et_ptr.m_bit_shift == m_et_ptr.m_shift_init); + return *this; + } + + template + typename std::enable_if>::type& operator++() { + --m_et_ptr.m_bit_shift; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % m_et_ptr.m_num_values; + m_et_ptr.m_ptr += (m_et_ptr.m_bit_shift == m_et_ptr.m_shift_init) ? 3 : 0; + return *this; + } + + Iterator operator++(int) { + auto old = *this; + ++(*this); + return old; + } + + template + typename std::enable_if>::type& operator+=(const difference_type& n) { + const auto advance = n + (m_et_ptr.m_shift_init - m_et_ptr.m_bit_shift) / m_et_ptr.m_bits; + m_et_ptr.m_bit_shift = m_et_ptr.m_shift_init - (advance % m_et_ptr.m_num_values) * m_et_ptr.m_bits; + m_et_ptr.m_ptr += advance / m_et_ptr.m_num_values; + return *this; + } + + template + typename std::enable_if>::type& operator+=(const difference_type& n) { + m_et_ptr.m_ptr += n / m_et_ptr.m_num_values; + return (n % m_et_ptr.m_num_values) ? ++*this : *this; + } + + template + typename std::enable_if>::type& operator+=(const difference_type& n) { + const auto advance = n + m_et_ptr.m_shift_init - m_et_ptr.m_bit_shift; + m_et_ptr.m_bit_shift = m_et_ptr.m_shift_init - (advance % m_et_ptr.m_num_values); + m_et_ptr.m_ptr += 3 * (advance / m_et_ptr.m_num_values); + return *this; + } + + Iterator operator+(const difference_type& n) { + auto tmp(*this); + tmp += n; + return tmp; + } + + template + typename std::enable_if>::type& operator--() { + m_et_ptr.m_bit_shift += m_et_ptr.m_bits; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % (m_et_ptr.m_num_values * m_et_ptr.m_bits); + m_et_ptr.m_ptr -= static_cast(m_et_ptr.m_bit_shift == 0); + return *this; + } + + template + typename std::enable_if>::type& operator--() { + m_et_ptr.m_bit_shift ^= m_et_ptr.m_bits; + m_et_ptr.m_ptr -= static_cast(m_et_ptr.m_bit_shift == 4); + return *this; + } + + template + typename std::enable_if>::type& operator--() { + ++m_et_ptr.m_bit_shift; + m_et_ptr.m_bit_shift = m_et_ptr.m_bit_shift % m_et_ptr.m_num_values; + m_et_ptr.m_ptr -= m_et_ptr.m_bit_shift == 0 ? 3 : 0; + return *this; + } + + Iterator operator--(int) { + auto old = *this; + --(*this); + return old; + } + + template + typename std::enable_if>::type& operator-=(const difference_type& n) { + const auto advance = m_et_ptr.m_bit_shift / m_et_ptr.m_bits + n; + m_et_ptr.m_bit_shift = (advance % m_et_ptr.m_num_values) * m_et_ptr.m_bits; + m_et_ptr.m_ptr -= advance / m_et_ptr.m_num_values; + return *this; + } + + template + typename std::enable_if>::type& operator-=(const difference_type& n) { + m_et_ptr.m_ptr -= n / m_et_ptr.m_num_values; + return (n % m_et_ptr.m_num_values) ? --*this : *this; + } + + template + typename std::enable_if>::type& operator-=(const difference_type& n) { + const auto advance = m_et_ptr.m_bit_shift + n; + m_et_ptr.m_bit_shift = advance % m_et_ptr.m_num_values; + m_et_ptr.m_ptr -= 3 * (advance / m_et_ptr.m_num_values); + return *this; + } + + Iterator operator-(const difference_type& n) { + auto tmp(*this); + tmp -= n; + return tmp; + } + + // compare operators + constexpr bool operator!=(const Iterator& rhs) const { + return (m_et_ptr.m_ptr != rhs.m_et_ptr.m_ptr) || (m_et_ptr.m_bit_shift != rhs.m_et_ptr.m_bit_shift); + } + + // dereference operators + constexpr const proxy_type& operator*() const { + return m_et_ptr; + } + + reference operator*() { + return m_et_ptr; + } + +private: + proxy_type m_et_ptr; +}; + +/** + * @brief Make element iterator from pointer. + * + * @tparam ET Type of ov::element::Type_t. + * @tparam T Type of pointer data. Must be fundamental type of ET. + + * @param ptr Pointer to data. + * @return Element iterator for type ET. + */ +template ::type* = nullptr> +constexpr Iterator iterator(T* ptr) { + return {ptr}; +} +} // namespace element +} // namespace ov diff --git a/src/core/include/openvino/core/type/element_type.hpp b/src/core/include/openvino/core/type/element_type.hpp index 39833797c3a663..531a8ccfcb5b6c 100644 --- a/src/core/include/openvino/core/type/element_type.hpp +++ b/src/core/include/openvino/core/type/element_type.hpp @@ -48,7 +48,10 @@ enum class Type_t { i32, //!< i32 element type i64, //!< i64 element type u1, //!< binary element type + u2, //!< u2 element type + u3, //!< u3 element type u4, //!< u4 element type + u6, //!< u6 element type u8, //!< u8 element type u16, //!< u16 element type u32, //!< u32 element type @@ -168,9 +171,18 @@ constexpr Type i64(Type_t::i64); /// \brief binary element type /// \ingroup ov_element_cpp_api constexpr Type u1(Type_t::u1); +/// \brief u2 element type +/// \ingroup ov_element_cpp_api +constexpr Type u2(Type_t::u2); +/// \brief u3 element type +/// \ingroup ov_element_cpp_api +constexpr Type u3(Type_t::u3); /// \brief u4 element type /// \ingroup ov_element_cpp_api constexpr Type u4(Type_t::u4); +/// \brief u6 element type +/// \ingroup ov_element_cpp_api +constexpr Type u6(Type_t::u6); /// \brief u8 element type /// \ingroup ov_element_cpp_api constexpr Type u8(Type_t::u8); diff --git a/src/core/include/openvino/core/type/element_type_traits.hpp b/src/core/include/openvino/core/type/element_type_traits.hpp index c47bae8a13914c..94f3c25372eb5e 100644 --- a/src/core/include/openvino/core/type/element_type_traits.hpp +++ b/src/core/include/openvino/core/type/element_type_traits.hpp @@ -68,11 +68,26 @@ struct element_type_traits { using value_type = int8_t; }; +template <> +struct element_type_traits { + using value_type = int8_t; +}; + +template <> +struct element_type_traits { + using value_type = int8_t; +}; + template <> struct element_type_traits { using value_type = int8_t; }; +template <> +struct element_type_traits { + using value_type = int8_t; +}; + template <> struct element_type_traits { using value_type = uint8_t; diff --git a/src/core/include/openvino/op/constant.hpp b/src/core/include/openvino/op/constant.hpp index ae539351058574..97d56098b89e4c 100644 --- a/src/core/include/openvino/op/constant.hpp +++ b/src/core/include/openvino/op/constant.hpp @@ -146,6 +146,9 @@ class OPENVINO_API Constant : public Op { case Type_t::string: fill_data(value); break; + case Type_t::u2: + case Type_t::u3: + case Type_t::u6: case Type_t::undefined: case Type_t::dynamic: OPENVINO_THROW("unsupported type"); @@ -872,6 +875,9 @@ class OPENVINO_API Constant : public Op { case Type_t::string: write_buffer(source); break; + case element::Type_t::u2: + case element::Type_t::u3: + case element::Type_t::u6: case element::Type_t::undefined: case element::Type_t::dynamic: OPENVINO_THROW("unsupported type"); diff --git a/src/core/src/pass/visualize_tree.cpp b/src/core/src/pass/visualize_tree.cpp index 7a24338a447f32..bf9e040683e102 100644 --- a/src/core/src/pass/visualize_tree.cpp +++ b/src/core/src/pass/visualize_tree.cpp @@ -373,7 +373,10 @@ static std::string get_value(const std::shared_ptr& consta case ov::element::Type_t::undefined: case ov::element::Type_t::dynamic: case ov::element::Type_t::u1: + case ov::element::Type_t::u2: + case ov::element::Type_t::u3: case ov::element::Type_t::u4: + case ov::element::Type_t::u6: case ov::element::Type_t::nf4: case ov::element::Type_t::i4: case ov::element::Type_t::f8e4m3: diff --git a/src/core/src/type/element_type.cpp b/src/core/src/type/element_type.cpp index 088a6a2367e1c2..8a529d6d0e678d 100644 --- a/src/core/src/type/element_type.cpp +++ b/src/core/src/type/element_type.cpp @@ -59,8 +59,14 @@ inline TypeInfo get_type_info(ov::element::Type_t type) { return {64, false, true, false, "int64_t", "i64"}; case ov::element::Type_t::u1: return {1, false, false, false, "uint1_t", "u1"}; + case ov::element::Type_t::u2: + return {2, false, false, false, "uint2_t", "u2"}; + case ov::element::Type_t::u3: + return {3, false, false, false, "uint3_t", "u3"}; case ov::element::Type_t::u4: return {4, false, false, false, "uint4_t", "u4"}; + case ov::element::Type_t::u6: + return {6, false, false, false, "uint6_t", "u6"}; case ov::element::Type_t::u8: return {8, false, false, true, "uint8_t", "u8"}; case ov::element::Type_t::u16: @@ -103,8 +109,14 @@ ov::element::Type type_from_string(const std::string& type) { return ::ov::element::Type(::ov::element::Type_t::i64); } else if (type == "u1" || type == "U1" || type == "BIN" || type == "bin") { return ::ov::element::Type(::ov::element::Type_t::u1); + } else if (type == "u2" || type == "U2") { + return ::ov::element::Type(::ov::element::Type_t::u2); + } else if (type == "u3" || type == "U3") { + return ::ov::element::Type(::ov::element::Type_t::u3); } else if (type == "u4" || type == "U4") { return ::ov::element::Type(::ov::element::Type_t::u4); + } else if (type == "u6" || type == "U6") { + return ::ov::element::Type(::ov::element::Type_t::u6); } else if (type == "u8" || type == "U8") { return ::ov::element::Type(::ov::element::Type_t::u8); } else if (type == "u16" || type == "U16") { @@ -135,11 +147,11 @@ ov::element::Type type_from_string(const std::string& type) { std::vector ov::element::Type::get_known_types() { std::vector rc = { - &ov::element::dynamic, &ov::element::boolean, &ov::element::bf16, &ov::element::f16, &ov::element::f32, - &ov::element::f64, &ov::element::i4, &ov::element::i8, &ov::element::i16, &ov::element::i32, - &ov::element::i64, &ov::element::u1, &ov::element::u4, &ov::element::u8, &ov::element::u16, - &ov::element::u32, &ov::element::u64, &ov::element::nf4, &ov::element::f8e4m3, &ov::element::f8e5m2, - &ov::element::string}; + &ov::element::dynamic, &ov::element::boolean, &ov::element::bf16, &ov::element::f16, &ov::element::f32, + &ov::element::f64, &ov::element::i4, &ov::element::i8, &ov::element::i16, &ov::element::i32, + &ov::element::i64, &ov::element::u1, &ov::element::u2, &ov::element::u3, &ov::element::u4, + &ov::element::u6, &ov::element::u8, &ov::element::u16, &ov::element::u32, &ov::element::u64, + &ov::element::nf4, &ov::element::f8e4m3, &ov::element::f8e5m2, &ov::element::string}; return rc; } @@ -163,7 +175,10 @@ ov::element::Type::Type(size_t bitwidth, {ov::element::Type_t::i32, {32, false, true, true, "int32_t", "i32"}}, {ov::element::Type_t::i64, {64, false, true, false, "int64_t", "i64"}}, {ov::element::Type_t::u1, {1, false, false, false, "uint1_t", "u1"}}, + {ov::element::Type_t::u2, {2, false, false, false, "uint2_t", "u2"}}, + {ov::element::Type_t::u3, {3, false, false, false, "uint3_t", "u3"}}, {ov::element::Type_t::u4, {4, false, false, false, "uint4_t", "u4"}}, + {ov::element::Type_t::u6, {6, false, false, false, "uint6_t", "u6"}}, {ov::element::Type_t::u8, {8, false, false, true, "uint8_t", "u8"}}, {ov::element::Type_t::u16, {16, false, false, false, "uint16_t", "u16"}}, {ov::element::Type_t::u32, {32, false, false, false, "uint32_t", "u32"}}, @@ -304,8 +319,14 @@ Type fundamental_type_for(const Type& type) { return from::value_type>(); case Type_t::u1: return from::value_type>(); + case Type_t::u2: + return from::value_type>(); + case Type_t::u3: + return from::value_type>(); case Type_t::u4: return from::value_type>(); + case Type_t::u6: + return from::value_type>(); case Type_t::u8: return from::value_type>(); case Type_t::u16: @@ -415,7 +436,10 @@ inline size_t compiler_byte_size(ov::element::Type_t et) { ET_CASE(i32); ET_CASE(i64); ET_CASE(u1); + ET_CASE(u2); + ET_CASE(u3); ET_CASE(u4); + ET_CASE(u6); ET_CASE(u8); ET_CASE(u16); ET_CASE(u32); @@ -451,7 +475,10 @@ OPENVINO_API EnumNames& EnumNames::get() { {"i32", element::Type_t::i32}, {"i64", element::Type_t::i64}, {"u1", element::Type_t::u1}, + {"u2", element::Type_t::u2}, + {"u3", element::Type_t::u3}, {"u4", element::Type_t::u4}, + {"u6", element::Type_t::u6}, {"u8", element::Type_t::u8}, {"u16", element::Type_t::u16}, {"u32", element::Type_t::u32}, diff --git a/src/core/tests/element_iterator_test.cpp b/src/core/tests/element_iterator_test.cpp new file mode 100644 index 00000000000000..cfe4b164c7c0c8 --- /dev/null +++ b/src/core/tests/element_iterator_test.cpp @@ -0,0 +1,478 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/core/type/element_iterator.hpp" + +#include + +#include + +#include "openvino/runtime/tensor.hpp" + +namespace ov { +namespace test { + +using testing::ElementsAre; +using testing::ElementsAreArray; + +namespace { +constexpr size_t get_buffer_size(const size_t bit_width, const size_t num_of_elements) { + return (num_of_elements * bit_width + 7) / 8; +} +} // namespace + +// bits number in comments are counted [b7, b6, ..., b0] +// ---- u1 +TEST(ElementIteratorTest, write_u1_data) { + constexpr auto elements_count = 16; + auto input = std::array{0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + EXPECT_THAT(output, ElementsAre(0x16, 0xB3)); +} + +TEST(ElementIteratorTest, read_const_u1_data) { + constexpr auto elements_count = 16; + constexpr auto input = std::array{0x21, static_cast(0xa3)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1)); +} + +TEST(ElementIteratorTest, read_non_const_u1_data) { + constexpr auto elements_count = 16; + auto input = std::array{0x21, static_cast(0xa3)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1)); +} + +TEST(ElementIteratorTest, read_u1_data_increment_decrement_iterator) { + auto input = std::array{0x32, static_cast(0xa3), 0x55}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, 1); // 2nd byte bit7 + EXPECT_EQ(*iter++, 0); // 1st byte bit0 + EXPECT_EQ(*++iter, 0); // 2nd byte bit6 + EXPECT_EQ(*iter--, 0); // 2nd byte bit6 + EXPECT_EQ(*iter, 1); // 2nd byte bit7 +} + +TEST(ElementIteratorTest, read_u1_data_iterator_with_offset) { + auto input = std::array{0x32, static_cast(0xa3), 0x41}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, 1); // 2nd byte bit7 + EXPECT_EQ(*(iter - 2), 1); // 1st byte bit1 + EXPECT_EQ(*(iter - 5), 1); // 1st byte bit4 + EXPECT_EQ(*(iter + 1), 0); // 2nd byte bit6 + EXPECT_EQ(*(iter + 8), 0); // 3rd byte bit7 + EXPECT_EQ(*(iter + 9), 1); // 3rd byte bit6 + EXPECT_EQ(*std::prev(iter, 1), 0); // 1st byte bit0 + EXPECT_EQ(*std::next(iter, 2), 1); // 2nd byte bit5 +} + +TEST(ElementIteratorTest, read_u1_from_tensor) { + auto input = std::array{0x32, static_cast(0xa3), 0x41, 0x11}; + auto t = ov::Tensor(element::u1, Shape{2, 16}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u1))); + + EXPECT_THAT( + std::vector(iter, iter + t.get_size()), + ElementsAre(0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)); +} + +TEST(ElementIteratorTest, u1_value_to_output_stream) { + constexpr auto value = static_cast(0x80); + auto iter = element::iterator(&value); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "1"); +} + +// ---- u2 +TEST(ElementIteratorTest, write_u2_data) { + constexpr auto elements_count = 16; + auto input = std::array{2, 0, 1, 3, 0, 0, 3, 3, 1, 2, 1, 2, 3, 2, 1, 0}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x87, 0x0f, 0x66, 0xe4)); +} + +TEST(ElementIteratorTest, read_const_u2_data) { + constexpr auto elements_count = 16; + constexpr auto input = std::array{static_cast(0x87), + 0x0f, + 0x66, + static_cast(0xe4)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 0, 1, 3, 0, 0, 3, 3, 1, 2, 1, 2, 3, 2, 1, 0)); +} + +TEST(ElementIteratorTest, read_non_const_u2_data) { + constexpr auto elements_count = 16; + auto input = std::array{static_cast(0x87), + 0x0f, + 0x66, + static_cast(0xe4)}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 0, 1, 3, 0, 0, 3, 3, 1, 2, 1, 2, 3, 2, 1, 0)); +} + +TEST(ElementIteratorTest, read_u2_data_increment_decrement_iterator) { + auto input = std::array{0x33, static_cast(0x93)}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, 2); // 2nd byte 1st half-nibble + EXPECT_EQ(*iter++, 3); // 1st byte 4th half-nibble + EXPECT_EQ(*++iter, 1); // 2nd byte 2nd half-nibble + EXPECT_EQ(*iter--, 1); // 2nd byte 2nd half-nibble + EXPECT_EQ(*--iter, 3); // 1st byte 4th half-nibble +} + +TEST(ElementIteratorTest, read_u2_data_iterator_with_offset) { + auto input = std::array{0x43, static_cast(0x93), 0x41}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, 2); // 2nd byte 1st half-nibble + EXPECT_EQ(*(iter - 3), 0); // 1st byte 2nd half-nibble + EXPECT_EQ(*(iter - 4), 1); // 1st byte 1st half-nibble + EXPECT_EQ(*(iter + 1), 1); // 2nd byte 2nd half-nibble + EXPECT_EQ(*(iter + 7), 1); // 3rd byte 4th half-nibble + EXPECT_EQ(*std::prev(iter, 1), 3); // 1st byte 4th half-nibble + EXPECT_EQ(*std::next(iter, 2), 0); // 2nd byte 3rd half-nibble +} + +TEST(ElementIteratorTest, u2_value_to_output_stream) { + constexpr auto value = static_cast(0x80); + auto iter = element::iterator(&value); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "2"); +} + +TEST(ElementIteratorTest, read_u2_from_tensor) { + auto input = std::array{0x32, static_cast(0xa3), 0x41, 0x11}; + auto t = ov::Tensor(element::u2, Shape{4, 4}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u2))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), + ElementsAre(0, 3, 0, 2, 2, 2, 0, 3, 1, 0, 0, 1, 0, 1, 0, 1)); +} + +// --- u3 +TEST(ElementIteratorTest, write_u3_data) { + constexpr auto elements_count = 8; + auto input = std::array{2, 3, 0, 1, 4, 5, 6, 7}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0b10110001, 0b00011011, 0b00001111)); +} + +TEST(ElementIteratorTest, read_non_const_u3_data) { + constexpr auto elements_count = 16; + auto input = std::array{0x7a, 0x6f, 0x55, static_cast(0xb1), 0x1b, 0x0f}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(1, 7, 2, 6, 1, 6, 3, 7, 2, 3, 0, 1, 4, 5, 6, 7)); +} + +TEST(ElementIteratorTest, read_const_u3_data) { + constexpr auto elements_count = 8; + constexpr auto input = std::array{static_cast(0b10110001), 0b00011011, 0b00001111}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), ElementsAre(2, 3, 0, 1, 4, 5, 6, 7)); +} + +TEST(ElementIteratorTest, read_u3_data_iterator_with_offset) { + // Has values {1, 7, 2, 6, 1, 6, 3, 7, [2], 3, 0, 1, 4, 5, 6, 7} + auto input = std::array{0x7a, 0x6f, 0x55, static_cast(0xb1), 0x1b, 0x0f}; + auto iter = element::iterator(input.data() + 3); + + EXPECT_EQ(*iter, 2); + EXPECT_EQ(*(iter - 3), 6); + EXPECT_EQ(*(iter - 4), 1); + EXPECT_EQ(*(iter - 5), 6); + EXPECT_EQ(*(iter + 1), 3); + EXPECT_EQ(*(iter + 5), 5); + EXPECT_EQ(*(iter + 7), 7); + EXPECT_EQ(*std::prev(iter, 1), 7); + EXPECT_EQ(*std::next(iter, 2), 0); +} + +TEST(ElementIteratorTest, read_u3_from_tensor) { + // Has values {1, 7, 2, 6, 1, 6, 3, 7, [2], 3, 0, 1, 4, 5, 6, 7} + auto input = std::array{0x7a, 0x6f, 0x55, static_cast(0xb1), 0x1b, 0x0f}; + auto t = ov::Tensor(element::u3, Shape{4, 2, 2}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u3))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), + ElementsAre(1, 7, 2, 6, 1, 6, 3, 7, 2, 3, 0, 1, 4, 5, 6, 7)); +} + +// --- u4 +// nibbles are counted as [n1, n0] +TEST(ElementIteratorTest, write_u4_data) { + constexpr auto elements_count = 16; + auto input = std::array{1, 2, 3, 10, 12, 15, 14, 4, 7, 9, 11, 13, 8, 0, 5, 6}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x21, 0xa3, 0xfc, 0x4e, 0x97, 0xdb, 0x08, 0x65)); +} + +TEST(ElementIteratorTest, read_const_u4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + constexpr auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 1, 10, 3, 15, 12, 4, 14, 9, 7, 13, 11, 8, 0, 6, 5)); +} + +TEST(ElementIteratorTest, read_non_const_u4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 1, 10, 3, 15, 12, 4, 14, 9, 7, 13, 11, 8, 0, 6, 5)); +} + +TEST(ElementIteratorTest, read_u4_data_increment_decrement_iterator) { + auto input = std::array{0x12, 0x3a}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, 10); // 2nd byte 1st nibble + EXPECT_EQ(*iter++, 1); // 1st byte 2nd nibble + EXPECT_EQ(*++iter, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*iter--, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*--iter, 1); // 1st byte 2nd nibble +} + +TEST(ElementIteratorTest, read_u4_data_iterator_with_offset) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, 10); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 2), 2); // 1st byte 1st nibble + EXPECT_EQ(*(iter + 7), 5); // 5th byte 2nd nibble + EXPECT_EQ(*(iter + 6), 11); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::prev(iter, 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::next(iter, 2), 1); // 3rd byte 1st nibble +} + +TEST(ElementIteratorTest, read_u4_from_tensor) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto t = ov::Tensor(element::u4, Shape{5, 2}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u4))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(2, 4, 10, 3, 1, 6, 9, 7, 11, 5)); +} + +// --- i4 +// nibbles are counted as [n1, n0] +TEST(ElementIteratorTest, write_i4_data) { + constexpr auto elements_count = 16; + auto input = std::array{1, 2, 3, -6, -4, -1, -2, 4, 7, -7, -5, -3, -8, 0, 5, 6}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x21, 0xa3, 0xfc, 0x4e, 0x97, 0xdb, 0x08, 0x65)); +} + +TEST(ElementIteratorTest, read_const_i4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + constexpr auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 1, -6, 3, -1, -4, 4, -2, -7, 7, -3, -5, -8, 0, 6, 5)); +} + +TEST(ElementIteratorTest, read_non_const_i4_data) { + constexpr auto elements_count = 16; + constexpr auto byte_size = get_buffer_size(4, elements_count); + auto input = std::array{0x12, + 0x3a, + static_cast(0xcf), + static_cast(0xe4), + 0x79, + static_cast(0xbd), + 0x08, + 0x56}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), + ElementsAre(2, 1, -6, 3, -1, -4, 4, -2, -7, 7, -3, -5, -8, 0, 6, 5)); +} + +TEST(ElementIteratorTest, read_i4_data_increment_decrement_iterator) { + auto input = std::array{0x12, 0x3a}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter--, -6); // 2nd byte 1st nibble + EXPECT_EQ(*iter++, 1); // 1st byte 2nd nibble + EXPECT_EQ(*++iter, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*iter--, 3); // 2nd byte 2nd nibble + EXPECT_EQ(*--iter, 1); // 1st byte 2nd nibble +} + +TEST(ElementIteratorTest, read_i4_data_iterator_with_offset) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto iter = element::iterator(input.data() + 1); + + EXPECT_EQ(*iter, -6); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 2), 2); // 1st byte 1st nibble + EXPECT_EQ(*(iter + 7), 5); // 5th byte 2nd nibble + EXPECT_EQ(*(iter + 6), -5); // 2nd byte 1st nibble + EXPECT_EQ(*(iter - 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::prev(iter, 1), 4); // 1st byte 2nd nibble + EXPECT_EQ(*std::next(iter, 2), 1); // 3rd byte 1st nibble +} + +TEST(ElementIteratorTest, i4_value_to_output_stream) { + constexpr auto value = static_cast(0x19); + auto iter = element::iterator(&value); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "-7"); +} + +TEST(ElementIteratorTest, read_i4_from_tensor) { + auto input = std::array{0x42, 0x3a, 0x61, 0x79, 0x5b}; + auto t = ov::Tensor(element::i4, Shape{10, 1, 1}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::i4))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(2, 4, -6, 3, 1, 6, -7, 7, -5, 5)); +} + +// --- u6 +TEST(ElementIteratorTest, write_u6_data) { + constexpr auto elements_count = 8; + auto input = std::array{2, 1, 0, 3, 18, 49, 35, 16}; + auto output = std::array{}; + auto iter = element::iterator(output.data()); + + std::copy(input.begin(), input.end(), iter); + + EXPECT_THAT(output, ElementsAre(0x21, 0x03, 0x00, 0x21, 0x30, 0x79)); +} + +TEST(ElementIteratorTest, read_non_const_u6_data) { + constexpr auto elements_count = 8; + auto input = std::array{0x21, 0x03, 0x00, 0x21, 0x30, 0x79}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), ElementsAre(2, 1, 0, 3, 18, 49, 35, 16)); +} + +TEST(ElementIteratorTest, read_const_u6_data) { + constexpr auto elements_count = 8; + constexpr auto input = std::array{0x21, 0x03, 0x00, 0x21, 0x30, 0x79}; + auto iter = element::iterator(input.data()); + + EXPECT_THAT(std::vector(iter, iter + elements_count), ElementsAre(2, 1, 0, 3, 18, 49, 35, 16)); +} + +TEST(ElementIteratorTest, read_u6_data_increment_decrement_iterator) { + // Has values {1, 2, 3, 10, [3], 8, 7, 2} + auto input = std::array{0x12, 0x3a, 0x00, 0x38, 0x72, 0x00}; + auto iter = element::iterator(input.data() + 3); + + EXPECT_EQ(*iter--, 3); + EXPECT_EQ(*iter++, 10); + EXPECT_EQ(*++iter, 8); + EXPECT_EQ(*iter--, 8); + EXPECT_EQ(*--iter, 10); +} + +TEST(ElementIteratorTest, read_u6_data_iterator_with_offset) { + // Has values {1, 2, 3, 10, [3], 8, 7, 2, 1, 42, 4, 20} + auto input = std::array{0x12, 0x3a, 0x00, 0x38, 0x72, 0x00, 0x1a, 0x44, 0x21}; + auto iter = element::iterator(input.data() + 3); + + EXPECT_EQ(*iter, 3); + EXPECT_EQ(*(iter - 3), 2); + EXPECT_EQ(*(iter - 4), 1); + EXPECT_EQ(*(iter - 2), 3); + EXPECT_EQ(*(iter + 1), 8); + EXPECT_EQ(*(iter + 5), 42); + EXPECT_EQ(*(iter + 7), 20); + EXPECT_EQ(*std::prev(iter, 1), 10); + EXPECT_EQ(*std::next(iter, 2), 7); +} + +TEST(ElementIteratorTest, u6_value_to_output_stream) { + auto input = std::array{0x12, 0x3a, 0x00}; + auto iter = element::iterator(input.data()); + + std::stringstream s; + s << *iter; + + EXPECT_EQ(s.str(), "1"); +} + +TEST(ElementIteratorTest, read_u6_from_tensor) { + // Has values {1, 2, 3, 10, 3, 8, 7, 2, 1, 42, 4, 20} + auto input = std::array{0x12, 0x3a, 0x00, 0x38, 0x72, 0x00, 0x1a, 0x44, 0x21}; + auto t = ov::Tensor(element::u6, Shape{4, 1, 3}, input.data()); + auto iter = element::iterator(static_cast(t.data(element::u6))); + + EXPECT_THAT(std::vector(iter, iter + t.get_size()), ElementsAre(1, 2, 3, 10, 3, 8, 7, 2, 1, 42, 4, 20)); +} + +} // namespace test +} // namespace ov diff --git a/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp b/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp index 366026070e0cb9..199a44f9c28a71 100644 --- a/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp +++ b/src/plugins/intel_cpu/src/nodes/executors/type_mask.hpp @@ -81,9 +81,10 @@ struct TypeMask { CASE(f8e4m3) CASE(f8e5m2) CASE(string) + default: + return _undefined; } #undef CASE - return _undefined; } }; diff --git a/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp b/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp index 48d596ab3ba0e9..a4837187b29a6a 100644 --- a/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp +++ b/src/plugins/intel_gpu/tests/unit/test_cases/hash_key_gpu_test.cpp @@ -255,7 +255,7 @@ class check_hash_value: public ::testing::Test { const auto primitive_hash = primitve->hash(); const auto params_hash = prim_inst->get_impl_params()->hash(); ASSERT_EQ(primitive_hash, 4135863035456568493UL); - ASSERT_EQ(params_hash, 5990757629995899044UL); + ASSERT_EQ(params_hash, 11563701278302723583UL); } };