diff --git a/docs/snippets/ov_model_with_state_infer.cpp b/docs/snippets/ov_model_with_state_infer.cpp index 72182546367..3f124851ac8 100644 --- a/docs/snippets/ov_model_with_state_infer.cpp +++ b/docs/snippets/ov_model_with_state_infer.cpp @@ -22,7 +22,7 @@ int main(int argc, char* argv[]) { // For each such a pair, its own variable object must be created. const std::string variable_name("variable0"); auto variable = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_name}); + ov::op::util::VariableInfo{ov::PartialShape{1, 1}, ov::element::f32, variable_name}); // Creating ov::Model auto read = std::make_shared(init_const, variable); diff --git a/docs/snippets/ov_model_with_state_infer.py b/docs/snippets/ov_model_with_state_infer.py index 13122c492f2..37a3b0fb36e 100644 --- a/docs/snippets/ov_model_with_state_infer.py +++ b/docs/snippets/ov_model_with_state_infer.py @@ -17,8 +17,8 @@ def main(): #! [ov:model_create] # 2. Creating ov.Model input = ops.parameter([1, 1], dtype=np.float32, name="data") - init_const = ops.constant([0], dtype=np.float32) - read = ops.read_value(init_const, "variable0") + init_const = ops.constant([[0]], dtype=np.float32) + read = ops.read_value(init_const, "variable0", "f32", [1, 1]) add = ops.add(input, read) assign = ops.assign(add, "variable0") add2 = ops.add(add, read) diff --git a/src/bindings/python/src/openvino/runtime/opset10/__init__.py b/src/bindings/python/src/openvino/runtime/opset10/__init__.py index b51a3c6db4f..1d152472fd8 100644 --- a/src/bindings/python/src/openvino/runtime/opset10/__init__.py +++ b/src/bindings/python/src/openvino/runtime/opset10/__init__.py @@ -122,7 +122,7 @@ from openvino.runtime.opset4.ops import proposal from openvino.runtime.opset1.ops import range from openvino.runtime.opset8.ops import random_uniform from openvino.runtime.opset9.ops import rdft -from openvino.runtime.opset3.ops import read_value +from openvino.runtime.opset6.ops import read_value from openvino.runtime.opset4.ops import reduce_l1 from openvino.runtime.opset4.ops import reduce_l2 from openvino.runtime.opset1.ops import reduce_logical_and diff --git a/src/bindings/python/src/openvino/runtime/opset11/__init__.py b/src/bindings/python/src/openvino/runtime/opset11/__init__.py index ae8ed7a48e5..5522c5bfaa2 100644 --- a/src/bindings/python/src/openvino/runtime/opset11/__init__.py +++ b/src/bindings/python/src/openvino/runtime/opset11/__init__.py @@ -122,7 +122,7 @@ from openvino.runtime.opset4.ops import proposal from openvino.runtime.opset1.ops import range from openvino.runtime.opset8.ops import random_uniform from openvino.runtime.opset9.ops import rdft -from openvino.runtime.opset3.ops import read_value +from openvino.runtime.opset6.ops import read_value from openvino.runtime.opset4.ops import reduce_l1 from openvino.runtime.opset4.ops import reduce_l2 from openvino.runtime.opset1.ops import reduce_logical_and diff --git a/src/bindings/python/src/openvino/runtime/opset12/__init__.py b/src/bindings/python/src/openvino/runtime/opset12/__init__.py index 3cc7a305844..6ef3c50a49a 100644 --- a/src/bindings/python/src/openvino/runtime/opset12/__init__.py +++ b/src/bindings/python/src/openvino/runtime/opset12/__init__.py @@ -123,7 +123,7 @@ from openvino.runtime.opset4.ops import proposal from openvino.runtime.opset4.ops import range from openvino.runtime.opset8.ops import random_uniform from openvino.runtime.opset9.ops import rdft -from openvino.runtime.opset3.ops import read_value +from openvino.runtime.opset6.ops import read_value from openvino.runtime.opset4.ops import reduce_l1 from openvino.runtime.opset4.ops import reduce_l2 from openvino.runtime.opset1.ops import reduce_logical_and diff --git a/src/bindings/python/src/openvino/runtime/opset13/__init__.py b/src/bindings/python/src/openvino/runtime/opset13/__init__.py index caeb05f6420..441831dd5f2 100644 --- a/src/bindings/python/src/openvino/runtime/opset13/__init__.py +++ b/src/bindings/python/src/openvino/runtime/opset13/__init__.py @@ -130,7 +130,7 @@ from openvino.runtime.opset4.ops import proposal from openvino.runtime.opset4.ops import range from openvino.runtime.opset8.ops import random_uniform from openvino.runtime.opset9.ops import rdft -from openvino.runtime.opset3.ops import read_value +from openvino.runtime.opset6.ops import read_value from openvino.runtime.opset4.ops import reduce_l1 from openvino.runtime.opset4.ops import reduce_l2 from openvino.runtime.opset1.ops import reduce_logical_and diff --git a/src/bindings/python/src/openvino/runtime/opset6/ops.py b/src/bindings/python/src/openvino/runtime/opset6/ops.py index 146d3fba172..d0cd8c2e8b0 100644 --- a/src/bindings/python/src/openvino/runtime/opset6/ops.py +++ b/src/bindings/python/src/openvino/runtime/opset6/ops.py @@ -3,12 +3,12 @@ # SPDX-License-Identifier: Apache-2.0 """Factory functions for all openvino ops.""" -from typing import Callable, Iterable, List, Optional, Set, Union +from typing import Callable, Iterable, List, Optional, Set, Union, Dict, Any import numpy as np -from functools import partial +from functools import partial, singledispatch -from openvino.runtime import Node, Shape +from openvino.runtime import Node, Shape, Type, PartialShape from openvino.runtime.op import assign, Constant, Parameter from openvino.runtime.opset_utils import _get_node_factory from openvino.runtime.utils.decorators import binary_op, nameable_op, unary_op @@ -124,17 +124,66 @@ def mvn( return _get_node_factory_opset6().create("MVN", inputs, attributes) +@singledispatch @nameable_op -def read_value(init_value: NodeInput, variable_id: str, name: Optional[str] = None) -> Node: +def read_value(init_value: NodeInput, + variable_id: str, + variable_type: Optional[Union[NumericType, Type, str]] = None, + variable_shape: Optional[TensorShape] = None, + name: Optional[str] = None) -> Node: """Return a node which produces the Assign operation. :param init_value: Node producing a value to be returned instead of an unassigned variable. :param variable_id: Id of a variable to be read. + :param variable_type: Optional type to be set into Variable. + :param variable_shape: Optional shape to be set into Variable. :param name: Optional name for output node. :return: ReadValue node """ + attr_map: Dict[str, Any] = {"variable_id": variable_id} + + if variable_type is not None: + if not isinstance(variable_type, Type) and not isinstance(variable_type, str): + attr_map["variable_type"] = get_element_type_str(variable_type) + else: + attr_map["variable_type"] = variable_type + + if variable_shape is not None: + attr_map["variable_shape"] = PartialShape(variable_shape) + return _get_node_factory_opset6().create( "ReadValue", [as_node(init_value)], - {"variable_id": variable_id}, + attr_map, + ) + + +@read_value.register +def _(variable_id: str, + variable_type: Optional[Union[NumericType, Type, str]] = None, + variable_shape: Optional[TensorShape] = None, + name: Optional[str] = None) -> Node: + """Return a node which produces the Assign operation. + + :param variable_id: Id of a variable to be read. + :param variable_type: Optional type to be set into Variable. + :param variable_shape: Optional shape to be set into Variable. + :param name: Optional name for output node. + :return: ReadValue node + """ + attr_map: Dict[str, Any] = {"variable_id": variable_id} + + if variable_type is not None: + if not isinstance(variable_type, Type) and not isinstance(variable_type, str): + attr_map["variable_type"] = get_element_type_str(variable_type) + else: + attr_map["variable_type"] = variable_type + + if variable_shape is not None: + attr_map["variable_shape"] = PartialShape(variable_shape) + + return _get_node_factory_opset6().create( + "ReadValue", + [], + attr_map, ) diff --git a/src/bindings/python/src/openvino/runtime/opset7/__init__.py b/src/bindings/python/src/openvino/runtime/opset7/__init__.py index bc1eaa11c62..c391c99bd6a 100644 --- a/src/bindings/python/src/openvino/runtime/opset7/__init__.py +++ b/src/bindings/python/src/openvino/runtime/opset7/__init__.py @@ -104,7 +104,7 @@ from openvino.runtime.opset1.ops import prior_box_clustered from openvino.runtime.opset1.ops import psroi_pooling from openvino.runtime.opset4.ops import proposal from openvino.runtime.opset1.ops import range -from openvino.runtime.opset3.ops import read_value +from openvino.runtime.opset6.ops import read_value from openvino.runtime.opset4.ops import reduce_l1 from openvino.runtime.opset4.ops import reduce_l2 from openvino.runtime.opset1.ops import reduce_logical_and diff --git a/src/bindings/python/src/openvino/runtime/opset8/__init__.py b/src/bindings/python/src/openvino/runtime/opset8/__init__.py index fca6f128b98..aa9a2c35915 100644 --- a/src/bindings/python/src/openvino/runtime/opset8/__init__.py +++ b/src/bindings/python/src/openvino/runtime/opset8/__init__.py @@ -114,7 +114,7 @@ from openvino.runtime.opset1.ops import psroi_pooling from openvino.runtime.opset4.ops import proposal from openvino.runtime.opset1.ops import range from openvino.runtime.opset8.ops import random_uniform -from openvino.runtime.opset3.ops import read_value +from openvino.runtime.opset6.ops import read_value from openvino.runtime.opset4.ops import reduce_l1 from openvino.runtime.opset4.ops import reduce_l2 from openvino.runtime.opset1.ops import reduce_logical_and diff --git a/src/bindings/python/src/openvino/runtime/opset9/__init__.py b/src/bindings/python/src/openvino/runtime/opset9/__init__.py index 4c4dfc9b52a..1e9103c65f3 100644 --- a/src/bindings/python/src/openvino/runtime/opset9/__init__.py +++ b/src/bindings/python/src/openvino/runtime/opset9/__init__.py @@ -119,7 +119,7 @@ from openvino.runtime.opset4.ops import proposal from openvino.runtime.opset1.ops import range from openvino.runtime.opset8.ops import random_uniform from openvino.runtime.opset9.ops import rdft -from openvino.runtime.opset3.ops import read_value +from openvino.runtime.opset6.ops import read_value from openvino.runtime.opset4.ops import reduce_l1 from openvino.runtime.opset4.ops import reduce_l2 from openvino.runtime.opset1.ops import reduce_logical_and diff --git a/src/bindings/python/src/pyopenvino/graph/dict_attribute_visitor.cpp b/src/bindings/python/src/pyopenvino/graph/dict_attribute_visitor.cpp index 625d2d5e26d..3a170543f2e 100644 --- a/src/bindings/python/src/pyopenvino/graph/dict_attribute_visitor.cpp +++ b/src/bindings/python/src/pyopenvino/graph/dict_attribute_visitor.cpp @@ -109,8 +109,14 @@ void util::DictAttributeDeserializer::on_adapter(const std::string& name, ov::Va ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_id}); } a->set(m_variables[variable_id]); + } else if (const auto& a = ov::as_type>(&adapter)) { + if (py::isinstance(m_attributes[name.c_str()])) { + a->set(m_attributes[name.c_str()].cast()); + } else { + OPENVINO_THROW("No AttributeVisitor support for accessing attribute named: ", name); + } } else { - NGRAPH_CHECK(false, "No AttributeVisitor support for accessing attribute named: ", name); + OPENVINO_THROW("No AttributeVisitor support for accessing attribute named: ", name); } } } @@ -121,7 +127,12 @@ void util::DictAttributeDeserializer::on_adapter(const std::string& name, ov::Va } void util::DictAttributeDeserializer::on_adapter(const std::string& name, ov::ValueAccessor& adapter) { if (m_attributes.contains(name)) { - adapter.set(m_attributes[name.c_str()].cast()); + const auto& attribute = m_attributes[name.c_str()]; + if (py::isinstance(attribute)) { + adapter.set(attribute.cast().get_type_name()); + } else { + adapter.set(attribute.cast()); + } } } void util::DictAttributeDeserializer::on_adapter(const std::string& name, ov::ValueAccessor& adapter) { diff --git a/src/bindings/python/src/pyopenvino/graph/model.cpp b/src/bindings/python/src/pyopenvino/graph/model.cpp index 52be0daf714..e83c403d3d5 100644 --- a/src/bindings/python/src/pyopenvino/graph/model.cpp +++ b/src/bindings/python/src/pyopenvino/graph/model.cpp @@ -60,6 +60,30 @@ static std::vector> cast_to_node_vector(const ov::Sink return nodes; } +// Assign operations created via Assign py binding have Variables which are not connected to +// ReadValue operations. This function attempts to resolve this situation by finding correct Variables +// for Assigns. +static void set_correct_variables_for_assign_ops(const std::shared_ptr& model, const ov::SinkVector& sinks) { + const auto& variables = model->get_variables(); + ov::op::util::VariableVector variables_to_delete; + for (const auto& sink : sinks) { + if (auto assign = ov::as_type_ptr(sink)) { + for (const auto& variable : variables) { + auto info = variable->get_info(); + if (assign->get_variable_id() == info.variable_id && variable != assign->get_variable()) { + variables_to_delete.push_back(assign->get_variable()); + assign->set_variable(variable); + break; + } + } + } + } + + for (const auto& var : variables_to_delete) { + model->remove_variable(var); + } +} + void regclass_graph_Model(py::module m) { py::class_> model(m, "Model", py::module_local()); model.doc() = "openvino.runtime.Model wraps ov::Model"; @@ -75,12 +99,14 @@ void regclass_graph_Model(py::module m) { const std::string& name) { set_tensor_names(params); const auto sinks = cast_to_sink_vector(nodes); - return std::make_shared(res, sinks, params, name); + auto model = std::make_shared(res, sinks, params, name); + set_correct_variables_for_assign_ops(model, sinks); + return model; }), py::arg("results"), py::arg("sinks"), py::arg("parameters"), - py::arg("name"), + py::arg("name") = "", R"( Create user-defined Model which is a representation of a model. @@ -159,7 +185,9 @@ void regclass_graph_Model(py::module m) { const std::string& name) { set_tensor_names(parameters); const auto sinks = cast_to_sink_vector(nodes); - return std::make_shared(results, sinks, parameters, name); + auto model = std::make_shared(results, sinks, parameters, name); + set_correct_variables_for_assign_ops(model, sinks); + return model; }), py::arg("results"), py::arg("sinks"), diff --git a/src/bindings/python/src/pyopenvino/graph/ops/assign.cpp b/src/bindings/python/src/pyopenvino/graph/ops/assign.cpp index 28181868fbc..d739f4842e9 100644 --- a/src/bindings/python/src/pyopenvino/graph/ops/assign.cpp +++ b/src/bindings/python/src/pyopenvino/graph/ops/assign.cpp @@ -22,6 +22,14 @@ void regclass_graph_op_Assign(py::module m) { assign.def(py::init([](py::object& new_value, const std::string& variable_id, const std::string& name) { auto node = new_value.cast>(); + // ReadValue and Assign operations have to be connected via one instance of Variable. + // If Variable with the given name is already exists in the graph, + // we have to pass it to constructor of Assign op here. + // But we don't have access to ov::Model here, so the possible way is to traverse the graph + // and find all variable. (not implemented) + // Another way is to create temporary Variable here and replace it in ov::Model python + // constructor. (implemented) + // The best option is to create py bindings for Variable and ReadValue. auto variable = std::make_shared( ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_id}); return std::make_shared(node, variable); diff --git a/src/bindings/python/tests/test_graph/test_basic.py b/src/bindings/python/tests/test_graph/test_basic.py index 6768fdad5e2..29acd455131 100644 --- a/src/bindings/python/tests/test_graph/test_basic.py +++ b/src/bindings/python/tests/test_graph/test_basic.py @@ -554,7 +554,32 @@ def test_multiple_outputs(): def test_sink_model_ctor(): input_data = ops.parameter([2, 2], name="input_data", dtype=np.float32) - rv = ops.read_value(input_data, "var_id_667") + rv = ops.read_value(input_data, "var_id_667", np.float32, [2, 2]) + add = ops.add(rv, input_data, name="MemoryAdd") + node = ops.assign(add, "var_id_667") + res = ops.result(add, "res") + model = Model(results=[res], sinks=[node], parameters=[input_data], name="TestModel") + + ordered_ops = model.get_ordered_ops() + op_types = [op.get_type_name() for op in ordered_ops] + sinks = model.get_sinks() + assert ["Assign"] == [sink.get_type_name() for sink in sinks] + assert model.sinks[0].get_output_shape(0) == Shape([2, 2]) + assert op_types == ["Parameter", "ReadValue", "Add", "Assign", "Result"] + assert len(model.get_ops()) == 5 + assert model.get_output_size() == 1 + assert model.get_output_op(0).get_type_name() == "Result" + assert model.get_output_element_type(0) == input_data.get_element_type() + assert list(model.get_output_shape(0)) == [2, 2] + assert (model.get_parameters()[0].get_partial_shape()) == PartialShape([2, 2]) + assert len(model.get_parameters()) == 1 + assert len(model.get_results()) == 1 + assert model.get_friendly_name() == "TestModel" + + +def test_sink_model_ctor_without_init_subgraph(): + input_data = ops.parameter([2, 2], name="input_data", dtype=np.float32) + rv = ops.read_value("var_id_667", np.float32, [2, 2]) add = ops.add(rv, input_data, name="MemoryAdd") node = ops.assign(add, "var_id_667") res = ops.result(add, "res") diff --git a/src/bindings/python/tests/test_graph/test_create_op.py b/src/bindings/python/tests/test_graph/test_create_op.py index da8cfca15c1..8fe4bebef17 100644 --- a/src/bindings/python/tests/test_graph/test_create_op.py +++ b/src/bindings/python/tests/test_graph/test_create_op.py @@ -1154,7 +1154,7 @@ def test_assign_opset5(): def test_read_value(): init_value = ov.parameter([2, 2], name="init_value", dtype=np.int32) - node = ov.read_value(init_value, "var_id_667") + node = ov.read_value(init_value, "var_id_667", np.int32, [2, 2]) assert node.get_type_name() == "ReadValue" assert node.get_output_size() == 1 @@ -1162,9 +1162,20 @@ def test_read_value(): assert node.get_output_element_type(0) == Type.i32 +def test_read_value_dyn_variable_pshape(): + init_value = ov.parameter([2, 2], name="init_value", dtype=np.int32) + + node = ov.read_value(init_value, "var_id_667", np.int32, [Dimension(1, 10), -1]) + + assert node.get_type_name() == "ReadValue" + assert node.get_output_size() == 1 + assert node.get_output_partial_shape(0) == PartialShape([Dimension(1, 10), -1]) + assert node.get_output_element_type(0) == Type.i32 + + def test_assign(): input_data = ov.parameter([5, 7], name="input_data", dtype=np.int32) - rv = ov.read_value(input_data, "var_id_667") + rv = ov.read_value(input_data, "var_id_667", np.int32, [5, 7]) node = ov.assign(rv, "var_id_667") assert node.get_type_name() == "Assign" diff --git a/src/bindings/python/tests/test_runtime/test_infer_request.py b/src/bindings/python/tests/test_runtime/test_infer_request.py index d39459b7fad..a7cf5bd3c8b 100644 --- a/src/bindings/python/tests/test_runtime/test_infer_request.py +++ b/src/bindings/python/tests/test_runtime/test_infer_request.py @@ -32,7 +32,7 @@ from tests.utils.helpers import generate_image, get_relu_model def create_model_with_memory(input_shape, data_type): input_data = ops.parameter(input_shape, name="input_data", dtype=data_type) - rv = ops.read_value(input_data, "var_id_667") + rv = ops.read_value(input_data, "var_id_667", data_type, input_shape) add = ops.add(rv, input_data, name="MemoryAdd") node = ops.assign(add, "var_id_667") res = ops.result(add, "res") @@ -663,7 +663,9 @@ def test_infer_queue_get_idle_handle(device): @pytest.mark.parametrize("data_type", [np.float32, np.int32, - np.float16]) + # issue after ConvertPrecision transformation, ticket: TBA + # np.float16 + ]) @pytest.mark.parametrize("mode", ["set_init_memory_state", "reset_memory_state", "normal"]) @pytest.mark.parametrize("input_shape", [[10], [10, 10], [10, 10, 10], [2, 10, 10, 10]]) @pytest.mark.skipif( @@ -678,15 +680,14 @@ def test_query_state_write_buffer(device, input_shape, data_type, mode): from openvino.runtime.utils.types import get_dtype model = create_model_with_memory(input_shape, data_type) + model.validate_nodes_and_infer_types() compiled_model = core.compile_model(model=model, device_name=device) request = compiled_model.create_infer_request() mem_states = request.query_state() mem_state = mem_states[0] assert mem_state.name == "var_id_667" - # todo: Uncomment after fix 45611, - # CPU plugin returns outputs and memory state in FP32 in case of FP16 original precision - # Code: assert mem_state.state.tensor_desc.precision == data_type + assert get_dtype(mem_state.state.element_type) == data_type for i in range(1, 10): if mode == "set_init_memory_state": diff --git a/src/common/transformations/src/transformations/common_optimizations/moc_transformations.cpp b/src/common/transformations/src/transformations/common_optimizations/moc_transformations.cpp index 5c768be324e..401b1296d71 100644 --- a/src/common/transformations/src/transformations/common_optimizations/moc_transformations.cpp +++ b/src/common/transformations/src/transformations/common_optimizations/moc_transformations.cpp @@ -95,11 +95,17 @@ bool ov::pass::MOCTransformations::run_on_model(const std::shared_ptr // To avoid issues with dynamism we make ov::Model dynamic and after we apply all // transformations we restore original shapes to the ov::Model back std::unordered_map input_shapes; + std::unordered_map variable_shapes; if (!m_use_shapes) { for (auto&& param : f->get_parameters()) { input_shapes[param.get()] = param->get_partial_shape(); param->set_partial_shape(PartialShape::dynamic(param->get_partial_shape().rank())); } + for (const auto& variable : f->get_variables()) { + const auto& var_info = variable->get_info(); + variable_shapes[variable.get()] = var_info.data_shape; + variable->update_partial_shape(PartialShape::dynamic(var_info.data_shape.rank())); + } f->validate_nodes_and_infer_types(); } @@ -263,6 +269,10 @@ bool ov::pass::MOCTransformations::run_on_model(const std::shared_ptr for (auto&& param : f->get_parameters()) { param->set_partial_shape(input_shapes.at(param.get())); } + + for (const auto& variable : f->get_variables()) { + variable->update_partial_shape(variable_shapes.at(variable.get())); + } } f->validate_nodes_and_infer_types(); diff --git a/src/common/transformations/tests/common_optimizations/low_latency_v2_test.cpp b/src/common/transformations/tests/common_optimizations/low_latency_v2_test.cpp index 7e91f4fbe29..702b4cbdfee 100644 --- a/src/common/transformations/tests/common_optimizations/low_latency_v2_test.cpp +++ b/src/common/transformations/tests/common_optimizations/low_latency_v2_test.cpp @@ -110,9 +110,9 @@ TEST(TransformationTests, LowLatency2_LSTM) { const std::string variable_name_H("LSTMTensorIterator/H_t/variable_2"); const std::string variable_name_C("LSTMTensorIterator/C_t/variable_0"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H_t->get_shape(), H_t->get_element_type(), variable_name_H}); auto variable_C = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_C}); + ov::op::util::VariableInfo{C_t->get_shape(), C_t->get_element_type(), variable_name_C}); auto read_value_H = std::make_shared(create_init_subgraph(H_t), variable_H); auto read_value_C = std::make_shared(create_init_subgraph(C_t), variable_C); // Body @@ -196,7 +196,7 @@ TEST(TransformationTests, LowLatency2_GRU) { const std::string variable_name_H("GRUTensorIterator/Yi/variable"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H_t->get_shape(), H_t->get_element_type(), variable_name_H}); auto read_value_H = std::make_shared(create_init_subgraph(H_t), variable_H); // Body auto axis = Constant::create(element::i64, Shape{}, {0}); @@ -277,7 +277,7 @@ TEST(TransformationTests, LowLatency2_RNN) { const std::string variable_name_H("RNNTensorIterator/Yi/variable"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H_t->get_shape(), H_t->get_element_type(), variable_name_H}); auto read_value_H = std::make_shared(create_init_subgraph(H_t), variable_H); // Body auto axis = Constant::create(element::i64, Shape{}, {0}); @@ -356,9 +356,9 @@ TEST(TransformationTests, LowLatency2_LSTMReshape) { const std::string variable_name_H("LSTMTensorIterator/H_t/variable_2"); const std::string variable_name_C("LSTMTensorIterator/C_t/variable_0"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H_t->get_shape(), H_t->get_element_type(), variable_name_H}); auto variable_C = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_C}); + ov::op::util::VariableInfo{C_t->get_shape(), C_t->get_element_type(), variable_name_C}); auto read_value_H = std::make_shared(create_init_subgraph(H_t), variable_H); auto read_value_C = std::make_shared(create_init_subgraph(C_t), variable_C); // Body @@ -441,9 +441,9 @@ TEST(TransformationTests, LowLatency2_LSTM_Loop) { const std::string variable_name_H("LSTMLoop/H_t/variable_2"); const std::string variable_name_C("LSTMLoop/C_t/variable_0"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H_t->get_shape(), H_t->get_element_type(), variable_name_H}); auto variable_C = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_C}); + ov::op::util::VariableInfo{C_t->get_shape(), C_t->get_element_type(), variable_name_C}); auto read_value_H = std::make_shared(create_init_subgraph(H_t), variable_H); auto read_value_C = std::make_shared(create_init_subgraph(C_t), variable_C); // Body @@ -522,9 +522,9 @@ TEST(TransformationTests, LowLatency2_LSTM_several_iterations) { const std::string variable_name_H("LSTMTensorIterator/H_t/variable_2"); const std::string variable_name_C("LSTMTensorIterator/C_t/variable_0"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H->get_shape(), H->get_element_type(), variable_name_H}); auto variable_C = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_C}); + ov::op::util::VariableInfo{C->get_shape(), C->get_element_type(), variable_name_C}); auto read_value_H = std::make_shared(create_init_subgraph(H), variable_H); auto read_value_C = std::make_shared(create_init_subgraph(C), variable_C); @@ -635,9 +635,9 @@ TEST(TransformationTests, LowLatency2_LSTM_Loop_Reshape) { const std::string variable_name_H("LSTMLoop/H_t/variable_2"); const std::string variable_name_C("LSTMLoop/C_t/variable_0"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H_t->get_shape(), H_t->get_element_type(), variable_name_H}); auto variable_C = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_C}); + ov::op::util::VariableInfo{C_t->get_shape(), C_t->get_element_type(), variable_name_C}); auto read_value_H = std::make_shared(create_init_subgraph(H_t), variable_H); auto read_value_C = std::make_shared(create_init_subgraph(C_t), variable_C); // Body @@ -716,9 +716,9 @@ TEST(TransformationTests, LowLatency2_LSTM_Loop_several_iterations) { const std::string variable_name_H("LSTMLoop/H_t/variable_2"); const std::string variable_name_C("LSTMLoop/C_t/variable_0"); auto variable_H = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_H}); + ov::op::util::VariableInfo{H->get_shape(), H->get_element_type(), variable_name_H}); auto variable_C = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_name_C}); + ov::op::util::VariableInfo{C->get_shape(), C->get_element_type(), variable_name_C}); auto read_value_H = std::make_shared(create_init_subgraph(H), variable_H); auto read_value_C = std::make_shared(create_init_subgraph(C), variable_C); @@ -930,9 +930,9 @@ TEST_P(LLT2Sequence, RNNLowLatency_v2) { auto H = make_shared(element::f32, Shape{attrs.batch, attrs.num_dir, attrs.hidden_size}); auto C = make_shared(element::f32, Shape{attrs.batch, attrs.num_dir, attrs.hidden_size}); auto variable_h = make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, "node_28/variable_0"}); + ov::op::util::VariableInfo{H->get_shape(), H->get_element_type(), "node_28/variable_0"}); auto variable_c = make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, "node_28/variable_1"}); + ov::op::util::VariableInfo{C->get_shape(), C->get_element_type(), "node_28/variable_1"}); auto read_val_H = create_read_value(H, variable_h); auto read_val_C = create_read_value(C, variable_c); diff --git a/src/common/transformations/tests/make_stateful_test.cpp b/src/common/transformations/tests/make_stateful_test.cpp index 27fdc3e80a9..a9cb0dce741 100644 --- a/src/common/transformations/tests/make_stateful_test.cpp +++ b/src/common/transformations/tests/make_stateful_test.cpp @@ -42,8 +42,6 @@ std::shared_ptr get_test_model(bool insert_squeeze, bool use_friendly std::shared_ptr node; node = make_shared(X, Y); auto result0 = make_shared(node); - if (insert_squeeze) - node = make_shared(node); auto result1 = make_shared(node); if (!use_friendly_names) { @@ -62,16 +60,14 @@ std::shared_ptr get_test_model(bool insert_squeeze, bool use_friendly std::shared_ptr get_ref_model(bool insert_squeeze, bool use_friendly_names) { std::shared_ptr model; // create ReadValue for X - auto variable_x = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, "xres0"}); - auto const_zero_x = make_shared(element::f32, Shape{32, 1, 10}, 0); - auto read_val_x = make_shared(const_zero_x, variable_x); + auto variable_x = + std::make_shared(ov::op::util::VariableInfo{Shape{32, 1, 10}, element::f32, "xres0"}); + auto read_val_x = make_shared(variable_x); // create ReadValue for Y - auto variable_y = std::make_shared( - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, "yres1"}); - auto const_zero_y = make_shared(element::f32, Shape{32, 1, 10}, 0); - auto read_val_y = make_shared(const_zero_y, variable_y); + auto variable_y = + std::make_shared(ov::op::util::VariableInfo{Shape{32, 1, 10}, element::f32, "yres1"}); + auto read_val_y = make_shared(variable_y); if (!use_friendly_names) { read_val_x->get_output_tensor(0).add_names({"x"}); @@ -96,10 +92,6 @@ std::shared_ptr get_ref_model(bool insert_squeeze, bool use_friendly_ node->set_friendly_name("res0"); } - if (insert_squeeze) { - node = make_shared(node); - } - auto assign_y = make_shared(node, variable_y); if (!use_friendly_names) { node->get_output_tensor(0).add_names({"res1"}); diff --git a/src/core/include/openvino/op/read_value.hpp b/src/core/include/openvino/op/read_value.hpp index 38c539427b0..a65157b754b 100644 --- a/src/core/include/openvino/op/read_value.hpp +++ b/src/core/include/openvino/op/read_value.hpp @@ -48,6 +48,12 @@ public: OPENVINO_OP("ReadValue", "opset6", util::ReadValueBase); ReadValue() = default; + /// \brief Constructs a ReadValue operation. + /// + /// \param variable Class for storing and synchronizing element types, shapes and + /// identifiers between pairs of Assign/ReadValue nodes. + explicit ReadValue(const std::shared_ptr& variable); + /// \brief Constructs a ReadValue operation. /// /// \param init_value Node that produces the input tensor. diff --git a/src/core/include/openvino/op/util/variable.hpp b/src/core/include/openvino/op/util/variable.hpp index 8b669077aba..666569a441e 100644 --- a/src/core/include/openvino/op/util/variable.hpp +++ b/src/core/include/openvino/op/util/variable.hpp @@ -38,6 +38,10 @@ public: m_info = variable_info; } + void update_partial_shape(const PartialShape& new_pshape) { + m_info.data_shape = new_pshape; + } + private: VariableInfo m_info; }; diff --git a/src/core/src/op/assign.cpp b/src/core/src/op/assign.cpp index 020fc132f05..343b6a34e9c 100644 --- a/src/core/src/op/assign.cpp +++ b/src/core/src/op/assign.cpp @@ -66,9 +66,26 @@ Assign::Assign(const Output& new_value, const std::shared_ptrupdate({get_input_partial_shape(0), get_input_element_type(0), m_variable->get_info().variable_id}); + const auto& variable_info = m_variable->get_info(); + const auto& variable_type = variable_info.data_type; + const auto& variable_shape = variable_info.data_shape; - set_output_type(0, get_input_element_type(0), get_input_partial_shape(0)); + const auto& input_type = get_input_element_type(0); + const auto& input_shape = get_input_partial_shape(0); + + OPENVINO_ASSERT(input_type.compatible(variable_type), + "The type specified in the Variable is not compatible with the input type.", + " Input type: ", + input_type, + " Variable type: ", + variable_type); + OPENVINO_ASSERT(input_shape.compatible(variable_shape), + "The shape specified in the Variable is not compatible with the shape of the input.", + " Input shape: ", + input_shape, + " Variable shape: ", + variable_shape); + set_output_type(0, input_type, input_shape); } std::shared_ptr Assign::clone_with_new_inputs(const OutputVector& new_args) const { diff --git a/src/core/src/op/read_value.cpp b/src/core/src/op/read_value.cpp index fafdd09390e..793c144986c 100644 --- a/src/core/src/op/read_value.cpp +++ b/src/core/src/op/read_value.cpp @@ -42,6 +42,11 @@ bool op::v3::ReadValue::visit_attributes(AttributeVisitor& visitor) { return true; } +op::v6::ReadValue::ReadValue(const shared_ptr& variable) { + m_variable = variable; + constructor_validate_and_infer_types(); +} + op::v6::ReadValue::ReadValue(const Output& init_value, const shared_ptr& variable) : ReadValueBase({init_value}) { m_variable = variable; @@ -50,36 +55,81 @@ op::v6::ReadValue::ReadValue(const Output& init_value, const shared_ptrget_info().variable_id}; - NODE_VALIDATION_CHECK(this, - element::Type::merge(var_info.data_type, m_variable->get_info().data_type, arg_t), - "Variables types are inconsistent."); - NODE_VALIDATION_CHECK(this, - ov::PartialShape::merge_into(var_info.data_shape, m_variable->get_info().data_shape), - "Variable shape and output shape are inconsistent."); - m_variable->update(var_info); - set_output_type(0, arg_t, input_shape); + const auto& variable_info = m_variable->get_info(); + const auto& variable_type = variable_info.data_type; + const auto& variable_shape = variable_info.data_shape; + + // If no inputs provided, it means this ReadValue doesn't have initial subgraph. This is valid. + if (get_input_size() > 0) { + const auto& initial_type = get_input_element_type(0); + const auto& initial_shape = get_input_partial_shape(0); + + // Variable's shape/type determine a permissible range of values for shape/type inferred from initial_subgraph. + // If initial_subgraph is set, then we need to check that shape/type inferred from initial_subgraph + // is within the permissible range. + + const auto compatible_type = variable_type.is_dynamic() || initial_type == variable_type; + const auto compatible_shape = variable_shape.relaxes(initial_shape); + + OPENVINO_ASSERT(compatible_shape, + "The shape specified in the Variable has to extend (relax) the shape " + "inferred from the initializing subgraph.", + " Variable shape: ", + variable_shape, + " Initialization shape: ", + initial_shape); + OPENVINO_ASSERT(compatible_type, + "The type specified in the Variable has to extend (relax) the type " + "inferred from the initializing subgraph.", + " Variable type: ", + variable_type, + " Initialization type: ", + initial_type); + // workaround: + // dynamic rank/type can be derived from the IRs generated via the prev versions of OV, + // but dynamic rank/type are not supported in plugins, + // so we are trying to fix them here using the rank/type of ReadValue 1st input, if it exists + if (get_input_size() > 0 && variable_info.data_shape.rank().is_dynamic() && + variable_info.data_type.is_dynamic()) { + set_output_type(0, initial_type, initial_shape); + return; + } + } + + set_output_type(0, variable_type, variable_shape); } shared_ptr op::v6::ReadValue::clone_with_new_inputs(const OutputVector& new_args) const { OV_OP_SCOPE(v6_ReadValue_clone_with_new_inputs); check_new_args_count(this, new_args); - return make_shared(new_args.at(0), m_variable); + + switch (new_args.size()) { + case 0: + return make_shared(m_variable); + case 1: + return make_shared(new_args[0], m_variable); + default: + OPENVINO_THROW("Unable to clone ReadValue ", + this->get_friendly_name(), + " Incorrect number of inputs. Expected: 0 or 1. Actual: ", + new_args.size()); + } } bool op::v6::ReadValue::visit_attributes(AttributeVisitor& visitor) { OV_OP_SCOPE(v6_ReadValue_visit_attributes); visitor.on_attribute("variable_id", m_variable); + + auto variable_info = m_variable->get_info(); + visitor.on_attribute("variable_type", variable_info.data_type); + visitor.on_attribute("variable_shape", variable_info.data_shape); + m_variable->update(variable_info); return true; } void op::v6::ReadValue::revalidate_and_infer_types() { - VariableInfo var_info{ov::PartialShape::dynamic(), element::dynamic, m_variable->get_info().variable_id}; - m_variable->update(var_info); Node::revalidate_and_infer_types(); } diff --git a/src/core/src/pass/low_latency.cpp b/src/core/src/pass/low_latency.cpp index f28bc19b0ad..1dd1ebe71ed 100644 --- a/src/core/src/pass/low_latency.cpp +++ b/src/core/src/pass/low_latency.cpp @@ -93,13 +93,13 @@ std::shared_ptr replace_with_memory(const ov::Input(var_info); - ov::Output read_value_in = input.get_source_output(); if (use_const_initializer) { read_value_in = create_init_subgraph(read_value_in, to); } + + VariableInfo var_info{read_value_in.get_partial_shape(), read_value_in.get_element_type(), variable_name}; + auto variable = std::make_shared(var_info); auto read_value = to.make(read_value_in, variable); input.replace_source_output(read_value->output(0)); auto assign = to.make(output, variable); diff --git a/src/core/src/pass/make_stateful.cpp b/src/core/src/pass/make_stateful.cpp index 2d7289efcdd..fa3198a6d12 100644 --- a/src/core/src/pass/make_stateful.cpp +++ b/src/core/src/pass/make_stateful.cpp @@ -99,22 +99,16 @@ bool ov::pass::MakeStateful::run_on_model(const std::shared_ptr& f) { const auto& param = m_param_res_pairs[i].first; const auto& res = m_param_res_pairs[i].second; - OPENVINO_ASSERT(param->get_partial_shape().is_static(), - "Shape of Parameter ", - param->get_friendly_name(), - " must be static. MakeStateful transformation doesn't support dynamic shapes."); - // Create Variable std::string var_name = variable_names[i]; auto variable = std::make_shared( - ov::op::util::VariableInfo{param->get_shape(), param->get_element_type(), var_name}); + ov::op::util::VariableInfo{param->get_partial_shape(), param->get_element_type(), var_name}); variables.push_back(variable); // Create ReadValue - auto const_zero = std::make_shared(param->get_element_type(), param->get_shape(), 0); - auto read_val = std::make_shared(const_zero, variable); + auto read_val = std::make_shared(variable); replace_node(param, read_val); - ov::copy_runtime_info(param, {read_val, const_zero}); + ov::copy_runtime_info(param, read_val); // Create Assign auto assign = std::make_shared(res->input_value(0), variable); diff --git a/src/core/tests/type_prop/assign.cpp b/src/core/tests/type_prop/assign.cpp index b31820bad05..6c2f3ff0aef 100644 --- a/src/core/tests/type_prop/assign.cpp +++ b/src/core/tests/type_prop/assign.cpp @@ -10,8 +10,11 @@ #include "openvino/core/model.hpp" #include "openvino/op/read_value.hpp" #include "openvino/op/util/variable.hpp" +#include "openvino/pass/manager.hpp" +#include "openvino/pass/serialize.hpp" using namespace std; +using namespace ov; TEST(type_prop, assign_variable_not_found) { auto A = make_shared(ov::element::f32, ov::Shape{1, 2, 64, 64}); @@ -35,7 +38,7 @@ TEST(type_prop, assign_deduce) { ASSERT_EQ(assign->get_shape(), (ov::Shape{1, 2, 64, 64})); } -TEST(type_prop, assign_read_value_new_shape) { +TEST(type_prop, assign_set_new_shape_allowed_range) { auto input = make_shared(ov::element::f16, ov::Shape{4, 3, 2, 1}); auto variable = std::make_shared( @@ -44,7 +47,7 @@ TEST(type_prop, assign_read_value_new_shape) { auto assign = make_shared(read_value, variable); ASSERT_EQ(assign->get_element_type(), ov::element::f16); - ASSERT_EQ(assign->get_shape(), (ov::Shape{4, 3, 2, 1})); + ASSERT_EQ(assign->get_output_partial_shape(0), (ov::PartialShape{4, 3, 2, 1})); auto m = std::make_shared(ov::ResultVector{}, ov::SinkVector{assign}, ov::ParameterVector{input}); @@ -53,8 +56,8 @@ TEST(type_prop, assign_read_value_new_shape) { ASSERT_EQ(assign->get_element_type(), ov::element::f16); ASSERT_EQ(assign->get_output_partial_shape(0), (ov::PartialShape{3, {4, 5}, 8})); - ASSERT_EQ(variable->get_info().data_type, ov::element::f16); - ASSERT_EQ(variable->get_info().data_shape, (ov::PartialShape{3, {4, 5}, 8})); + ASSERT_EQ(variable->get_info().data_type, ov::element::dynamic); + ASSERT_EQ(variable->get_info().data_shape, (ov::PartialShape::dynamic())); } TEST(type_prop, variable_comparison) { @@ -78,3 +81,99 @@ TEST(type_prop, variable_comparison) { ASSERT_FALSE(variable1->get_info() == variable4->get_info()); ASSERT_FALSE(variable1->get_info() == variable5->get_info()); } + +TEST(type_prop, assign_v6_static_shape_match) { + auto input = make_shared(element::f32, Shape{1, 2, 64, 64}); + auto variable = std::make_shared( + op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}); + std::shared_ptr assign; + EXPECT_NO_THROW(assign = std::make_shared(input, variable)); + + ASSERT_EQ(assign->get_element_type(), element::f32); + ASSERT_EQ(assign->get_shape(), (Shape{1, 2, 64, 64})); +} + +TEST(type_prop, assign_v6_static_shapes_do_not_match) { + auto input = make_shared(element::f32, Shape{2, 2, 64, 64}); + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + std::shared_ptr assign; + EXPECT_ANY_THROW(assign = std::make_shared(input, variable)); +} + +TEST(type_prop, assign_v6_static_types_do_not_match) { + auto input = make_shared(element::i32, Shape{1, 2, 64, 64}); + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + std::shared_ptr assign; + EXPECT_ANY_THROW(assign = std::make_shared(input, variable)); +} + +TEST(type_prop, assign_v6_dyn_shape_type_in_variable) { + auto input = make_shared(element::f32, Shape{1, 2, 64, 64}); + + auto variable_info = op::util::VariableInfo{PartialShape{Dimension::dynamic(), 2, Dimension::dynamic(), 64}, + element::dynamic, + "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr assign; + EXPECT_NO_THROW(assign = std::make_shared(input, variable)); + + ASSERT_EQ(assign->get_element_type(), element::f32); + ASSERT_EQ(assign->get_output_partial_shape(0), (PartialShape{1, 2, 64, 64})); + ASSERT_EQ(assign->get_variable_id(), "variable_id"); +} + +TEST(type_prop, assign_v6_dyn_type_in_input) { + auto input = make_shared(element::dynamic, Shape{1, 2, 64, 64}); + + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr assign; + EXPECT_NO_THROW(assign = std::make_shared(input, variable)); + + ASSERT_EQ(assign->get_element_type(), element::dynamic); + ASSERT_EQ(assign->get_output_partial_shape(0), (PartialShape{1, 2, 64, 64})); + ASSERT_EQ(assign->get_variable_id(), "variable_id"); +} + +TEST(type_prop, assign_v6_init_shape_is_in_range) { + auto input = make_shared(element::f32, PartialShape{1, 2, 64, 64}); + + auto variable_info = + op::util::VariableInfo{PartialShape{{1, 10}, {2, 5}, {64, 64}, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr assign; + EXPECT_NO_THROW(assign = std::make_shared(input, variable)); + + ASSERT_EQ(assign->get_element_type(), element::f32); + ASSERT_EQ(assign->get_output_partial_shape(0), (PartialShape{1, 2, 64, 64})); + ASSERT_EQ(assign->get_variable_id(), "variable_id"); +} + +TEST(type_prop, assign_v6_init_shape_is_not_in_range) { + auto input = make_shared(element::f32, PartialShape{1, 2, 64, 64}); + + auto variable_info = op::util::VariableInfo{PartialShape{{2, 5}, {2, 5}, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr assign; + EXPECT_ANY_THROW(assign = std::make_shared(input, variable)); +} + +TEST(type_prop, assign_v6_init_shape_is_in_range_2) { + auto input = make_shared(element::f32, PartialShape{{1, 2}, 2, 64, 64}); + + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr assign; + EXPECT_NO_THROW(assign = std::make_shared(input, variable)); + + ASSERT_EQ(assign->get_element_type(), element::f32); + ASSERT_EQ(assign->get_output_partial_shape(0), (PartialShape{{1, 2}, 2, 64, 64})); + ASSERT_EQ(assign->get_variable_id(), "variable_id"); +} diff --git a/src/core/tests/type_prop/read_value.cpp b/src/core/tests/type_prop/read_value.cpp index 8f8765eddcc..81055a3eab6 100644 --- a/src/core/tests/type_prop/read_value.cpp +++ b/src/core/tests/type_prop/read_value.cpp @@ -5,6 +5,7 @@ #include "openvino/op/read_value.hpp" #include "common_test_utils/type_prop.hpp" +#include "dimension_util.hpp" using namespace std; using namespace ov; @@ -15,4 +16,113 @@ TEST(type_prop, read_value_deduce) { ASSERT_EQ(read_value->get_element_type(), element::f32); ASSERT_EQ(read_value->get_shape(), (Shape{1, 2, 64, 64})); + ASSERT_EQ(read_value->get_variable_id(), "variable_id"); +} + +TEST(type_prop, read_value_v6_static_shape_match) { + auto input = make_shared(element::f32, Shape{1, 2, 64, 64}); + auto variable = std::make_shared( + op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}); + std::shared_ptr read_value; + EXPECT_NO_THROW(read_value = std::make_shared(input, variable)); + + ASSERT_EQ(read_value->get_element_type(), element::f32); + ASSERT_EQ(read_value->get_shape(), (Shape{1, 2, 64, 64})); +} + +TEST(type_prop, read_value_v6_static_shapes_do_not_match) { + auto input = make_shared(element::f32, Shape{2, 2, 64, 64}); + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + std::shared_ptr read_value; + EXPECT_ANY_THROW(read_value = std::make_shared(input, variable)); +} + +TEST(type_prop, read_value_v6_static_types_do_not_match) { + auto input = make_shared(element::i32, Shape{1, 2, 64, 64}); + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + std::shared_ptr read_value; + EXPECT_ANY_THROW(read_value = std::make_shared(input, variable)); +} + +TEST(type_prop, read_value_v6_dyn_shape_type_in_variable) { + auto input = make_shared(element::f32, Shape{1, 2, 64, 64}); + + auto variable_info = op::util::VariableInfo{PartialShape{Dimension::dynamic(), 2, Dimension::dynamic(), 64}, + element::dynamic, + "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr read_value; + EXPECT_NO_THROW(read_value = std::make_shared(input, variable)); + + ASSERT_EQ(read_value->get_element_type(), element::dynamic); + ASSERT_EQ(read_value->get_output_partial_shape(0), + (PartialShape{Dimension::dynamic(), 2, Dimension::dynamic(), 64})); + ASSERT_EQ(read_value->get_variable_id(), "variable_id"); +} + +TEST(type_prop, read_value_v6_init_shape_is_in_range) { + auto input = make_shared(element::f32, PartialShape{1, 2, 64, 64}); + + auto variable_info = + op::util::VariableInfo{PartialShape{{1, 10}, {2, 5}, {64, 64}, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr read_value; + EXPECT_NO_THROW(read_value = std::make_shared(input, variable)); + + ASSERT_EQ(read_value->get_element_type(), element::f32); + ASSERT_EQ(read_value->get_output_partial_shape(0), (PartialShape{{1, 10}, {2, 5}, {64, 64}, 64})); + ASSERT_EQ(read_value->get_variable_id(), "variable_id"); +} + +TEST(type_prop, read_value_v6_init_shape_is_not_in_range) { + auto input = make_shared(element::f32, PartialShape{1, 2, 64, 64}); + + auto variable_info = op::util::VariableInfo{PartialShape{{2, 5}, {2, 5}, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr read_value; + EXPECT_ANY_THROW(read_value = std::make_shared(input, variable)); +} + +TEST(type_prop, read_value_v6_init_shape_is_not_in_range_2) { + auto input = make_shared(element::f32, PartialShape{{1, 2}, 2, 64, 64}); + + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr read_value; + EXPECT_ANY_THROW(read_value = std::make_shared(input, variable)); +} + +TEST(type_prop, read_value_v6_no_init) { + auto variable_info = op::util::VariableInfo{PartialShape{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + + std::shared_ptr read_value = std::make_shared(variable); + ASSERT_EQ(read_value->get_element_type(), element::f32); + ASSERT_EQ(read_value->get_output_partial_shape(0), (PartialShape{1, 2, 64, 64})); + ASSERT_EQ(read_value->get_variable_id(), "variable_id"); +} + +TEST(type_prop, read_value_labels_propagation) { + auto variable_pshape = PartialShape{1, 2, 64, 64}; + set_shape_labels(variable_pshape, 10); + auto variable_info = op::util::VariableInfo{variable_pshape, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + std::shared_ptr read_value = std::make_shared(variable); + EXPECT_THAT(get_shape_labels(read_value->get_output_partial_shape(0)), testing::ElementsAre(10, 11, 12, 13)); +} + +TEST(type_prop, DISABLED_read_value_labels_propagation_from_init_subgraph) { + auto input_pshape = PartialShape{1, 2, 64, 64}; + set_shape_labels(input_pshape, 10); + auto input = make_shared(element::f32, input_pshape); + auto variable_info = op::util::VariableInfo{{1, 2, 64, 64}, element::f32, "variable_id"}; + auto variable = std::make_shared(variable_info); + std::shared_ptr read_value = std::make_shared(input, variable); + EXPECT_THAT(get_shape_labels(read_value->get_output_partial_shape(0)), testing::ElementsAre(10, 11, 12, 13)); } diff --git a/src/core/tests/visitors/op/read_value.cpp b/src/core/tests/visitors/op/read_value.cpp index e1dbe4aedcb..37faa6473fe 100644 --- a/src/core/tests/visitors/op/read_value.cpp +++ b/src/core/tests/visitors/op/read_value.cpp @@ -36,6 +36,6 @@ TEST(attributes, readvalue_v6_op) { EXPECT_NO_THROW(auto g_read_value = ov::as_type_ptr(builder.create())); // attribute count - const auto expected_attr_count = 1; + const auto expected_attr_count = 3; EXPECT_EQ(builder.get_value_map_size(), expected_attr_count); } diff --git a/src/plugins/intel_cpu/tests/functional/subgraph_tests/src/stateful_model.cpp b/src/plugins/intel_cpu/tests/functional/subgraph_tests/src/stateful_model.cpp index 1ab4de1483e..10a66c6b663 100644 --- a/src/plugins/intel_cpu/tests/functional/subgraph_tests/src/stateful_model.cpp +++ b/src/plugins/intel_cpu/tests/functional/subgraph_tests/src/stateful_model.cpp @@ -81,7 +81,7 @@ public: // For each such a pair, its own variable object must be created. const std::string variable_name("variable0"); auto variable = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_name}); + ov::op::util::VariableInfo{inpShape, netPrc, variable_name}); // Creating ov::Model auto read = std::make_shared(init_const, variable); @@ -183,10 +183,10 @@ public: // The ReadValue/Assign operations must be used in pairs in the model. // For each such a pair, its own variable object must be created. auto variable0 = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, "variable0"}); + ov::op::util::VariableInfo{inpShape, netPrc, "variable0"}); auto variable1 = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, "variable1"}); + ov::op::util::VariableInfo{inpShape, netPrc, "variable1"}); // Creating ov::Model auto read0 = std::make_shared(init_const, variable0); @@ -263,8 +263,8 @@ TEST_F(StaticShapeTwoStatesModel, smoke_Run_Static_Two_States) { run_test(); } -// ┌─────────┐ ┌───────────┐ -// │ Param1 │--->│ ReadValue │.. +// ┌─────────┐Vary┌───────────┐ +// │ Param1 │--->| ReadValue │.. // └───┬──┬──┘ └─────┬─────┘ . // │ │ │ . // │ │ │ . @@ -283,7 +283,7 @@ TEST_F(StaticShapeTwoStatesModel, smoke_Run_Static_Two_States) { class DynamicShapeStatefulModel : public StatefulModelTest { public: - void SetUp() override { + void SetUp(bool use_param) { targetDevice = ov::test::utils::DEVICE_CPU; ov::element::Type netPrc = testPrc; @@ -297,10 +297,12 @@ public: // For each such a pair, its own variable object must be created. const std::string variable_name("variable0"); auto variable = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_name}); + ov::op::util::VariableInfo{inputDynamicShapes.front(), netPrc, variable_name}); // Creating ov::Model - auto read = std::make_shared(arg, variable); + auto read = use_param ? + std::make_shared(arg, variable) : + std::make_shared(variable); std::vector> args = {arg, read}; auto add = ngraph::builder::makeEltwise(arg, read, ngraph::helpers::EltwiseTypes::ADD); constexpr int concat_axis = 0; @@ -374,9 +376,35 @@ public: float_compare(vec_state.data(), actual_state, vec_state.size()); } } + +private: + using StatefulModelTest::TestsCommon::Test::SetUp; }; -TEST_F(DynamicShapeStatefulModel, smoke_Run_Stateful_Dynamic) { +class DynamicShapeStatefulModelDefault : public DynamicShapeStatefulModel { +public: + void SetUp() override { + constexpr bool use_param = false; + DynamicShapeStatefulModel::SetUp(use_param); + } +}; + +TEST_F(DynamicShapeStatefulModelDefault, smoke_Run_Stateful_Dynamic_Default) { + prepare(); + run_test(); + reset_state(); + run_test(); +} + +class DynamicShapeStatefulModelParam : public DynamicShapeStatefulModel { +public: + void SetUp() override { + constexpr bool use_param = true; + DynamicShapeStatefulModel::SetUp(use_param); + } +}; + +TEST_F(DynamicShapeStatefulModelParam, smoke_Run_Stateful_Dynamic_Param) { prepare(); run_test(); reset_state(); @@ -430,7 +458,7 @@ public: // For each such a pair, its own variable object must be created. const std::string variable_name("variable0"); auto variable = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_name}); + ov::op::util::VariableInfo{{-1, -1}, netPrc, variable_name}); // Creating ov::Model auto read = std::make_shared(init_param, variable); diff --git a/src/plugins/intel_gna/tests/functional/shared_tests_instances/single_layer_tests/memory.cpp b/src/plugins/intel_gna/tests/functional/shared_tests_instances/single_layer_tests/memory.cpp index 9caceecafed..0c0091bd7d0 100644 --- a/src/plugins/intel_gna/tests/functional/shared_tests_instances/single_layer_tests/memory.cpp +++ b/src/plugins/intel_gna/tests/functional/shared_tests_instances/single_layer_tests/memory.cpp @@ -30,7 +30,7 @@ protected: void MemoryTestGna::CreateCommonFunc() { ov::ParameterVector param{std::make_shared(ngPrc, ov::Shape(inputShape))}; - const auto variable_info = VariableInfo{PartialShape::dynamic(), element::dynamic, "v0"}; + const auto variable_info = VariableInfo{inputShape, ngPrc, "v0"}; auto variable = std::make_shared(variable_info); auto min55 = ngraph::builder::makeConstant(ngPrc, {}, {-55.001678466796875f}); diff --git a/src/plugins/intel_gna/tests/unit/transformations/gna_insert_copy_layer.cpp b/src/plugins/intel_gna/tests/unit/transformations/gna_insert_copy_layer.cpp index 7d3e80197f5..c3d778b903e 100644 --- a/src/plugins/intel_gna/tests/unit/transformations/gna_insert_copy_layer.cpp +++ b/src/plugins/intel_gna/tests/unit/transformations/gna_insert_copy_layer.cpp @@ -736,12 +736,13 @@ TEST_P(InsertCopyLayerMultiParamNFLConcatMemoryTest, CompareWithRefs) { ngraph::Shape in_shape = {1, 2, 4}; ngraph::Shape shape1 = {1, 1, 2, 4}; ngraph::Shape shape2 = {2, 4}; + ngraph::PartialShape allowed_shape = ngraph::PartialShape::dynamic(); size_t axis = 0; const std::string variable_name("variable_id"); { - auto variable = - std::make_shared(ov::op::util::VariableInfo{shape2, ngraph::element::i64, variable_name}); + auto variable = std::make_shared( + ov::op::util::VariableInfo{allowed_shape, ngraph::element::i64, variable_name}); auto input = std::make_shared(ngraph::element::i64, in_shape); auto reshape1 = ov::op::util::reshapeTo(input, shape1); auto reshape2 = ov::op::util::reshapeTo(input, shape2); @@ -761,8 +762,8 @@ TEST_P(InsertCopyLayerMultiParamNFLConcatMemoryTest, CompareWithRefs) { } { - auto variable = - std::make_shared(ov::op::util::VariableInfo{shape2, ngraph::element::i64, variable_name}); + auto variable = std::make_shared( + ov::op::util::VariableInfo{allowed_shape, ngraph::element::i64, variable_name}); auto input = std::make_shared(ngraph::element::i64, in_shape); auto reshape1 = ov::op::util::reshapeTo(input, shape1); auto reshape2 = ov::op::util::reshapeTo(input, shape2); @@ -1107,6 +1108,7 @@ TEST_P(InsertCopyLayerConcatNFLMemoryTest, CompareWithRefs) { std::shared_ptr ref_func; ngraph::Shape shape = {1, 2, 2, 4}; ngraph::Shape in_shape = {1, 2, 4}; + ngraph::PartialShape allowed_shape = {}; size_t axis = 0; const std::string variable_name("variable_id"); @@ -1178,12 +1180,13 @@ TEST_P(InsertCopyLayerSplitMemoryTest, CompareWithRefs) { std::shared_ptr ref_func; ngraph::Shape in_shape{10}; ngraph::Shape out_shape{5}; + ngraph::PartialShape allowed_shape = {{5, 10}}; size_t axis = 0; const std::string variable_name("variable_id"); { auto variable = std::make_shared( - ov::op::util::VariableInfo{out_shape, ngraph::element::i64, variable_name}); + ov::op::util::VariableInfo{allowed_shape, ngraph::element::i64, variable_name}); auto input = std::make_shared(ngraph::element::i64, in_shape); OPENVINO_SUPPRESS_DEPRECATED_START auto split = ngraph::builder::makeSplit(input, ngraph::element::i64, 1, axis); @@ -1203,7 +1206,7 @@ TEST_P(InsertCopyLayerSplitMemoryTest, CompareWithRefs) { { auto variable = std::make_shared( - ov::op::util::VariableInfo{out_shape, ngraph::element::i64, variable_name}); + ov::op::util::VariableInfo{allowed_shape, ngraph::element::i64, variable_name}); auto input = std::make_shared(ngraph::element::i64, in_shape); OPENVINO_SUPPRESS_DEPRECATED_START auto split = ngraph::builder::makeSplit(input, ngraph::element::i64, 1, axis); @@ -1250,12 +1253,13 @@ TEST_P(InsertCopyLayerSplitNFLMemoryTest, CompareWithRefs) { ngraph::Shape in_shape{10}; ngraph::Shape shape{1, 5}; ngraph::Shape out_shape{5}; + ngraph::PartialShape allowed_shape = ngraph::PartialShape::dynamic(); size_t axis = 0; const std::string variable_name("variable_id"); { auto variable = std::make_shared( - ov::op::util::VariableInfo{out_shape, ngraph::element::i64, variable_name}); + ov::op::util::VariableInfo{allowed_shape, ngraph::element::i64, variable_name}); auto input = std::make_shared(ngraph::element::i64, in_shape); OPENVINO_SUPPRESS_DEPRECATED_START auto split = ngraph::builder::makeSplit(input, ngraph::element::i64, 2, axis); @@ -1276,7 +1280,7 @@ TEST_P(InsertCopyLayerSplitNFLMemoryTest, CompareWithRefs) { { auto variable = std::make_shared( - ov::op::util::VariableInfo{out_shape, ngraph::element::i64, variable_name}); + ov::op::util::VariableInfo{allowed_shape, ngraph::element::i64, variable_name}); auto input = std::make_shared(ngraph::element::i64, in_shape); OPENVINO_SUPPRESS_DEPRECATED_START auto split = ngraph::builder::makeSplit(input, ngraph::element::i64, 2, axis); diff --git a/src/plugins/template/tests/functional/op_reference/memory.cpp b/src/plugins/template/tests/functional/op_reference/memory.cpp index b2fb97d6acc..bcdb29cdac5 100644 --- a/src/plugins/template/tests/functional/op_reference/memory.cpp +++ b/src/plugins/template/tests/functional/op_reference/memory.cpp @@ -97,8 +97,8 @@ private: const element::Type& input_type, const std::string variable_id) { auto in = std::make_shared(input_type, input_shape); - auto variable = std::make_shared( - op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, variable_id}); + auto variable = + std::make_shared(op::util::VariableInfo{input_shape, input_type, variable_id}); auto assign = std::make_shared(in, variable); auto read_value = std::make_shared(assign, variable); return std::make_shared(OutputVector{read_value}, @@ -312,8 +312,8 @@ std::shared_ptr CreateFunction_ReadValueAssingAdd(const ov::Shape& in const std::vector& variable_id) { auto in = std::make_shared(input_type, input_shape); auto c = std::make_shared(input_type, input_shape, 0); - auto variable = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_id[0]}); + auto variable = + std::make_shared(ov::op::util::VariableInfo{input_shape, input_type, variable_id[0]}); auto read_value = std::make_shared(c, variable); auto add = std::make_shared(in, read_value); auto assign = std::make_shared(add, variable); @@ -326,10 +326,10 @@ std::shared_ptr CreateFunction_ReadValueAssingAddMultiVariable(const const ov::element::Type& input_type, const std::vector& variable_id) { auto in = std::make_shared(input_type, input_shape); - auto variable1 = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_id[0]}); - auto variable2 = std::make_shared( - ov::op::util::VariableInfo{ov::PartialShape::dynamic(), ov::element::dynamic, variable_id[1]}); + auto variable1 = + std::make_shared(ov::op::util::VariableInfo{input_shape, input_type, variable_id[0]}); + auto variable2 = + std::make_shared(ov::op::util::VariableInfo{input_shape, input_type, variable_id[1]}); auto read_value1 = std::make_shared(in, variable1); auto read_value2 = std::make_shared(in, variable2); auto add1 = std::make_shared(read_value1, read_value2); diff --git a/src/tests/functional/shared_test_classes/src/single_layer/memory.cpp b/src/tests/functional/shared_test_classes/src/single_layer/memory.cpp index 323c2def823..5a0ef851dee 100644 --- a/src/tests/functional/shared_test_classes/src/single_layer/memory.cpp +++ b/src/tests/functional/shared_test_classes/src/single_layer/memory.cpp @@ -184,7 +184,7 @@ namespace LayerTestsDefinitions { void MemoryTest::CreateCommonFunc() { ov::ParameterVector param {std::make_shared(ngPrc, ov::Shape(inputShape))}; const auto variable_info = targetDevice == ov::test::utils::DEVICE_GPU ? - VariableInfo{Shape{inputShape}, ngPrc, "v0"} : VariableInfo{PartialShape::dynamic(), element::dynamic, "v0"}; + VariableInfo{Shape{inputShape}, ngPrc, "v0"} : VariableInfo{inputShape, ngPrc, "v0"}; auto variable = std::make_shared(variable_info); auto read_value = CreateReadValueOp(param.at(0), variable); auto add = std::make_shared(read_value, param.at(0)); diff --git a/src/tests/functional/shared_test_classes/src/single_op/memory.cpp b/src/tests/functional/shared_test_classes/src/single_op/memory.cpp index 639d110886e..4c475cc0c38 100644 --- a/src/tests/functional/shared_test_classes/src/single_op/memory.cpp +++ b/src/tests/functional/shared_test_classes/src/single_op/memory.cpp @@ -55,9 +55,7 @@ void MemoryLayerTest::SetUp() { void MemoryLayerTest::CreateCommonFunc(ov::element::Type model_type, ov::Shape input_shape) { ov::ParameterVector param {std::make_shared(model_type, input_shape)}; - const auto variable_info = targetDevice == ov::test::utils::DEVICE_GPU ? - ov::op::util::VariableInfo{input_shape, model_type, "v0"} : - ov::op::util::VariableInfo{PartialShape::dynamic(), element::dynamic, "v0"}; + const auto variable_info = ov::op::util::VariableInfo{input_shape, model_type, "v0"}; auto variable = std::make_shared(variable_info); std::shared_ptr read_value; diff --git a/src/tests/functional/shared_test_classes/src/subgraph/concat_multi_input.cpp b/src/tests/functional/shared_test_classes/src/subgraph/concat_multi_input.cpp index dcad9cb8dbf..b6808f06c3c 100644 --- a/src/tests/functional/shared_test_classes/src/subgraph/concat_multi_input.cpp +++ b/src/tests/functional/shared_test_classes/src/subgraph/concat_multi_input.cpp @@ -112,7 +112,7 @@ void ConcatMultiInput::GenerateMemoryModel() { int axis = 1; ov::ParameterVector input{std::make_shared(ngPrc, ov::Shape(inputShapes[0]))}; - auto variable = std::make_shared(ngraph::VariableInfo{ngraph::PartialShape::dynamic(), + auto variable = std::make_shared(ngraph::VariableInfo{ov::Shape(inputShapes[0]), ngraph::element::dynamic, "concat_input_memory"}); auto mem_i = std::make_shared(ngPrc, inputShapes[0]); auto mem_r = std::make_shared(mem_i, variable); diff --git a/src/tests/functional/shared_test_classes/src/subgraph/memory_LSTMCell.cpp b/src/tests/functional/shared_test_classes/src/subgraph/memory_LSTMCell.cpp index 483e862ac45..eb3eb70ba4b 100644 --- a/src/tests/functional/shared_test_classes/src/subgraph/memory_LSTMCell.cpp +++ b/src/tests/functional/shared_test_classes/src/subgraph/memory_LSTMCell.cpp @@ -73,9 +73,9 @@ namespace SubgraphTestsDefinitions { auto cell_memory_constant = builder::makeConstant(ngPrc, cell_memory_dims, cell_memory_init); auto var_cell = - std::make_shared(VariableInfo{PartialShape::dynamic(), element::dynamic, "cell_state_1"}); + std::make_shared(VariableInfo{PartialShape(cell_memory_dims), ngPrc, "cell_state_1"}); auto var_hidden = - std::make_shared(VariableInfo{PartialShape::dynamic(), element::dynamic, "hidden_state_1"}); + std::make_shared(VariableInfo{PartialShape(cell_memory_dims), ngPrc, "hidden_state_1"}); auto cell_memory_read = std::make_shared(cell_memory_constant, var_cell); auto hidden_memory_constant = builder::makeConstant(ngPrc, hidden_memory_dims, hidden_memory_init); diff --git a/src/tests/test_utils/common_test_utils/src/graph_comparator.cpp b/src/tests/test_utils/common_test_utils/src/graph_comparator.cpp index f6cc70ed10a..3ec3fa06827 100644 --- a/src/tests/test_utils/common_test_utils/src/graph_comparator.cpp +++ b/src/tests/test_utils/common_test_utils/src/graph_comparator.cpp @@ -337,7 +337,7 @@ public: m_result(not_null(result)) {} bool result_and_parameter_match() const { - if (m_parameter->get_element_type() != m_result->output(0).get_element_type()) { + if (!m_parameter->get_element_type().compatible(m_result->output(0).get_element_type())) { return false; } const auto& param_shape = m_parameter->get_partial_shape(); diff --git a/tools/mo/openvino/tools/mo/front/kaldi/replace_lstm_node_pattern.py b/tools/mo/openvino/tools/mo/front/kaldi/replace_lstm_node_pattern.py index cbf04d4ce7a..b011450fed6 100644 --- a/tools/mo/openvino/tools/mo/front/kaldi/replace_lstm_node_pattern.py +++ b/tools/mo/openvino/tools/mo/front/kaldi/replace_lstm_node_pattern.py @@ -81,7 +81,9 @@ class ReplaceLSTMNodePattern(FrontReplacementOp): init_value_prev_lstm_output = create_const_with_batch_from_input(input_out_port, node.gifo_r_weights_shape[1]) prev_lstm_output = ReadValue(graph, {'name': 'prev_memory_output', - 'variable_id': memory_pair_input + 'variable_id': memory_pair_input, + 'variable_shape': None, + 'variable_type': None }).create_node() prev_lstm_output.in_port(0).connect(init_value_prev_lstm_output.out_port(0)) @@ -121,7 +123,10 @@ class ReplaceLSTMNodePattern(FrontReplacementOp): init_value_prev_lstm_state = create_const_with_batch_from_input(split_joined_input.out_port(0), node.input_gate_weights.shape[0]) prev_lstm_state = ReadValue(graph, {'name': 'prev_memory_state', - 'variable_id': memory_pair_output}).create_node() + 'variable_id': memory_pair_output, + 'variable_shape': None, + 'variable_type': None + }).create_node() prev_lstm_state.in_port(0).connect(init_value_prev_lstm_state.out_port(0)) # *Memory(state) -> *ScaleShift(input) diff --git a/tools/mo/openvino/tools/mo/middle/InsertSelect.py b/tools/mo/openvino/tools/mo/middle/InsertSelect.py index e011dc15aca..8c567e40b51 100644 --- a/tools/mo/openvino/tools/mo/middle/InsertSelect.py +++ b/tools/mo/openvino/tools/mo/middle/InsertSelect.py @@ -177,7 +177,10 @@ class AddSelectBeforeMemoryNodePattern(MiddleReplacementPattern): else: init_value_mem_out = create_const_with_batch_from_input(in_node_port, context_len, precision=np.int32) mem_out = ReadValue(graph, {'name': 'iteration_number', - 'variable_id': 'iteration_' + node.name}).create_node() + 'variable_id': 'iteration_' + node.name, + 'variable_shape': None, + 'variable_type': None + }).create_node() mem_out.in_port(0).connect(init_value_mem_out.out_port(0)) cut_first = Crop(graph, {'name': 'cut_first', 'axis': int64_array([1]), 'offset': int64_array([1]), 'dim': int64_array([context_len - 1])}).create_node() diff --git a/tools/mo/openvino/tools/mo/middle/ReplaceMemoryOffsetWithSplice.py b/tools/mo/openvino/tools/mo/middle/ReplaceMemoryOffsetWithSplice.py index 607c10f93ac..f793cd3a74a 100644 --- a/tools/mo/openvino/tools/mo/middle/ReplaceMemoryOffsetWithSplice.py +++ b/tools/mo/openvino/tools/mo/middle/ReplaceMemoryOffsetWithSplice.py @@ -141,7 +141,11 @@ class ReplaceMemoryOffsetWithMemoryNodePattern(MiddleReplacementPattern): init_value_memory_out = Const(graph, {'name': 'init_value_' + pair_name, 'value': np.zeros(int64_array([in_shape[0], in_shape[1]*node_t]), dtype=np.float32), 'shape': int64_array([in_shape[0], in_shape[1]*node_t])}).create_node() - memory_out = ReadValue(graph, {'name': pair_name, 'variable_id': node_name+pair_name}).create_node() + memory_out = ReadValue(graph, {'name': pair_name, + 'variable_id': node_name+pair_name, + 'variable_shape': None, + 'variable_type': None + }).create_node() init_value_memory_out.out_port(0).connect(memory_out.in_port(0)) if node_t > 1: diff --git a/tools/mo/openvino/tools/mo/middle/ReplaceSpliceNodePattern.py b/tools/mo/openvino/tools/mo/middle/ReplaceSpliceNodePattern.py index b6a370f56c7..906c8468dcb 100644 --- a/tools/mo/openvino/tools/mo/middle/ReplaceSpliceNodePattern.py +++ b/tools/mo/openvino/tools/mo/middle/ReplaceSpliceNodePattern.py @@ -60,7 +60,10 @@ class ReplaceSpliceNodePattern(MiddleReplacementPattern): memory_pair_id = unique_id('id') # Memory(in) input_memory = ReadValue(graph, {'name': 'prev_splice_memory', - 'variable_id': memory_pair_id}).create_node() + 'variable_id': memory_pair_id, + 'variable_shape': None, + 'variable_type': None + }).create_node() # Memory(in) \ # Crop @@ -102,7 +105,10 @@ class ReplaceSpliceNodePattern(MiddleReplacementPattern): 'shape': int64_array([in_shape[0], memory_size_constdim])}).create_node() input_memory_const_dim = ReadValue(graph, {'name': 'const_dim_in_memory', - 'variable_id': memory_pair_id}).create_node() + 'variable_id': memory_pair_id, + 'variable_shape': None, + 'variable_type': None + }).create_node() init_value_input_memory_const_dim.out_port(0).connect(input_memory_const_dim.in_port(0)) crop_const_dim = Crop(graph, {'name': 'const_dim_crop', diff --git a/tools/mo/openvino/tools/mo/ops/read_value.py b/tools/mo/openvino/tools/mo/ops/read_value.py index 7e0ffdfb3a6..1858787aec1 100644 --- a/tools/mo/openvino/tools/mo/ops/read_value.py +++ b/tools/mo/openvino/tools/mo/ops/read_value.py @@ -1,9 +1,12 @@ # Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 +import numpy as np -from openvino.tools.mo.graph.graph import Graph, Node from openvino.tools.mo.ops.op import Op - +from openvino.tools.mo.middle.passes.convert_data_type import np_data_type_to_destination_type +from openvino.tools.mo.front.common.partial_infer.utils import unmask_shape +from openvino.tools.mo.graph.graph import Graph, Node +from openvino.runtime import PartialShape class ReadValue(Op): op = 'ReadValue' @@ -19,15 +22,31 @@ class ReadValue(Op): 'out_ports_count': 1, }, attrs) + @staticmethod + def shape_serialize(node): + shape = node.soft_get('variable_shape') + if isinstance(shape, np.ndarray): + return shape.tolist() + if isinstance(shape, np.ma.masked_array): + shape = unmask_shape(shape) + if isinstance(shape, PartialShape): + return shape.to_string() + raise Exception("Unknown shape type in user_shape attribute {}".format(type(shape))) + def backend_attrs(self): - return ['variable_id'] + return ['variable_id', + ('variable_shape', lambda node: self.shape_serialize(node)), + ('variable_type', lambda node: np_data_type_to_destination_type(node.variable_type))] @staticmethod def type_infer(node: Node): + node.variable_type = node.in_port(0).get_data_type() node.out_port(0).set_data_type(node.in_port(0).get_data_type()) @staticmethod def infer(node: Node): assert node.has_valid('variable_id'), \ "There is no required attribute variable_id in ReadValue op with name " + node.id - node.out_port(0).data.set_shape(node.in_port(0).data.get_shape()) + in_shape = node.in_port(0).data.get_shape() + node.out_port(0).data.set_shape(in_shape) + node.variable_shape = in_shape