diff --git a/src/common/transformations/tests/control_flow/unroll_if_test.cpp b/src/common/transformations/tests/control_flow/unroll_if_test.cpp index 9c3ac5ea677802..28a2315ca60c0c 100644 --- a/src/common/transformations/tests/control_flow/unroll_if_test.cpp +++ b/src/common/transformations/tests/control_flow/unroll_if_test.cpp @@ -23,9 +23,15 @@ #include "transformations/init_node_info.hpp" #include "transformations/rt_info/fused_names_attribute.hpp" -using namespace ov; using namespace testing; +namespace ov { +namespace test { +using op::v0::Constant; +using op::v0::Parameter; +using op::v0::Result; +using op::v1::Add; + std::shared_ptr get_then_body() { auto Xt = std::make_shared(ov::element::f32, ov::Shape{3}); Xt->set_friendly_name("Xt"); @@ -350,3 +356,60 @@ TEST(TransformationTests, UnrollIfInsideIf) { auto res = compare_functions(f, f_ref); ASSERT_TRUE(res.first) << res.second; } + +TEST(TransformationTests, UnrollIfToParameterResultModel) { + constexpr auto et = element::f32; + std::shared_ptr model, model_ref; + + { + const auto a = std::make_shared(et, PartialShape{5, 7}); + const auto b = std::make_shared(et, PartialShape{1}); + const auto c = std::make_shared(et, PartialShape{5, 7}); + + const auto then_add = std::make_shared(a, b); + auto then_result = std::make_shared(then_add); + auto else_result = std::make_shared(c); + + const auto then_body = std::make_shared(OutputVector{then_result}, ParameterVector{a, b}); + const auto else_body = std::make_shared(OutputVector{else_result}, ParameterVector{c}); + + const auto if_input_0 = std::make_shared(et, a->get_output_partial_shape(0)); + const auto if_input_1 = std::make_shared(et, b->get_output_partial_shape(0)); + const auto condition = Constant::create(element::boolean, {1}, {false}); + const auto if_op = std::make_shared(condition); + if_op->set_then_body(then_body); + if_op->set_else_body(else_body); + if_op->set_input(if_input_0, a, c); + if_op->set_input(if_input_1, b, nullptr); + const auto if_result = if_op->set_output(then_result, else_result); + + const auto results = ResultVector{std::make_shared(if_result)}; + model = std::make_shared(results, ParameterVector{if_input_0, if_input_1}, "simple_if"); + model->input(0).set_names({"Input.0"}); + model->input(1).set_names({"Input.1"}); + model->output(0).set_names({"Output"}); + + pass::Manager manager; + manager.register_pass(); + manager.register_pass(); + manager.run_passes(model); + + OV_ASSERT_NO_THROW(check_rt_info(model)); + } + { + const auto p = std::make_shared(et, PartialShape{5, 7}); + const auto r = std::make_shared(p); + model_ref = std::make_shared(ResultVector{r}, ParameterVector{p}, "simple_if"); + model_ref->input(0).set_names({"Input.0"}); + model_ref->output(0).set_names({"Output"}); + } + + const auto cmp_result = compare_functions(model, model_ref); + ASSERT_TRUE(cmp_result.first) << cmp_result.second; + + EXPECT_THAT(model->input(0).get_names(), UnorderedElementsAre("Input.0", "Output")); + EXPECT_THAT(model->output(0).get_names(), UnorderedElementsAre("Output")); +} + +} // namespace test +} // namespace ov diff --git a/src/core/dev_api/openvino/core/descriptor_tensor.hpp b/src/core/dev_api/openvino/core/descriptor_tensor.hpp index 9418183a1189fc..cdd9ba4c2bbab8 100644 --- a/src/core/dev_api/openvino/core/descriptor_tensor.hpp +++ b/src/core/dev_api/openvino/core/descriptor_tensor.hpp @@ -1,12 +1,21 @@ // Copyright (C) 2018-2024 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // +#pragma once -#include "openvino/core/descriptor/tensor.hpp" +#include +#include + +#include "openvino/core/partial_shape.hpp" +#include "openvino/core/type/element_type.hpp" namespace ov { namespace descriptor { +class Tensor; +class Input; +class Output; + // To change Tensor element type please change the Parameter type. OPENVINO_API void set_element_type(Tensor& tensor, const element::Type& elemenet_type); @@ -14,5 +23,70 @@ void set_element_type(Tensor& tensor, const element::Type& elemenet_type); // To change Tensor type please change the Parameter type. OPENVINO_API void set_tensor_type(Tensor& tensor, const element::Type& element_type, const PartialShape& pshape); + +/** + * @brief Set destination tensor names as copy of all names from source tensor all tensor names. + * + * @param dst The tensor descriptor to set names. + * @param src The tensor descriptor as from which names will be copied. + */ +OPENVINO_API +void copy_tensor_names(Tensor& dst, const Tensor& src); + +/** @brief Tensor descriptor interface. */ +class OPENVINO_API ITensorDescriptor { +public: + virtual const element::Type& get_element_type() const = 0; + virtual const PartialShape& get_partial_shape() const = 0; + virtual const Shape& get_shape() const = 0; + virtual void set_type_shape(const element::Type& et, const PartialShape& shape) = 0; + + virtual void set_names(const std::unordered_set& names) = 0; + virtual void add_names(const std::unordered_set& names) = 0; + virtual const std::unordered_set& get_names() const = 0; + virtual const std::unordered_set& get_all_names() const = 0; + virtual const std::string& get_any_name() const = 0; + + virtual RTMap& rt_map() = 0; + virtual const RTMap& rt_map() const = 0; + virtual size_t pointer_hash() const noexcept = 0; + +protected: + virtual ~ITensorDescriptor(); +}; + +/** @brief The TensorExtension defines developer API for ov::descriptor::Tensor. */ +struct OPENVINO_API TensorExtension { + /** + * @brief Get the tensor descriptor object + * + * @param tensor Tensor descriptor to access its implementation. + * @return Reference to Tensor description implementation. + */ + static const ITensorDescriptor& get_descriptor(const Tensor& tensor); + static std::shared_ptr& get_descriptor_ptr(Tensor& tensor); + + /** + * @brief The hasher of shared pointer Tensor descriptor. + */ + struct OPENVINO_API Hasher { + size_t operator()(const std::shared_ptr& tensor) const; + }; + + /** + * @brief The comparator of shared pointer Tensor descriptor. + */ + struct OPENVINO_API Equal { + bool operator()(const std::shared_ptr& lhs, const std::shared_ptr& rhs) const; + }; +}; + +/** + * @brief Set input descriptor as shared tensor on output descriptor. + * + * @param output_descriptor Descriptor to set shared tensor. + * @param input_descriptor Input descriptor to set in output as shared tensor. + */ +OPENVINO_API void set_shared_tensor(Output& output_descriptor, const Input& input_descriptor); } // namespace descriptor } // namespace ov diff --git a/src/core/include/openvino/core/descriptor/input.hpp b/src/core/include/openvino/core/descriptor/input.hpp index cbedde6d73a118..2e1335a09ee5c6 100644 --- a/src/core/include/openvino/core/descriptor/input.hpp +++ b/src/core/include/openvino/core/descriptor/input.hpp @@ -13,18 +13,13 @@ namespace ov { class Node; -namespace op { -namespace v0 { -class Result; -} // namespace v0 -} // namespace op + namespace descriptor { class Output; // Describes a tensor that is an input to an op, directly or indirectly via a tuple class OPENVINO_API Input { friend class ov::Node; - friend class ov::op::v0::Result; public: /// \param node The node that owns this input @@ -111,12 +106,6 @@ class OPENVINO_API Input { Input& operator=(const Input&) = default; protected: - /// \return the tensor for the connected output - std::shared_ptr get_tensor_ptr() const; - - /// \return the tensor for the connected output - std::shared_ptr get_tensor_ptr(); - // owner of an argument node (in lieu of m_arguments) std::shared_ptr m_src_node; Node* m_node; // The node we are an input for diff --git a/src/core/include/openvino/core/descriptor/tensor.hpp b/src/core/include/openvino/core/descriptor/tensor.hpp index 9624994d8d612e..13a3826c37f291 100644 --- a/src/core/include/openvino/core/descriptor/tensor.hpp +++ b/src/core/include/openvino/core/descriptor/tensor.hpp @@ -22,99 +22,107 @@ namespace ov { class Node; /// \brief Alias for symbol tensor. using TensorSymbol = std::vector>; -/// \brief Alias for vector of symbol tensors. +/// \brief Alias for vector of symbol tensors. using TensorSymbolVector = std::vector; -namespace pass { -class ReverseShapeAndTypeInfer; -} namespace descriptor { - -class Tensor; +class ITensorDescriptor; /// \brief Compile-time descriptor of a first-class value that is a tensor. class OPENVINO_API Tensor { public: + /// \brief Creates Tensor descriptor + /// \param element_type Element type + /// \param pshape Partial shape of tensor + /// \param names Tensor names (optional default empty). Tensor(const element::Type& element_type, const PartialShape& pshape, const std::unordered_set& names = {}); + + OPENVINO_DEPRECATED("This constructor is deprecated. Will be removed in 2026.0") Tensor(const element::Type& element_type, const PartialShape& pshape, Node* node, size_t node_output_number); Tensor(const Tensor&) = delete; Tensor& operator=(const Tensor&) = delete; + /// \brief Gets any tensor name. + /// Throws if tensor has no names. const std::string& get_any_name() const; + + /// \brief Gets tensor names const std::unordered_set& get_names() const; + + /// \brief Set new names. + /// \param names Names to set. void set_names(const std::unordered_set& names); + + /// \brief Adds new names to tensor. + /// \param names new names to be added. void add_names(const std::unordered_set& names); /// \brief sets lower bound value description void set_lower_value(const ov::Tensor& value); + /// \brief sets upper bound value description void set_upper_value(const ov::Tensor& value); + /// \brief sets value symbol description void set_value_symbol(const TensorSymbol& value_symbol); + /// \brief unsets bound value descriptions void invalidate_values(); - const element::Type& get_element_type() const { - return m_element_type; - } + /// \brief Gets element type. + const element::Type& get_element_type() const; + + /// \brief Gets shape. + /// Throw if Tensor's shape is not static. const Shape& get_shape() const; - const PartialShape& get_partial_shape() const { - return m_partial_shape; - } + + /// \brief Gets partial shape. + const PartialShape& get_partial_shape() const; + /// \brief gets lower bound value description - const ov::Tensor& get_lower_value() const { - return m_lower_value; - } + const ov::Tensor& get_lower_value() const; + /// \brief gets upper bound value description - const ov::Tensor& get_upper_value() const { - return m_upper_value; - } + const ov::Tensor& get_upper_value() const; + /// \brief gets symbol value description - TensorSymbol get_value_symbol() const { - return m_value_symbol; - } + TensorSymbol get_value_symbol() const; + /// \brief checks if lower and upper bound are set and point to the same Tensor - bool has_and_set_bound() const { - return m_upper_value && m_lower_value && m_upper_value.data() == m_lower_value.data(); - } + bool has_and_set_bound() const; + + /// \brief Get Tensor size in bytes. + /// \return Size in bytes. size_t size() const; - RTMap& get_rt_info() { - return m_rt_info; - } - const RTMap& get_rt_info() const { - return m_rt_info; - } + /// \brief Gets runtime informations. + /// \return Runtime information map which can be modified. + RTMap& get_rt_info(); - void clone_from(const Tensor& old); + /// \brief Gets runtime informations. + /// \return Read only runtime information map. + const RTMap& get_rt_info() const; -protected: - element::Type m_element_type; + /// \brief Clones Tensor from the other. + /// \param other Tensor used to clone its properties. + void clone_from(const Tensor& other); - PartialShape m_partial_shape; +protected: ov::Tensor m_lower_value, m_upper_value; TensorSymbol m_value_symbol; - - std::unordered_set m_names; - std::unordered_set::const_iterator m_name_it; - RTMap m_rt_info; - - friend OPENVINO_API void set_element_type(Tensor& tensor, const element::Type& elemenet_type); - friend OPENVINO_API void set_tensor_type(Tensor& tensor, - const element::Type& element_type, - const PartialShape& pshape); + std::shared_ptr m_impl; private: - mutable std::atomic m_shape_changing{false}; - mutable bool m_shape_changed{true}; - mutable Shape m_shape; + // hidden extension API for Tensor descriptor + friend struct TensorExtension; }; OPENVINO_API std::ostream& operator<<(std::ostream&, const ov::descriptor::Tensor&); } // namespace descriptor + } // namespace ov diff --git a/src/core/include/openvino/core/graph_util.hpp b/src/core/include/openvino/core/graph_util.hpp index 66c640a62314df..f5694ca89fee51 100644 --- a/src/core/include/openvino/core/graph_util.hpp +++ b/src/core/include/openvino/core/graph_util.hpp @@ -21,6 +21,10 @@ #include "openvino/op/parameter.hpp" #include "openvino/pass/serialize.hpp" +#ifdef OPENVINO_CPP_VER_17 +# include +#endif + namespace ov { OPENVINO_API @@ -288,27 +292,45 @@ bool replace_node_update_name(const std::shared_ptr& target, const std::sh /// \param bin_path Path where .bin file will be saved (optional). /// The same name as for xml_path will be used by default. /// \param version Version of the generated IR (optional). +/// \{ OPENVINO_API void serialize(const std::shared_ptr& m, const std::string& xml_path, const std::string& bin_path = "", ov::pass::Serialize::Version version = ov::pass::Serialize::Version::UNSPECIFIED); +#ifdef OPENVINO_CPP_VER_17 +template >* = nullptr> +void serialize(const std::shared_ptr& m, + const Path& xml_path, + const Path& bin_path = {""}, + ov::pass::Serialize::Version version = ov::pass::Serialize::Version::UNSPECIFIED) { + serialize(m, xml_path.string(), bin_path.string(), version); +} +#endif +/// \} + /// \brief Save given model into IR. Floating point weights are compressed to FP16 by default. /// This method saves a model to IR applying all necessary transformations that usually applied -/// in model conversion flow provided by mo tool. Paricularly, floatting point weights are compressed to FP16. +/// in model conversion flow provided by mo tool. Particularly, floating point weights are compressed to FP16. /// \param model Model which will be converted to IR representation. /// \param output_model Path to the output model file, must have extension .xml -/// \param compress_to_fp16 Whether to compress floatting point weights to FP16 (true by default) +/// \param compress_to_fp16 Whether to compress floating point weights to FP16 (true by default) OPENVINO_API void save_model(const std::shared_ptr& model, const std::string& output_model, bool compress_to_fp16 = true); - #if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) OPENVINO_API void save_model(const std::shared_ptr& model, const std::wstring& output_model, bool compress_to_fp16 = true); #endif -} // namespace ov \ No newline at end of file + +#ifdef OPENVINO_CPP_VER_17 +template >* = nullptr> +void save_model(const std::shared_ptr& model, const Path& output_model, bool compress_to_fp16 = true) { + save_model(model, output_model.string(), compress_to_fp16); +} +#endif +} // namespace ov diff --git a/src/core/include/openvino/op/result.hpp b/src/core/include/openvino/op/result.hpp index 00e805d1f2aeb5..9cad2d9444a267 100644 --- a/src/core/include/openvino/op/result.hpp +++ b/src/core/include/openvino/op/result.hpp @@ -13,6 +13,51 @@ namespace v0 { /// \brief Result operation. /// /// \ingroup ov_ops_cpp_api +/// +/// The Result output tensor is special, it shares tensor with Result's input but requires to have dedicated properties +/// like: +/// - tensor names. +/// +/// Setting/adding Result's output names modify this specific tensor names. +/// Result's specific tensor names are added to input descriptor and transferred to new descriptor if Result's input +/// has been replaced. +/// +/// Examples 1: No specific names on Result's output +/// +/// set output names: +/// [N1] +/// ↓ +/// |----------------| [names: N1] |-----------------| +/// | Node |--------------------------->| Result | -> Model output names: N1 +/// |----------------| |-----------------| +/// +/// +/// Examples 2: Result's has got specific names +/// +/// set output names: set output names: +/// [N1] [R1, R2] +/// ↓ ↓ +/// |----------------| [names: N1, R1, R2] |-----------------| +/// | Node |--------------------------->| Result | -> Model output names: R1, R2 +/// |----------------| |-----------------| +/// +/// +/// Examples 3: Result from example 2 connected to new node +/// +/// set output names: set output names: +/// [N2] [R1, R2] +/// ↓ ↓ +/// |----------------| [names: N2, R1, R2] |-----------------| +/// | Node |--------------------------->| Result | -> Model output names: R1, R2 +/// |----------------| |-----------------| +/// +/// set output names: +/// [N1] +/// ↓ +/// |----------------| [names: N1] +/// | Node |-----------------> +/// |----------------| +/// class OPENVINO_API Result : public Op { public: OPENVINO_OP("Result", "opset1"); diff --git a/src/core/include/openvino/pass/serialize.hpp b/src/core/include/openvino/pass/serialize.hpp index fc3e743d4005dc..d0eaadde346bf6 100644 --- a/src/core/include/openvino/pass/serialize.hpp +++ b/src/core/include/openvino/pass/serialize.hpp @@ -11,6 +11,10 @@ #include "openvino/opsets/opset.hpp" #include "openvino/pass/pass.hpp" +#ifdef OPENVINO_CPP_VER_17 +# include +#endif + namespace ov { namespace pass { @@ -35,6 +39,13 @@ class OPENVINO_API Serialize : public ov::pass::ModelPass { Serialize(const std::string& xmlPath, const std::string& binPath, Version version = Version::UNSPECIFIED); +#ifdef OPENVINO_CPP_VER_17 + Serialize(const std::filesystem::path& xmlPath, + const std::filesystem::path& binPath, + Version version = Version::UNSPECIFIED) + : Serialize(xmlPath.string(), binPath.string(), version) {} +#endif + private: std::ostream* m_xmlFile; std::ostream* m_binFile; diff --git a/src/core/src/descriptor/input.cpp b/src/core/src/descriptor/input.cpp index 544abd10945806..28288002780739 100644 --- a/src/core/src/descriptor/input.cpp +++ b/src/core/src/descriptor/input.cpp @@ -71,14 +71,6 @@ ov::descriptor::Tensor& ov::descriptor::Input::get_tensor() { return m_output->get_tensor(); } -std::shared_ptr ov::descriptor::Input::get_tensor_ptr() const { - return m_output->get_tensor_ptr(); -} - -std::shared_ptr ov::descriptor::Input::get_tensor_ptr() { - return m_output->get_tensor_ptr(); -} - const ov::Shape& ov::descriptor::Input::get_shape() const { return m_output->get_shape(); } diff --git a/src/core/src/descriptor/shared_tensor.cpp b/src/core/src/descriptor/shared_tensor.cpp new file mode 100644 index 00000000000000..314aa524bcacec --- /dev/null +++ b/src/core/src/descriptor/shared_tensor.cpp @@ -0,0 +1,125 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/core/descriptor/output.hpp" +#include "openvino/core/descriptor_tensor.hpp" +#include "openvino/util/common_util.hpp" + +namespace ov { +namespace descriptor { +/** + * @brief Dedicated tensor descriptor implementation to share input descriptor. + * + * Shared tensor share input tensor but have specific properties: + * - tensor names - if set these are used as descriptor names and appended to input tensor because is same tensor + */ +class SharedTensor : public ITensorDescriptor { +public: + SharedTensor(std::shared_ptr tensor) + : m_shared_tensor{std::move(tensor)}, + m_output_names{}, + m_name_it{} { + OPENVINO_ASSERT(m_shared_tensor, "Cannot set NULL tensor descriptor"); + } + + // --- ITensorDescriptor API + virtual const element::Type& get_element_type() const override { + return m_shared_tensor->get_element_type(); + } + + virtual const PartialShape& get_partial_shape() const override { + return m_shared_tensor->get_partial_shape(); + } + + virtual const Shape& get_shape() const override { + return m_shared_tensor->get_shape(); + } + + virtual void set_type_shape(const element::Type& et, const PartialShape& shape) override { + m_shared_tensor->set_type_shape(et, shape); + } + + void set_names(const std::unordered_set& names) override { + rm_tensor_output_names(); + m_output_names = names; + m_name_it = std::min_element(m_output_names.begin(), m_output_names.end()); + m_shared_tensor->add_names(m_output_names); + } + + void add_names(const std::unordered_set& names) override { + m_output_names.insert(names.begin(), names.end()); + m_name_it = std::min_element(m_output_names.begin(), m_output_names.end()); + m_shared_tensor->add_names(names); + } + + const std::unordered_set& get_names() const override { + return m_output_names.empty() ? m_shared_tensor->get_names() : m_output_names; + } + + const std::unordered_set& get_all_names() const override { + return m_shared_tensor->get_names(); + } + + const std::string& get_any_name() const override { + return m_output_names.empty() ? m_shared_tensor->get_any_name() : *m_name_it; + } + + const RTMap& rt_map() const override { + return m_shared_tensor->rt_map(); + } + + RTMap& rt_map() override { + return m_shared_tensor->rt_map(); + } + + size_t pointer_hash() const noexcept override { + return m_shared_tensor->pointer_hash(); + } + + // --- SharedTensor specific interface + void set_tensor(std::shared_ptr tensor) { + if (tensor != m_shared_tensor) { + OPENVINO_ASSERT(tensor, "Cannot set NULL tensor descriptor"); + rm_tensor_output_names(); + auto prev_rt_map = rt_map(); + + m_shared_tensor = std::move(tensor); + m_shared_tensor->add_names(m_output_names); + rt_map().insert(std::make_move_iterator(prev_rt_map.begin()), std::make_move_iterator(prev_rt_map.end())); + } + } + +private: + void rm_tensor_output_names() { + auto names = m_shared_tensor->get_names(); + for (const auto& output_name : m_output_names) { + names.erase(output_name); + } + + m_shared_tensor->set_names(names); + } + + std::shared_ptr m_shared_tensor; + std::unordered_set m_output_names; + std::unordered_set::const_iterator m_name_it; +}; + +/** + * @brief Set output tensor descriptor with shared tensor from new input. + * + * @param output Output descriptor to be updated. + * @param input Input descriptor to set as shared tensor. + */ +void set_shared_tensor(Output& output, const Input& input) { + auto& output_descriptor = TensorExtension::get_descriptor_ptr(output.get_tensor()); + const auto& input_descriptor = TensorExtension::get_descriptor_ptr(input.get_output().get_tensor()); + if (auto* result_ptr = dynamic_cast(output_descriptor.get())) { + result_ptr->set_tensor(input_descriptor); + } else { + output_descriptor = std::make_shared(input_descriptor); + } +} + +} // namespace descriptor +} // namespace ov diff --git a/src/core/src/descriptor/tensor.cpp b/src/core/src/descriptor/tensor.cpp index ae3f7c6e77cd4f..6e85b25b2b9f8a 100644 --- a/src/core/src/descriptor/tensor.cpp +++ b/src/core/src/descriptor/tensor.cpp @@ -8,26 +8,153 @@ #include "openvino/core/descriptor_tensor.hpp" #include "openvino/core/except.hpp" #include "openvino/core/node.hpp" +#include "openvino/core/type/element_iterator.hpp" #include "openvino/op/util/symbolic_info.hpp" +#include "openvino/util/common_util.hpp" -ov::descriptor::Tensor::Tensor(const element::Type& element_type, - const PartialShape& pshape, - const std::unordered_set& names) - : m_element_type(element_type), - m_partial_shape(pshape) { - set_names(names); +namespace ov { +namespace descriptor { + +/** @brief Helper class to store Tensor shape information.*/ +class ShapeInfo { +public: + ShapeInfo() = default; + ShapeInfo(const PartialShape& shape) : m_partial_shape{shape} {} + + void set_partial_shape(PartialShape shape) { + AtomicGuard lock(m_shape_changing); + m_partial_shape = std::move(shape); + m_shape_changed = true; + } + + const PartialShape& get_partial_shape() const { + return m_partial_shape; + } + + const Shape& get_shape() const { + AtomicGuard lock(m_shape_changing); + if (m_shape_changed) { + m_shape = m_partial_shape.to_shape(); + m_shape_changed = false; + } + return m_shape; + } + +private: + PartialShape m_partial_shape{}; + mutable Shape m_shape{}; + mutable std::atomic m_shape_changing{false}; + mutable bool m_shape_changed{true}; +}; + +// --- Tensor descriptor interface +ITensorDescriptor::~ITensorDescriptor() = default; + +/** @brief Basic tensor descriptor. */ +class BasicTensor : public ITensorDescriptor { +public: + BasicTensor() = default; + + BasicTensor(const element::Type& et, const PartialShape& shape, const std::unordered_set& names) + : m_element_type{et}, + m_shape_info{shape}, + m_names{names}, + m_name_it{find_new_any_name(m_names)}, + m_rt_map{}, + m_legacy_name{} {} + + virtual const element::Type& get_element_type() const override { + return m_element_type; + } + + virtual const PartialShape& get_partial_shape() const override { + return m_shape_info.get_partial_shape(); + } + + virtual const Shape& get_shape() const override { + return m_shape_info.get_shape(); + } + + virtual void set_type_shape(const element::Type& et, const PartialShape& shape) override { + m_element_type = et; + m_shape_info.set_partial_shape(shape); + } + + void set_names(const std::unordered_set& names) override { + m_names = names; + m_name_it = find_new_any_name(m_names); + }; + + void add_names(const std::unordered_set& names) override { + m_names.insert(names.begin(), names.end()); + m_name_it = find_new_any_name(m_names); + } + + const std::unordered_set& get_names() const override { + return m_names; + } + + const std::unordered_set& get_all_names() const override { + return get_names(); + } + + const std::string& get_any_name() const override { + OPENVINO_ASSERT(!get_names().empty(), "Attempt to get a name for a Tensor without names"); + return *m_name_it; + } + + const RTMap& rt_map() const override { + return m_rt_map; + } + + RTMap& rt_map() override { + return m_rt_map; + }; + + size_t pointer_hash() const noexcept override { + return std::hash()(this); + } + +private: + element::Type m_element_type; + ShapeInfo m_shape_info; + std::unordered_set m_names; + std::unordered_set::const_iterator m_name_it; + RTMap m_rt_map; + std::string m_legacy_name; + + static decltype(m_name_it) find_new_any_name(const decltype(m_names)& names) { + return std::min_element(names.begin(), names.end()); + } +}; + +// --- TensorExtension +const ITensorDescriptor& TensorExtension::get_descriptor(const Tensor& tensor) { + return *tensor.m_impl; } -ov::descriptor::Tensor::Tensor(const element::Type& element_type, - const PartialShape& pshape, - ov::Node* node, - size_t node_output_number) - : m_element_type(element_type), - m_partial_shape(pshape) { - m_name_it = m_names.cend(); +std::shared_ptr& TensorExtension::get_descriptor_ptr(Tensor& tensor) { + return tensor.m_impl; } -void ov::descriptor::Tensor::invalidate_values() { +bool TensorExtension::Equal::operator()(const std::shared_ptr& lhs, const std::shared_ptr& rhs) const { + return TensorExtension::get_descriptor(*lhs).pointer_hash() == TensorExtension::get_descriptor(*rhs).pointer_hash(); +} + +size_t TensorExtension::Hasher::operator()(const std::shared_ptr& tensor) const { + return get_descriptor(*tensor).pointer_hash(); +} + +// --- Tensor +Tensor::Tensor(const element::Type& element_type, + const PartialShape& pshape, + const std::unordered_set& names) + : m_impl(std::make_shared(element_type, pshape, names)) {} + +Tensor::Tensor(const element::Type& element_type, const PartialShape& pshape, ov::Node* node, size_t) + : m_impl(std::make_shared(element_type, pshape, std::unordered_set{})) {} + +void Tensor::invalidate_values() { if (ov::skip_invalidation(*this)) return; m_upper_value = {}; @@ -35,110 +162,110 @@ void ov::descriptor::Tensor::invalidate_values() { m_value_symbol.clear(); } -void ov::descriptor::Tensor::set_lower_value(const ov::Tensor& value) { +void Tensor::set_lower_value(const ov::Tensor& value) { OPENVINO_ASSERT(static_cast(value)); - OPENVINO_ASSERT(m_partial_shape.same_scheme(value.get_shape())); - OPENVINO_ASSERT(m_element_type == value.get_element_type()); + OPENVINO_ASSERT(get_partial_shape().same_scheme(value.get_shape())); + OPENVINO_ASSERT(get_element_type() == value.get_element_type()); m_lower_value = value; } -void ov::descriptor::Tensor::set_upper_value(const ov::Tensor& value) { +void Tensor::set_upper_value(const ov::Tensor& value) { OPENVINO_ASSERT(static_cast(value)); - OPENVINO_ASSERT(m_partial_shape.same_scheme(value.get_shape())); - OPENVINO_ASSERT(m_element_type == value.get_element_type()); + OPENVINO_ASSERT(get_partial_shape().same_scheme(value.get_shape())); + OPENVINO_ASSERT(get_element_type() == value.get_element_type()); m_upper_value = value; } -void ov::descriptor::Tensor::set_value_symbol(const TensorSymbol& value_symbol) { +void Tensor::set_value_symbol(const TensorSymbol& value_symbol) { const auto& symbols_size = value_symbol.size(); if (symbols_size == 0) { m_value_symbol.clear(); } else { - OPENVINO_ASSERT(m_partial_shape.is_static()); - OPENVINO_ASSERT(shape_size(m_partial_shape.to_shape()) == symbols_size); + OPENVINO_ASSERT(get_partial_shape().is_static()); + OPENVINO_ASSERT(shape_size(get_partial_shape().to_shape()) == symbols_size); m_value_symbol = value_symbol; } } -const ov::Shape& ov::descriptor::Tensor::get_shape() const { - AtomicGuard lock(m_shape_changing); - if (m_shape_changed) { - m_shape = m_partial_shape.to_shape(); - m_shape_changed = false; - } - return m_shape; +const ov::Tensor& Tensor::get_lower_value() const { + return m_lower_value; } -size_t ov::descriptor::Tensor::size() const { - const bool bitwidth_less_than_byte = m_element_type.bitwidth() < 8; - return bitwidth_less_than_byte ? (shape_size(get_shape()) * m_element_type.bitwidth() + 7) >> 3 - : (shape_size(get_shape()) * m_element_type.size()); +const ov::Tensor& Tensor::get_upper_value() const { + return m_upper_value; } -const std::unordered_set& ov::descriptor::Tensor::get_names() const { - return m_names; +TensorSymbol Tensor::get_value_symbol() const { + return m_value_symbol; } -const std::string& ov::descriptor::Tensor::get_any_name() const { - if (m_name_it == m_names.cend()) { - OPENVINO_THROW("Attempt to get a name for a Tensor without names"); - } - return *m_name_it; +bool Tensor::has_and_set_bound() const { + return m_upper_value && m_lower_value && m_upper_value.data() == m_lower_value.data(); } -void ov::descriptor::Tensor::set_names(const std::unordered_set& names) { - m_names = names; - m_name_it = m_names.cbegin(); - for (auto it = m_names.cbegin(); it != m_names.cend(); it++) { - if (*it < *m_name_it) - // Update any name - m_name_it = it; - } +const element::Type& Tensor::get_element_type() const { + return m_impl->get_element_type(); } -void ov::descriptor::Tensor::add_names(const std::unordered_set& names) { - for (const auto& name : names) { - auto res = m_names.insert(name); - if (m_name_it == m_names.end() || *res.first < *m_name_it) - // Update any name - m_name_it = res.first; - } +const PartialShape& Tensor::get_partial_shape() const { + return m_impl->get_partial_shape(); +} +const Shape& Tensor::get_shape() const { + return m_impl->get_shape(); } -void ov::descriptor::Tensor::clone_from(const ov::descriptor::Tensor& old) { - { - AtomicGuard lock(m_shape_changing); - m_partial_shape = old.get_partial_shape(); - m_shape_changed = true; - } - set_names(old.get_names()); - m_element_type = old.get_element_type(); - m_lower_value = old.get_lower_value(); - m_upper_value = old.get_upper_value(); - m_value_symbol = old.get_value_symbol(); - m_rt_info = old.get_rt_info(); +size_t Tensor::size() const { + return element::get_memory_size(get_element_type(), shape_size(get_shape())); } -void ov::descriptor::set_tensor_type(ov::descriptor::Tensor& tensor, - const element::Type& element_type, - const PartialShape& pshape) { - tensor.m_element_type = element_type; - AtomicGuard lock(tensor.m_shape_changing); - tensor.m_partial_shape = pshape; - tensor.m_shape_changed = true; +const std::unordered_set& Tensor::get_names() const { + return m_impl->get_names(); } -void ov::descriptor::set_element_type(ov::descriptor::Tensor& tensor, const element::Type& element_type) { - tensor.m_element_type = element_type; +const RTMap& Tensor::get_rt_info() const { + return m_impl->rt_map(); } -std::ostream& ov::descriptor::operator<<(std::ostream& out, const ov::descriptor::Tensor& tensor) { - std::string names; - for (const auto& name : tensor.get_names()) { - if (!names.empty()) - names += ", "; - names += name; - } - out << "Tensor(" << names << ")"; +RTMap& Tensor::get_rt_info() { + return m_impl->rt_map(); +} + +const std::string& Tensor::get_any_name() const { + return m_impl->get_any_name(); +} + +void Tensor::set_names(const std::unordered_set& names) { + m_impl->set_names(names); +} + +void Tensor::add_names(const std::unordered_set& names) { + m_impl->add_names(names); +} + +void Tensor::clone_from(const Tensor& other) { + m_impl->set_type_shape(other.get_element_type(), other.get_partial_shape()); + set_names(other.get_names()); + m_lower_value = other.get_lower_value(); + m_upper_value = other.get_upper_value(); + m_value_symbol = other.get_value_symbol(); + get_rt_info() = other.get_rt_info(); +} + +void set_tensor_type(Tensor& tensor, const element::Type& element_type, const PartialShape& pshape) { + TensorExtension::get_descriptor_ptr(tensor)->set_type_shape(element_type, pshape); +} + +void set_element_type(Tensor& tensor, const element::Type& element_type) { + TensorExtension::get_descriptor_ptr(tensor)->set_type_shape(element_type, tensor.get_partial_shape()); +} + +void copy_tensor_names(Tensor& dst, const Tensor& src) { + dst.set_names(TensorExtension::get_descriptor(src).get_all_names()); +} + +std::ostream& operator<<(std::ostream& out, const Tensor& tensor) { + out << "Tensor(" << util::join(tensor.get_names()) << ")"; return out; } +} // namespace descriptor +} // namespace ov diff --git a/src/core/src/node.cpp b/src/core/src/node.cpp index ec9197a5a337cb..689e1c80af12a0 100644 --- a/src/core/src/node.cpp +++ b/src/core/src/node.cpp @@ -155,8 +155,8 @@ std::shared_ptr ov::Node::copy_with_new_inputs( for (auto& cdep : control_dependencies) { clone->add_control_dependency(cdep); } - for (size_t i = 0; i < get_output_size(); i++) { - clone->get_output_tensor(i).set_names(get_output_tensor(i).get_names()); + for (size_t i = 0; i < get_output_size(); ++i) { + descriptor::copy_tensor_names(clone->get_output_tensor(i), get_output_tensor(i)); } return clone; } @@ -218,9 +218,8 @@ ov::descriptor::Input& ov::Node::get_input_descriptor(size_t position) { ov::descriptor::Output& ov::Node::get_output_descriptor(size_t position) { while (m_outputs.size() <= position) { - size_t i = m_outputs.size(); - auto tensor_descriptor = make_shared(element::dynamic, PartialShape::dynamic(), this, i); - m_outputs.emplace_back(this, i, tensor_descriptor); + const auto i = m_outputs.size(); + m_outputs.emplace_back(this, i, make_shared(element::dynamic, PartialShape::dynamic())); } return m_outputs[position]; } @@ -468,8 +467,8 @@ ov::descriptor::Tensor& ov::Node::get_output_tensor(size_t i) const { ov::descriptor::Tensor& ov::Node::get_input_tensor(size_t i) const { OPENVINO_ASSERT(i < m_inputs.size(), idx_txt, i, out_of_range_txt); - descriptor::Input input = m_inputs[i]; - return input.get_tensor(); + auto& input = m_inputs[i]; + return input.get_output().get_tensor(); } size_t ov::Node::get_input_size() const { diff --git a/src/core/src/op/result.cpp b/src/core/src/op/result.cpp index 237d6bd7a2084a..97dc95a0e53f17 100644 --- a/src/core/src/op/result.cpp +++ b/src/core/src/op/result.cpp @@ -9,6 +9,7 @@ #include #include "itt.hpp" +#include "openvino/core/descriptor_tensor.hpp" namespace ov { namespace op { @@ -22,10 +23,8 @@ void Result::validate_and_infer_types() { OV_OP_SCOPE(v0_Result_validate_and_infer_types); NODE_VALIDATION_CHECK(this, get_input_size() == 1, "Argument has ", get_input_size(), " outputs (1 expected)."); - // Result doesn't change change in/out tensors - auto& output = get_output_descriptor(0); - auto& input = get_input_descriptor(0); - output.set_tensor_ptr(input.get_tensor_ptr()); + // Result shares input tensor but can have specific properties which are added/removed to input. + descriptor::set_shared_tensor(get_output_descriptor(0), get_input_descriptor(0)); } std::shared_ptr Result::clone_with_new_inputs(const OutputVector& new_args) const { diff --git a/src/core/src/preprocess/preprocess_impls.cpp b/src/core/src/preprocess/preprocess_impls.cpp index cbe18a78beb575..c2523beed66620 100644 --- a/src/core/src/preprocess/preprocess_impls.cpp +++ b/src/core/src/preprocess/preprocess_impls.cpp @@ -6,6 +6,7 @@ #include "layout_utils.hpp" #include "openvino/core/descriptor_tensor.hpp" +#include "openvino/util/common_util.hpp" namespace ov { namespace preprocess { @@ -325,11 +326,9 @@ void InputInfo::InputInfoImpl::dump(std::ostream& str, //----------- OutputInfoImpl ---------- void OutputInfo::OutputInfoImpl::build(ov::ResultVector& results) { - std::shared_ptr result; auto node = m_output_node; - const auto start_out_node_names = node.get_tensor().get_names(); - node.get_tensor().set_names({}); - result = std::dynamic_pointer_cast(node.get_node_shared_ptr()); + const auto result = ov::as_type_ptr(node.get_node_shared_ptr()); + // Set result layout from 'model' information if (get_model_data()->is_layout_set()) { // Overwrite existing model's layout here (fix 74065) @@ -369,49 +368,36 @@ void OutputInfo::OutputInfoImpl::build(ov::ResultVector& results) { node = std::get<0>(action_result); post_processing_applied = true; } - // Restore tensor names - node.get_tensor().set_names(start_out_node_names); + auto orig_parent = result->get_input_source_output(0).get_node_shared_ptr(); - bool reset_orig_friendly_name = false; + // Move result tensor names from previous input to new + const auto result_input_names = result->get_input_tensor(0).get_names(); + result->get_input_tensor(0).set_names({}); + node.get_tensor().set_names(result_input_names); + if (!post_processing_applied) { return; } + if (orig_parent->get_output_size() == 1) { node.get_node_shared_ptr()->set_friendly_name(orig_parent->get_friendly_name()); - reset_orig_friendly_name = true; + + // Reset friendly name of input node to avoid names collision + // when there is at a new node inserted by post-processing steps + // If no new nodes are inserted by post-processing, then we need to preserve friendly name of input + // as it's required for old API correct work + result->get_input_source_output(0).get_node_shared_ptr()->set_friendly_name(""); } else if (node.get_node_shared_ptr() != orig_parent) { // Result node is changed - add "." suffix node.get_node_shared_ptr()->set_friendly_name(orig_parent->get_friendly_name() + "." + std::to_string(result->get_input_source_output(0).get_index())); } - - // Reset friendly name of input node to avoid names collision - // when there is at a new node inserted by post-processing steps - // If no new nodes are inserted by post-processing, then we need to preserve friendly name of input - // as it's required for old API correct work - if (reset_orig_friendly_name) { - result->get_input_source_output(0).get_node_shared_ptr()->set_friendly_name(""); - } - - // Create result - auto new_result = std::make_shared(node); - new_result->set_friendly_name(result->get_friendly_name()); - - // Preserve runtime info of original result - new_result->get_rt_info() = result->get_rt_info(); - new_result->input(0).get_rt_info() = result->input(0).get_rt_info(); - new_result->output(0).get_rt_info() = result->output(0).get_rt_info(); + result->input(0).replace_source_output(node); + result->revalidate_and_infer_types(); // Update layout if (!context.layout().empty()) { - new_result->set_layout(context.layout()); - } - - for (auto& old_result : results) { - if (result == old_result) { - old_result = new_result; - break; - } + result->set_layout(context.layout()); } } @@ -439,7 +425,7 @@ void OutputInfo::OutputInfoImpl::dump(std::ostream& str) const { str << "Output "; if (!start_out_node_names.empty()) { - str << "\"" << *start_out_node_names.begin() << "\""; + str << "\"" << util::join(start_out_node_names) << "\""; } str << ":" << std::endl; str << " Model's data tensor: "; diff --git a/src/core/tests/pass/serialization/deterministicity.cpp b/src/core/tests/pass/serialization/deterministicity.cpp index 8441da501eb9bf..a93f092889d2a1 100644 --- a/src/core/tests/pass/serialization/deterministicity.cpp +++ b/src/core/tests/pass/serialization/deterministicity.cpp @@ -296,6 +296,47 @@ TEST_P(SerializationDeterministicityInputOutputTest, FromIrModel) { EXPECT_TRUE(files_equal(xml_2, xml_1)); } +#ifdef OPENVINO_CPP_VER_17 +TEST_P(SerializationDeterministicityInputOutputTest, FromOvModelBybPath) { + auto irVersion = GetParam(); + + std::shared_ptr modelRef; + { + auto parameter0 = std::make_shared(ov::element::f32, ov::Shape{1, 3, 22, 22}); + parameter0->set_friendly_name("input0"); + auto result0 = std::make_shared(parameter0); + result0->set_friendly_name("output0"); + auto parameter1 = std::make_shared(ov::element::f32, ov::Shape{1, 3, 22, 22}); + parameter1->set_friendly_name("input1"); + auto result1 = std::make_shared(parameter1); + result1->set_friendly_name("output1"); + modelRef = + std::make_shared(ov::NodeVector{result0, result1}, ov::ParameterVector{parameter0, parameter1}); + } + + auto& expected1 = modelRef; + const auto out_xml_path = std::filesystem::path(m_out_xml_path_1); + const auto out_bin_path = std::filesystem::path(m_out_bin_path_1); + ov::pass::Serialize(out_xml_path, out_bin_path, irVersion).run_on_model(modelRef); + auto expected2 = ov::test::readModel(m_out_xml_path_1, m_out_bin_path_1); + + ov::pass::Serialize(m_out_xml_path_2, m_out_bin_path_2, irVersion).run_on_model(expected2); + + EXPECT_EQ(input0Name, expected1->input(0).get_node()->get_friendly_name()); + EXPECT_EQ(input1Name, expected1->input(1).get_node()->get_friendly_name()); + EXPECT_EQ(output0Name, expected1->output(0).get_node()->get_friendly_name()); + EXPECT_EQ(output1Name, expected1->output(1).get_node()->get_friendly_name()); + EXPECT_EQ(input0Name, expected2->input(0).get_node()->get_friendly_name()); + EXPECT_EQ(input1Name, expected2->input(1).get_node()->get_friendly_name()); + EXPECT_EQ(output0Name, expected2->output(0).get_node()->get_friendly_name()); + EXPECT_EQ(output1Name, expected2->output(1).get_node()->get_friendly_name()); + + std::ifstream xml_1(m_out_xml_path_1, std::ios::in | std::ios::binary); + std::ifstream xml_2(m_out_xml_path_2, std::ios::in | std::ios::binary); + EXPECT_TRUE(files_equal(xml_1, xml_2)); +} +#endif + INSTANTIATE_TEST_SUITE_P(DeterministicityInputOutput, SerializationDeterministicityInputOutputTest, ::testing::Values(ov::pass::Serialize::Version::IR_V10, ov::pass::Serialize::Version::IR_V11)); diff --git a/src/core/tests/pass/serialization/serialize.cpp b/src/core/tests/pass/serialization/serialize.cpp index e45d5d1d1434ff..5cb1965feebdd7 100644 --- a/src/core/tests/pass/serialization/serialize.cpp +++ b/src/core/tests/pass/serialization/serialize.cpp @@ -74,6 +74,23 @@ TEST_P(SerializationTest, SaveModel) { }); } +#ifdef OPENVINO_CPP_VER_17 +TEST_P(SerializationTest, CompareFunctionsByPath) { + const auto out_xml_path = std::filesystem::path(m_out_xml_path); + const auto out_bin_path = std::filesystem::path(m_out_bin_path); + CompareSerialized([&out_xml_path, &out_bin_path](const auto& m) { + ov::pass::Serialize(out_xml_path, out_bin_path).run_on_model(m); + }); +} + +TEST_P(SerializationTest, SaveModelByPath) { + const auto out_xml_path = std::filesystem::path(m_out_xml_path); + CompareSerialized([&out_xml_path](const auto& m) { + ov::save_model(m, out_xml_path, false); + }); +} +#endif + INSTANTIATE_TEST_SUITE_P( IRSerialization, SerializationTest, diff --git a/src/core/tests/type_prop/result.cpp b/src/core/tests/type_prop/result.cpp index f0c0eecc285004..9776768df052a0 100644 --- a/src/core/tests/type_prop/result.cpp +++ b/src/core/tests/type_prop/result.cpp @@ -7,8 +7,12 @@ #include "common_test_utils/type_prop.hpp" #include "openvino/op/constant.hpp" -using namespace std; -using namespace ov; +namespace ov { +namespace test { + +using ov::op::v0::Parameter; +using std::make_shared; +using testing::UnorderedElementsAre; TEST(type_prop, result) { const auto arg_shape = Shape{1, 2, 3, 4, 5}; @@ -51,3 +55,101 @@ TEST(type_prop, result_layout_invalid) { result->output(0).get_rt_info()[ov::LayoutAttribute::get_type_info_static()] = "NCHW"; // incorrect way ASSERT_THROW(result->get_layout(), ov::Exception); } + +using TypePropResultV0Test = TypePropOpTest; + +TEST_F(TypePropResultV0Test, set_specific_output_name_by_output) { + auto a = std::make_shared(element::f32, PartialShape::dynamic()); + a->get_output_tensor(0).set_names({"input"}); + + auto result = make_op(a); + + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("input")); + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("input")); + + result->output(0).set_names({"out"}); + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("out")); + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("out")); + EXPECT_THAT(a->output(0).get_names(), UnorderedElementsAre("input", "out")); + EXPECT_THAT(a->get_output_tensor(0).get_names(), UnorderedElementsAre("input", "out")); +} + +TEST_F(TypePropResultV0Test, set_specific_output_name_by_tensor_desc) { + auto a = std::make_shared(element::f32, PartialShape::dynamic()); + a->get_output_tensor(0).set_names({"input"}); + + auto result = make_op(a); + + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("input")); + + result->get_output_tensor(0).set_names({"out"}); + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("out")); + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("out")); + EXPECT_THAT(a->output(0).get_names(), UnorderedElementsAre("input", "out")); + EXPECT_THAT(a->get_output_tensor(0).get_names(), UnorderedElementsAre("input", "out")); +} + +TEST_F(TypePropResultV0Test, change_specific_output_name) { + auto a = std::make_shared(element::f32, PartialShape::dynamic()); + a->get_output_tensor(0).set_names({"input"}); + + auto result = make_op(a); + + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("input")); + + result->get_output_tensor(0).set_names({"out"}); + + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("out")); + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("out")); + EXPECT_THAT(a->output(0).get_names(), UnorderedElementsAre("input", "out")); + EXPECT_THAT(a->get_output_tensor(0).get_names(), UnorderedElementsAre("input", "out")); + + result->output(0).set_names({"new output"}); + + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("new output")); + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("new output")); + EXPECT_THAT(a->output(0).get_names(), UnorderedElementsAre("input", "new output")); + EXPECT_THAT(a->get_output_tensor(0).get_names(), UnorderedElementsAre("input", "new output")); +} + +TEST_F(TypePropResultV0Test, add_specific_output_name) { + auto a = std::make_shared(element::f32, PartialShape::dynamic()); + a->get_output_tensor(0).set_names({"input"}); + + auto result = make_op(a); + + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("input")); + + result->output(0).set_names({"out"}); + result->get_output_tensor(0).add_names({"extra output name", "o1"}); + result->output(0).add_names({"extra output name", "o2"}); + + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("out", "extra output name", "o1", "o2")); + EXPECT_THAT(result->get_output_tensor(0).get_names(), UnorderedElementsAre("out", "extra output name", "o1", "o2")); + EXPECT_THAT(a->output(0).get_names(), UnorderedElementsAre("input", "out", "extra output name", "o1", "o2")); + EXPECT_THAT(a->get_output_tensor(0).get_names(), + UnorderedElementsAre("input", "out", "extra output name", "o1", "o2")); +} + +TEST_F(TypePropResultV0Test, preserve_specific_name_on_input_replace) { + const auto a = std::make_shared(element::f32, PartialShape::dynamic()); + a->get_output_tensor(0).set_names({"input a"}); + + const auto result = make_op(a); + result->output(0).set_names({"out"}); + + EXPECT_THAT(result->input(0).get_tensor().get_names(), UnorderedElementsAre("out", "input a")); + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("out")); + + const auto b = std::make_shared(element::f32, PartialShape::dynamic()); + b->get_output_tensor(0).set_names({"input b"}); + + result->input(0).replace_source_output(b); + result->validate_and_infer_types(); + + EXPECT_THAT(result->input(0).get_tensor().get_names(), UnorderedElementsAre("input b", "out")); + EXPECT_THAT(result->output(0).get_names(), UnorderedElementsAre("out")); + EXPECT_THAT(a->output(0).get_names(), UnorderedElementsAre("input a")); +} +} // namespace test +} // namespace ov diff --git a/src/inference/dev_api/openvino/runtime/isync_infer_request.hpp b/src/inference/dev_api/openvino/runtime/isync_infer_request.hpp index bbe81ee1c9602d..b80bf32958e4ac 100644 --- a/src/inference/dev_api/openvino/runtime/isync_infer_request.hpp +++ b/src/inference/dev_api/openvino/runtime/isync_infer_request.hpp @@ -15,6 +15,7 @@ #include #include "openvino/core/descriptor/tensor.hpp" +#include "openvino/core/descriptor_tensor.hpp" #include "openvino/runtime/common.hpp" #include "openvino/runtime/iinfer_request.hpp" #include "openvino/runtime/profiling_info.hpp" @@ -162,7 +163,11 @@ class OPENVINO_RUNTIME_API ISyncInferRequest : public IInferRequest { private: std::shared_ptr m_compiled_model; // Mutable to return reference to ov::Tensor - mutable std::unordered_map, ov::SoPtr> m_tensors; + mutable std::unordered_map, + ov::SoPtr, + descriptor::TensorExtension::Hasher, + descriptor::TensorExtension::Equal> + m_tensors; // Cache ports mutable std::unordered_map m_cached_ports; mutable std::mutex m_cache_mutex; diff --git a/src/inference/src/dev/icompiled_model.cpp b/src/inference/src/dev/icompiled_model.cpp index f452dd3a330a17..3f4a8d397ab4d9 100644 --- a/src/inference/src/dev/icompiled_model.cpp +++ b/src/inference/src/dev/icompiled_model.cpp @@ -51,8 +51,11 @@ ov::ICompiledModel::ICompiledModel(const std::shared_ptr& model } } } - - std::unordered_map, std::shared_ptr> tensor_map; + std::unordered_map, + std::shared_ptr, + descriptor::TensorExtension::Hasher, + descriptor::TensorExtension::Equal> + tensor_map; for (const auto& param : model->get_parameters()) { const auto& param_name = param->get_friendly_name(); auto new_param = ov::as_type_ptr(param->copy_with_new_inputs({})); diff --git a/src/inference/src/model_reader.cpp b/src/inference/src/model_reader.cpp index aaf620ea0f803a..7babef019b5802 100644 --- a/src/inference/src/model_reader.cpp +++ b/src/inference/src/model_reader.cpp @@ -86,7 +86,7 @@ void update_v10_model(std::shared_ptr& model, bool frontendMode = fal "Model operation names have collisions with tensor names.", " Please use MO to generate new IR version, it should allow to avoid the issue"); leaf_names.emplace(res_name, nullptr); - result->output(0).get_tensor().add_names({std::move(res_name)}); + result->input(0).get_tensor().add_names({std::move(res_name)}); } for (const auto& param : model->get_parameters()) { const auto& param_name = param->get_friendly_name(); diff --git a/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.cpp b/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.cpp index 39a2d20c092835..05a0e0a2cf6a0e 100644 --- a/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.cpp +++ b/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.cpp @@ -1329,6 +1329,58 @@ std::set> jit_logical_and_emitter::get_supported_prec return {{element::f32, element::f32}}; } +/// LOGICAL_OR /// +jit_logical_or_emitter::jit_logical_or_emitter(dnnl::impl::cpu::aarch64::jit_generator* host, + dnnl::impl::cpu::aarch64::cpu_isa_t host_isa, + const std::shared_ptr& node) + : jit_emitter(host, host_isa, node, get_arithmetic_binary_exec_precision(node)) { + prepare_table(); +} + +jit_logical_or_emitter::jit_logical_or_emitter(dnnl::impl::cpu::aarch64::jit_generator* host, + dnnl::impl::cpu::aarch64::cpu_isa_t host_isa, + const ov::element::Type exec_prc) + : jit_emitter(host, host_isa, exec_prc) { + prepare_table(); +} + +size_t jit_logical_or_emitter::get_inputs_count() const { return 2; } + +size_t jit_logical_or_emitter::get_aux_vecs_count() const { return 1; } + +size_t jit_logical_or_emitter::get_aux_gprs_count() const { return 1; } + +void jit_logical_or_emitter::emit_impl(const std::vector &in_vec_idxs, const std::vector &out_vec_idxs) const { + if (host_isa_ == dnnl::impl::cpu::aarch64::asimd) { + emit_isa(in_vec_idxs, out_vec_idxs); + } else { + OV_CPU_JIT_EMITTER_THROW("Can't create jit eltwise kernel"); + } +} + +template +void jit_logical_or_emitter::emit_isa(const std::vector &in_vec_idxs, const std::vector &out_vec_idxs) const { + OV_CPU_JIT_EMITTER_ASSERT(exec_prc_ == ov::element::f32, "unsupported precision: " + exec_prc_.to_string()); + + using TReg = typename dnnl::impl::cpu::aarch64::cpu_isa_traits::TReg; + const TReg src1 = TReg(in_vec_idxs[0]); + const TReg src2 = TReg(in_vec_idxs[1]); + const TReg dst = TReg(out_vec_idxs[0]); + const TReg aux = TReg(aux_vec_idxs[0]); + + h->orr(dst.b16, src1.b16, src2.b16); + h->ld1r(aux.s, table_val2("one")); + h->and_(dst.b16, dst.b16, aux.b16); +} + +void jit_logical_or_emitter::register_table_entries() { + push_arg_entry_of("one", 0x3f800000, true); +} + +std::set> jit_logical_or_emitter::get_supported_precisions(const std::shared_ptr& node) { + return {{element::f32, element::f32}}; +} + /// LOGICAL_NOT /// jit_logical_not_emitter::jit_logical_not_emitter(dnnl::impl::cpu::aarch64::jit_generator* host, dnnl::impl::cpu::aarch64::cpu_isa_t host_isa, diff --git a/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.hpp b/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.hpp index 2173a1487f1057..be4e51cd0b759d 100644 --- a/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.hpp +++ b/src/plugins/intel_cpu/src/emitters/plugin/aarch64/jit_eltwise_emitters.hpp @@ -628,6 +628,34 @@ class jit_logical_and_emitter : public jit_emitter { void register_table_entries() override; }; +class jit_logical_or_emitter : public jit_emitter { +public: + jit_logical_or_emitter(dnnl::impl::cpu::aarch64::jit_generator *host, + dnnl::impl::cpu::aarch64::cpu_isa_t host_isa, + const ov::element::Type exec_prc = ov::element::f32); + + jit_logical_or_emitter(dnnl::impl::cpu::aarch64::jit_generator *host, + dnnl::impl::cpu::aarch64::cpu_isa_t host_isa, + const std::shared_ptr& n); + + size_t get_inputs_count() const override; + + size_t get_aux_vecs_count() const override; + + size_t get_aux_gprs_count() const override; + + static std::set> get_supported_precisions( + const std::shared_ptr& node = nullptr); + +private: + void emit_impl(const std::vector &in_vec_idxs, const std::vector &out_vec_idxs) const override; + + template + void emit_isa(const std::vector &in_vec_idxs, const std::vector &out_vec_idxs) const; + + void register_table_entries() override; +}; + class jit_logical_not_emitter : public jit_emitter { public: jit_logical_not_emitter(dnnl::impl::cpu::aarch64::jit_generator* host, diff --git a/src/plugins/intel_cpu/src/nodes/executors/aarch64/jit_eltwise.cpp b/src/plugins/intel_cpu/src/nodes/executors/aarch64/jit_eltwise.cpp index 0374888e3d7fcb..912fe23fcd1fcf 100644 --- a/src/plugins/intel_cpu/src/nodes/executors/aarch64/jit_eltwise.cpp +++ b/src/plugins/intel_cpu/src/nodes/executors/aarch64/jit_eltwise.cpp @@ -38,6 +38,7 @@ bool JitEltwiseExecutor::isSupported( Algorithm::EltwiseIsNaN, Algorithm::EltwiseLessEqual, Algorithm::EltwiseLogicalAnd, + Algorithm::EltwiseLogicalOr, Algorithm::EltwiseLogicalNot, Algorithm::EltwiseLogicalXor, Algorithm::EltwiseMaximum, diff --git a/src/plugins/intel_cpu/src/nodes/kernels/aarch64/jit_uni_eltwise_generic.cpp b/src/plugins/intel_cpu/src/nodes/kernels/aarch64/jit_uni_eltwise_generic.cpp index cfe36f78cc40f9..b3fe7018d23677 100644 --- a/src/plugins/intel_cpu/src/nodes/kernels/aarch64/jit_uni_eltwise_generic.cpp +++ b/src/plugins/intel_cpu/src/nodes/kernels/aarch64/jit_uni_eltwise_generic.cpp @@ -655,6 +655,7 @@ std::shared_ptr jit_uni_eltwise_generic::create_eltwise_emitte OV_CASE(Algorithm::EltwiseIsInf, ov::intel_cpu::aarch64::jit_is_inf_emitter), OV_CASE(Algorithm::EltwiseLessEqual, ov::intel_cpu::aarch64::jit_less_equal_emitter), OV_CASE(Algorithm::EltwiseLogicalAnd, ov::intel_cpu::aarch64::jit_logical_and_emitter), + OV_CASE(Algorithm::EltwiseLogicalOr, ov::intel_cpu::aarch64::jit_logical_or_emitter), OV_CASE(Algorithm::EltwiseLogicalNot, ov::intel_cpu::aarch64::jit_logical_not_emitter), OV_CASE(Algorithm::EltwiseLogicalXor, ov::intel_cpu::aarch64::jit_logical_xor_emitter), OV_CASE(Algorithm::EltwiseIsNaN, ov::intel_cpu::aarch64::jit_is_nan_emitter), @@ -845,6 +846,7 @@ std::set> eltwise_precision_helper::get_supported_pre OV_CASE(Algorithm::EltwiseIsNaN, jit_is_nan_emitter), OV_CASE(Algorithm::EltwiseLessEqual, jit_less_equal_emitter), OV_CASE(Algorithm::EltwiseLogicalAnd, jit_logical_and_emitter), + OV_CASE(Algorithm::EltwiseLogicalOr, jit_logical_or_emitter), OV_CASE(Algorithm::EltwiseLogicalNot, jit_logical_not_emitter), OV_CASE(Algorithm::EltwiseLogicalXor, jit_logical_xor_emitter), OV_CASE(Algorithm::EltwiseMaximum, jit_maximum_emitter), diff --git a/src/plugins/intel_npu/src/plugin/npuw/partitioning/patterns/opt.cpp b/src/plugins/intel_npu/src/plugin/npuw/partitioning/patterns/opt.cpp index db9666b9485546..5abe4b39fd44f2 100644 --- a/src/plugins/intel_npu/src/plugin/npuw/partitioning/patterns/opt.cpp +++ b/src/plugins/intel_npu/src/plugin/npuw/partitioning/patterns/opt.cpp @@ -386,7 +386,8 @@ DQMatMulGQ2i::DQMatMulGQ2i(Context::Ref ctx) { auto qcoeff = opp::wrap_type(); auto qcvtw = opp::wrap_type({qweight}); auto qmuls = opp::wrap_type({qcvtw, qcoeff}); - auto qreshp = opp::wrap_type({qmuls, opp::any_input()}); + auto qcvtm = opp::optional({qmuls->output(0)}); + auto qreshp = opp::wrap_type({qcvtm, opp::any_input()}); auto qcvtr = opp::optional({qreshp->output(0)}); auto qmmi = opp::any_input(); auto qmm = opp::wrap_type({qmmi, qcvtr}); @@ -398,6 +399,10 @@ DQMatMulGQ2i::DQMatMulGQ2i(Context::Ref ctx) { auto matched_node_qweight = node_to_output.at(qweight).get_node_shared_ptr(); auto matched_node_qcoeff = node_to_output.at(qcoeff).get_node_shared_ptr(); auto matched_node_qmuls = node_to_output.at(qmuls).get_node_shared_ptr(); + std::shared_ptr matched_node_qcvtm = nullptr; + if (node_to_output.count(qcvtm)) { + matched_node_qcvtm = node_to_output.at(qcvtm).get_node_shared_ptr(); + } auto matched_node_matmul = node_to_output.at(qmm).get_node_shared_ptr(); auto matched_node_qreshp = node_to_output.at(qreshp).get_node_shared_ptr(); auto matched_out_mmi = node_to_output.at(qmmi); @@ -426,6 +431,9 @@ DQMatMulGQ2i::DQMatMulGQ2i(Context::Ref ctx) { auto new_transpose_order_c = std::make_shared(ov::element::i32, ov::Shape{3}, new_transpose_order); auto new_transpose = std::make_shared(matched_node_qmuls, new_transpose_order_c); + if (matched_node_qcvtm) { + new_transpose = std::make_shared(matched_node_qcvtm, new_transpose_order_c); + } matched_node_qreshp->input(0).replace_source_output(new_transpose); matched_node_qreshp->validate_and_infer_types(); matched_matmul->validate_and_infer_types(); @@ -660,10 +668,11 @@ DQMatMulGQ2iP::DQMatMulGQ2iP(Context::Ref ctx) { auto qcoeff = opp::wrap_type(); auto qcvtw = opp::wrap_type({qweight}); auto qmuls = opp::wrap_type({qcvtw, qcoeff}); - auto qreshp = opp::wrap_type({qmuls, opp::any_input()}); - auto qcvtm = opp::optional({qreshp->output(0)}); + auto qcvtm = opp::optional({qmuls->output(0)}); + auto qreshp = opp::wrap_type({qcvtm, opp::any_input()}); + auto qcvtr = opp::optional({qreshp->output(0)}); auto qmmi = opp::any_input(); - auto qmm = opp::wrap_type({qmmi, qcvtm}); + auto qmm = opp::wrap_type({qmmi, qcvtr}); // Note: Use [=] to make sure the above objects stay alive in the callback auto callback = [=](ov::pass::pattern::Matcher& m) { @@ -672,6 +681,10 @@ DQMatMulGQ2iP::DQMatMulGQ2iP(Context::Ref ctx) { auto matched_node_qweight = node_to_output.at(qweight).get_node_shared_ptr(); auto matched_node_qcoeff = node_to_output.at(qcoeff).get_node_shared_ptr(); auto matched_node_qmuls = node_to_output.at(qmuls).get_node_shared_ptr(); + std::shared_ptr matched_node_qcvtm = nullptr; + if (node_to_output.count(qcvtm)) { + matched_node_qcvtm = node_to_output.at(qcvtm).get_node_shared_ptr(); + } auto matched_node_matmul = node_to_output.at(qmm).get_node_shared_ptr(); auto matched_node_qreshp = node_to_output.at(qreshp).get_node_shared_ptr(); auto matched_out_mmi = node_to_output.at(qmmi); @@ -703,6 +716,9 @@ DQMatMulGQ2iP::DQMatMulGQ2iP(Context::Ref ctx) { auto new_transpose_order_c = std::make_shared(ov::element::i32, ov::Shape{3}, new_transpose_order); auto new_transpose = std::make_shared(matched_node_qmuls, new_transpose_order_c); + if (matched_node_qcvtm) { + new_transpose = std::make_shared(matched_node_qcvtm, new_transpose_order_c); + } matched_node_qreshp->input(0).replace_source_output(new_transpose); matched_node_qreshp->validate_and_infer_types(); matched_matmul->validate_and_infer_types(); @@ -798,7 +814,8 @@ DQParMMGQ::DQParMMGQ(Context::Ref ctx) { auto qreshp = opp::wrap_type({qmuls, opp::any_input()}); auto qmmi = opp::wrap_type({opp::any_input(), opp::any_input()}); auto qcvtr = opp::optional({qreshp->output(0)}); - auto qmm = opp::wrap_type({qmmi, qcvtr}); + auto qcvtm = opp::optional({qmmi->output(0)}); + auto qmm = opp::wrap_type({qcvtm, qcvtr}); // Note: Use [=] to make sure the above objects stay alive in the callback auto callback = [=](ov::pass::pattern::Matcher& m) { diff --git a/src/plugins/intel_npu/tools/protopipe/README.md b/src/plugins/intel_npu/tools/protopipe/README.md index 807bd744851b70..a6e3e00f94db57 100644 --- a/src/plugins/intel_npu/tools/protopipe/README.md +++ b/src/plugins/intel_npu/tools/protopipe/README.md @@ -516,67 +516,72 @@ Iteration : ## How to build ### Prerequisites -1. Clone `npu-plugin` repository -2. Build OpenCV G-API with OpenVINO/ONNXRT support -#### Build OpenCV G-API with OpenVINO/ONNXRT support -1. Clone OpenCV repo: +1. Build OpenCV G-API with OpenVINO/ONNXRT support +- Clone and build [OpenVINO](https://github.com/openvinotoolkit/openvino) from sources + ``` + mkdir "build" && cd "build" + cmake ../ -DCMAKE_BUILD_TYPE=Release ^ + -DENABLE_PLUGINS_XML=ON ^ + -DCMAKE_INSTALL_PREFIX=install ^ + -DENABLE_DEBUG_CAPS=ON ^ + -DENABLE_NPU_DEBUG_CAPS=ON .. + + cmake --build . --config Release --target install --parallel + ``` +- Init OpenVINO enviroment + ``` + "/setupvars.bat" + ``` +2. Build OpenCV +- Clone OpenCV repo: ``` git clone https://github.com/opencv/opencv - cd opencv && git checkout 78195bc3df + cd opencv && git checkout 3919f33e21 ``` -2. Build OpenCV G-API: - ``` - mkdir -p build && cd build - cmake ../ -DBUILD_LIST=gapi \ - -DCMAKE_BUILD_TYPE=Release \ - -DWITH_OPENVINO=ON \ - -DOpenVINO_DIR= \ - -DWITH_ONNX=ON \ - -DORT_INSTALL_DIR= +- Build OpenCV G-API: + ``` + mkdir "build" && cd "build" + cmake .. -DBUILD_LIST=gapi ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DWITH_OPENVINO=ON cmake --build . --config Release --target opencv_gapi --parallel - ``` -### In-plugin build - -1. Clone and build [OpenVINO](https://github.com/openvinotoolkit/openvino) from sources -2. Build OpenCV G-API with OpenVINO / ONNXRT support -3. Clone `npu-plugin` repository - ``` - git clone https://github.com/openvinotoolkit/npu_plugin - git submodule update --init --recursive - ``` -4. Build `Protopipe` as part of the `npu-plugin` build: - ``` - mkdir build && cd build - cmake ../ -DOpenCV_DIR= -DOpenVINODeveloperPackage_DIR= - cmake --build . --config Release --target protopipe --parallel - ``` - + ``` + If ONNX support is needed build OpenCV G-API with ONNX support: + ``` + mkdir "build" && cd "build" + cmake .. -DBUILD_LIST=gapi ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DWITH_OPENVINO=ON ^ + -DWITH_ONNX=ON ^ + -DORT_INSTALL_DIR= + cmake --build . --config Release --target opencv_gapi --parallel + ``` +### Build Protopipe inside OpenVINO +1. Build protopipe + ``` + cd + mkdir "src/plugins/intel_npu/tools/protopipe/build" && cd "src/plugins/intel_npu/tools/protopipe/build" + cmake ../ -DOpenCV_DIR= -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release --target protopipe --parallel + ``` ### Standalone build -1. Build `yaml-cpp` - ``` - mkdir -p yaml-cpp_build cd && yaml-cpp_build - cmake ..//thirdparty/yaml-cpp -DCMAKE_INSTALL_PREFIX=install - cmake --build . --config Release --target install --parallel - ``` -2. Build `gflags` - ``` - git clone https://github.com/gflags/gflags - cd gflags - mkdir -p gflags_build cd && gflags_build - cmake ../ -DCMAKE_INSTALL_PREFIX=install - cmake --build . --config Release --target install --parallel - ``` -3. Build `Protopipe` - ``` - mkdir -b protopipe_build && cd protopipe_build - cmake /tools/protopipe/ \ - -DOpenCV_DIR= \ - -Dgflags_DIR= \ - -DOpenVINO_DIR= \ - - cmake --build . --config Release --target protopipe --parallel - ``` +1. Build `gflags` + ``` + git clone https://github.com/gflags/gflags + cd gflags + mkdir "gflags_build" && cd "gflags_build" + cmake ../ -DCMAKE_INSTALL_PREFIX=install + cmake --build . --config Release --target install --parallel + ``` +2. Build `Protopipe` + ``` + mkdir "protopipe_build" && cd "protopipe_build" + cmake /src/plugins/intel_npu/tools/protopipe ^ + -DOpenCV_DIR= ^ + -Dgflags_DIR= + + cmake --build . --config Release --target protopipe --parallel + ``` ### Verify the installation **Note**: Make sure `opencv_*` libraries are visible in the environment: - Windows: diff --git a/tests/model_hub_tests/transformation_tests/test_stateful_to_stateless_transformation.py b/tests/model_hub_tests/transformation_tests/test_stateful_to_stateless_transformation.py index c3aaa082322fdf..461b5f157814ef 100644 --- a/tests/model_hub_tests/transformation_tests/test_stateful_to_stateless_transformation.py +++ b/tests/model_hub_tests/transformation_tests/test_stateful_to_stateless_transformation.py @@ -8,21 +8,46 @@ import models_hub_common.utils as utils import pytest import os +import re def get_read_value_ops(model: ov.Model): return [op for op in model.get_ops() if op.get_type_name() == 'ReadValue'] -def check_desc_tensors(tensors1, tensors2): - # order of tensors may not match, comparing by the total amount and names - assert len(tensors1) == len(tensors2) - assert set(tuple(t.names) for t in tensors1) == set(tuple(t.names) for t in tensors2) - for t1 in tensors1: - t2_candidates = [t for t in tensors2 if t1.names & t.names] - assert len(t2_candidates) == 1 - t2 = t2_candidates[0] - assert t1.names == t2.names - assert t1.get_partial_shape() == t2.get_partial_shape() - assert t1.get_element_type() == t2.get_element_type() +def check_desc_tensors(expected_tensors, tensors): + # checks if tensors descriptors are same as expected + pattern_input = re.compile(R"input_restored.((past_key_values|present)\.(\d+)\.(key|value))") + + assert len(expected_tensors) == len(tensors) + for expected in expected_tensors: + # The `patch_stateful` in optimum use any name instead found key/value names OV will use names restore path + # Restore expected names to find tensor for compare (can be removed when HG optimum updated) + expected_names = {m[1] if m else name for m, name in ((pattern_input.match(name), name) for name in expected.names)} + # tensor names check is relaxed the expected is sub-set of final names + t_candidates = [t for t in tensors if expected_names.issubset(t.names)] + assert len(t_candidates) == 1 + tensor = t_candidates[0] + assert expected.get_element_type() == tensor.get_element_type() + assert expected.get_partial_shape() == tensor.get_partial_shape() + + +def check_result_desc_tensors(expected_tensors, tensors): + # checks if Result tensors descriptors are same as expected + pattern_restore_output = re.compile(R"output_restored.((past_key_values|present)\.(\d+)\.(key|value))") + pattern_output = re.compile(R"(present\.(\d+)\.(key|value))") + + assert len(expected_tensors) == len(tensors) + for expected in expected_tensors: + # The `patch_stateful` in optimum use any name instead found key/value names OV will use names restore path + # Restore expected names to find tensor for compare (can be removed when HG optimum updated) + expected_names = {name for name in expected.names if not pattern_restore_output.match(name)} + expected_o_names = {name for name in expected.names if pattern_output.match(name)} + expected_names = expected_o_names if expected_o_names else expected_names + t_candidates = [t for t in tensors if expected_names.issubset(t.names)] + assert len(t_candidates) == 1 + tensor = t_candidates[0] + assert expected.get_element_type() == tensor.get_element_type() + assert expected.get_partial_shape() == tensor.get_partial_shape() + @retry(3, exceptions=(OSError,), delay=1) def run_stateful_to_stateless_in_runtime(tmp_path, model_id, model_link): @@ -42,7 +67,7 @@ def run_stateful_to_stateless_in_runtime(tmp_path, model_id, model_link): print(model.model) print(stateless_model.model) check_desc_tensors(model.model.inputs, stateless_model.model.inputs) - check_desc_tensors(model.model.outputs, stateless_model.model.outputs) + check_result_desc_tensors(model.model.outputs, stateless_model.model.outputs) core = ov.Core() core.compile_model(model.model, 'CPU') @@ -57,4 +82,4 @@ def test_stateful_to_stateless_precommit(tmp_path, model_name, model_link, mark, pytest.skip(reason) elif mark == 'xfail': pytest.xfail(reason) - run_stateful_to_stateless_in_runtime(tmp_path, model_name, model_link) \ No newline at end of file + run_stateful_to_stateless_in_runtime(tmp_path, model_name, model_link)