Merge 18d4c19123 into 43de73cbd6
This commit is contained in:
commit
64edc4b25f
|
|
@ -84,6 +84,50 @@ static void set_correct_variables_for_assign_ops(const std::shared_ptr<ov::Model
|
|||
}
|
||||
}
|
||||
|
||||
static ov::Output<ov::Node> output_from_handle(ov::Model& model, const py::handle& handle) {
|
||||
if (py::isinstance<py::int_>(handle)) {
|
||||
return model.input(handle.cast<size_t>());
|
||||
} else if (py::isinstance<py::str>(handle)) {
|
||||
return model.input(handle.cast<std::string>());
|
||||
} else if (py::isinstance<ov::Output<ov::Node>>(handle)) {
|
||||
return handle.cast<ov::Output<ov::Node>>();
|
||||
} else {
|
||||
throw py::type_error("Incorrect key type " + std::string(py::str(handle.get_type())) +
|
||||
" to reshape a model, expected keys as openvino.runtime.Output, int or str.");
|
||||
}
|
||||
}
|
||||
|
||||
static ov::PartialShape partial_shape_from_handle(const py::handle& handle) {
|
||||
if (py::isinstance<ov::PartialShape>(handle)) {
|
||||
return handle.cast<ov::PartialShape>();
|
||||
} else if (py::isinstance<py::list>(handle) || py::isinstance<py::tuple>(handle)) {
|
||||
return Common::partial_shape_from_list(handle.cast<py::list>());
|
||||
} else if (py::isinstance<py::str>(handle)) {
|
||||
return ov::PartialShape(handle.cast<std::string>());
|
||||
} else {
|
||||
throw py::type_error(
|
||||
"Incorrect value type " + std::string(py::str(handle.get_type())) +
|
||||
" to reshape a model, expected values as openvino.runtime.PartialShape, str, list or tuple.");
|
||||
}
|
||||
}
|
||||
|
||||
static std::string string_from_handle(const py::handle& handle) {
|
||||
if (py::isinstance<py::str>(handle)) {
|
||||
return handle.cast<std::string>();
|
||||
} else {
|
||||
throw py::type_error("Incorrect key type " + std::string(py::str(handle.get_type())) +
|
||||
" to reshape a model, expected values as str.");
|
||||
}
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string, ov::PartialShape> get_variables_shapes(const py::dict& variables_shapes) {
|
||||
std::unordered_map<std::string, ov::PartialShape> variables_shape_map;
|
||||
for (const auto& item : variables_shapes) {
|
||||
variables_shape_map.emplace(string_from_handle(item.first), partial_shape_from_handle(item.second));
|
||||
}
|
||||
return variables_shape_map;
|
||||
}
|
||||
|
||||
void regclass_graph_Model(py::module m) {
|
||||
py::class_<ov::Model, std::shared_ptr<ov::Model>> model(m, "Model", py::module_local());
|
||||
model.doc() = "openvino.runtime.Model wraps ov::Model";
|
||||
|
|
@ -309,109 +353,186 @@ void regclass_graph_Model(py::module m) {
|
|||
|
||||
model.def(
|
||||
"reshape",
|
||||
[](ov::Model& self, const ov::PartialShape& partial_shape) {
|
||||
self.reshape(partial_shape);
|
||||
[](ov::Model& self, const ov::PartialShape& partial_shape, const py::dict& variables_shapes) {
|
||||
const auto new_variable_shapes = get_variables_shapes(variables_shapes);
|
||||
py::gil_scoped_release release;
|
||||
self.reshape(partial_shape, new_variable_shapes);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("partial_shape"),
|
||||
py::arg("variables_shapes") = py::dict(),
|
||||
R"(
|
||||
Reshape model input.
|
||||
|
||||
The allowed types of keys in the `variables_shapes` dictionary is `str`.
|
||||
The allowed types of values in the `variables_shapes` are:
|
||||
|
||||
(1) `openvino.runtime.PartialShape`
|
||||
(2) `list` consisting of dimensions
|
||||
(3) `tuple` consisting of dimensions
|
||||
(4) `str`, string representation of `openvino.runtime.PartialShape`
|
||||
|
||||
When list or tuple are used to describe dimensions, each dimension can be written in form:
|
||||
|
||||
(1) non-negative `int` which means static value for the dimension
|
||||
(2) `[min, max]`, dynamic dimension where `min` specifies lower bound and `max` specifies upper bound;
|
||||
the range includes both `min` and `max`; using `-1` for `min` or `max` means no known bound (3) `(min,
|
||||
max)`, the same as above (4) `-1` is a dynamic dimension without known bounds (4)
|
||||
`openvino.runtime.Dimension` (5) `str` using next syntax:
|
||||
'?' - to define fully dynamic dimension
|
||||
'1' - to define dimension which length is 1
|
||||
'1..10' - to define bounded dimension
|
||||
'..10' or '1..' to define dimension with only lower or only upper limit
|
||||
|
||||
GIL is released while running this function.
|
||||
|
||||
:param partial_shape: New shape.
|
||||
:type partial_shape: openvino.runtime.PartialShape
|
||||
:param variables_shapes: New shapes for variables
|
||||
:type variables_shapes: Dict[keys, values]
|
||||
:return : void
|
||||
)");
|
||||
|
||||
model.def(
|
||||
"reshape",
|
||||
[](ov::Model& self, const py::list& partial_shape) {
|
||||
ov::PartialShape new_shape(Common::partial_shape_from_list(partial_shape));
|
||||
[](ov::Model& self, const py::list& partial_shape, const py::dict& variables_shapes) {
|
||||
const auto new_shape = Common::partial_shape_from_list(partial_shape);
|
||||
const auto new_variables_shapes = get_variables_shapes(variables_shapes);
|
||||
py::gil_scoped_release release;
|
||||
self.reshape(new_shape);
|
||||
self.reshape(new_shape, new_variables_shapes);
|
||||
},
|
||||
py::arg("partial_shape"),
|
||||
py::arg("variables_shapes") = py::dict(),
|
||||
R"(
|
||||
Reshape model input.
|
||||
|
||||
The allowed types of keys in the `variables_shapes` dictionary is `str`.
|
||||
The allowed types of values in the `variables_shapes` are:
|
||||
|
||||
(1) `openvino.runtime.PartialShape`
|
||||
(2) `list` consisting of dimensions
|
||||
(3) `tuple` consisting of dimensions
|
||||
(4) `str`, string representation of `openvino.runtime.PartialShape`
|
||||
|
||||
When list or tuple are used to describe dimensions, each dimension can be written in form:
|
||||
|
||||
(1) non-negative `int` which means static value for the dimension
|
||||
(2) `[min, max]`, dynamic dimension where `min` specifies lower bound and `max` specifies upper bound;
|
||||
the range includes both `min` and `max`; using `-1` for `min` or `max` means no known bound (3) `(min,
|
||||
max)`, the same as above (4) `-1` is a dynamic dimension without known bounds (4)
|
||||
`openvino.runtime.Dimension` (5) `str` using next syntax:
|
||||
'?' - to define fully dynamic dimension
|
||||
'1' - to define dimension which length is 1
|
||||
'1..10' - to define bounded dimension
|
||||
'..10' or '1..' to define dimension with only lower or only upper limit
|
||||
|
||||
GIL is released while running this function.
|
||||
|
||||
:param partial_shape: New shape.
|
||||
:type partial_shape: list
|
||||
:param variables_shapes: New shapes for variables
|
||||
:type variables_shapes: Dict[keys, values]
|
||||
:return : void
|
||||
)");
|
||||
|
||||
model.def(
|
||||
"reshape",
|
||||
[](ov::Model& self, const py::tuple& partial_shape) {
|
||||
ov::PartialShape new_shape(Common::partial_shape_from_list(partial_shape.cast<py::list>()));
|
||||
[](ov::Model& self, const py::tuple& partial_shape, const py::dict& variables_shapes) {
|
||||
const auto new_shape = Common::partial_shape_from_list(partial_shape.cast<py::list>());
|
||||
const auto new_variables_shapes = get_variables_shapes(variables_shapes);
|
||||
py::gil_scoped_release release;
|
||||
self.reshape(new_shape);
|
||||
self.reshape(new_shape, new_variables_shapes);
|
||||
},
|
||||
py::arg("partial_shape"),
|
||||
py::arg("variables_shapes") = py::dict(),
|
||||
R"(
|
||||
Reshape model input.
|
||||
|
||||
The allowed types of keys in the `variables_shapes` dictionary is `str`.
|
||||
The allowed types of values in the `variables_shapes` are:
|
||||
|
||||
(1) `openvino.runtime.PartialShape`
|
||||
(2) `list` consisting of dimensions
|
||||
(3) `tuple` consisting of dimensions
|
||||
(4) `str`, string representation of `openvino.runtime.PartialShape`
|
||||
|
||||
When list or tuple are used to describe dimensions, each dimension can be written in form:
|
||||
|
||||
(1) non-negative `int` which means static value for the dimension
|
||||
(2) `[min, max]`, dynamic dimension where `min` specifies lower bound and `max` specifies upper bound;
|
||||
the range includes both `min` and `max`; using `-1` for `min` or `max` means no known bound (3) `(min,
|
||||
max)`, the same as above (4) `-1` is a dynamic dimension without known bounds (4)
|
||||
`openvino.runtime.Dimension` (5) `str` using next syntax:
|
||||
'?' - to define fully dynamic dimension
|
||||
'1' - to define dimension which length is 1
|
||||
'1..10' - to define bounded dimension
|
||||
'..10' or '1..' to define dimension with only lower or only upper limit
|
||||
|
||||
GIL is released while running this function.
|
||||
|
||||
:param partial_shape: New shape.
|
||||
:type partial_shape: tuple
|
||||
:param variables_shapes: New shapes for variables
|
||||
:type variables_shapes: Dict[keys, values]
|
||||
:return : void
|
||||
)");
|
||||
|
||||
model.def(
|
||||
"reshape",
|
||||
[](ov::Model& self, const std::string& partial_shape) {
|
||||
self.reshape(ov::PartialShape(partial_shape));
|
||||
[](ov::Model& self, const std::string& partial_shape, const py::dict& variables_shapes) {
|
||||
const auto new_variables_shape = get_variables_shapes(variables_shapes);
|
||||
py::gil_scoped_release release;
|
||||
self.reshape(ov::PartialShape(partial_shape), new_variables_shape);
|
||||
},
|
||||
py::call_guard<py::gil_scoped_release>(),
|
||||
py::arg("partial_shape"),
|
||||
py::arg("variables_shapes") = py::dict(),
|
||||
R"(
|
||||
Reshape model input.
|
||||
|
||||
The allowed types of keys in the `variables_shapes` dictionary is `str`.
|
||||
The allowed types of values in the `variables_shapes` are:
|
||||
|
||||
(1) `openvino.runtime.PartialShape`
|
||||
(2) `list` consisting of dimensions
|
||||
(3) `tuple` consisting of dimensions
|
||||
(4) `str`, string representation of `openvino.runtime.PartialShape`
|
||||
|
||||
When list or tuple are used to describe dimensions, each dimension can be written in form:
|
||||
|
||||
(1) non-negative `int` which means static value for the dimension
|
||||
(2) `[min, max]`, dynamic dimension where `min` specifies lower bound and `max` specifies upper bound;
|
||||
the range includes both `min` and `max`; using `-1` for `min` or `max` means no known bound (3) `(min,
|
||||
max)`, the same as above (4) `-1` is a dynamic dimension without known bounds (4)
|
||||
`openvino.runtime.Dimension` (5) `str` using next syntax:
|
||||
'?' - to define fully dynamic dimension
|
||||
'1' - to define dimension which length is 1
|
||||
'1..10' - to define bounded dimension
|
||||
'..10' or '1..' to define dimension with only lower or only upper limit
|
||||
|
||||
|
||||
GIL is released while running this function.
|
||||
|
||||
:param partial_shape: New shape.
|
||||
:type partial_shape: str
|
||||
:param variables_shapes: New shapes for variables
|
||||
:type variables_shapes: Dict[keys, values]
|
||||
:return : void
|
||||
)");
|
||||
|
||||
model.def(
|
||||
"reshape",
|
||||
[](ov::Model& self, const py::dict& partial_shapes) {
|
||||
[](ov::Model& self, const py::dict& partial_shapes, const py::dict& variables_shapes) {
|
||||
std::map<ov::Output<ov::Node>, ov::PartialShape> new_shapes;
|
||||
for (const auto& item : partial_shapes) {
|
||||
std::pair<ov::Output<ov::Node>, ov::PartialShape> new_shape;
|
||||
// check keys
|
||||
if (py::isinstance<py::int_>(item.first)) {
|
||||
new_shape.first = self.input(item.first.cast<size_t>());
|
||||
} else if (py::isinstance<py::str>(item.first)) {
|
||||
new_shape.first = self.input(item.first.cast<std::string>());
|
||||
} else if (py::isinstance<ov::Output<ov::Node>>(item.first)) {
|
||||
new_shape.first = item.first.cast<ov::Output<ov::Node>>();
|
||||
} else {
|
||||
throw py::type_error("Incorrect key type " + std::string(py::str(item.first.get_type())) +
|
||||
" to reshape a model, expected keys as openvino.runtime.Output, int or str.");
|
||||
}
|
||||
// check values
|
||||
if (py::isinstance<ov::PartialShape>(item.second)) {
|
||||
new_shape.second = item.second.cast<ov::PartialShape>();
|
||||
} else if (py::isinstance<py::list>(item.second) || py::isinstance<py::tuple>(item.second)) {
|
||||
new_shape.second = Common::partial_shape_from_list(item.second.cast<py::list>());
|
||||
} else if (py::isinstance<py::str>(item.second)) {
|
||||
new_shape.second = ov::PartialShape(item.second.cast<std::string>());
|
||||
} else {
|
||||
throw py::type_error(
|
||||
"Incorrect value type " + std::string(py::str(item.second.get_type())) +
|
||||
" to reshape a model, expected values as openvino.runtime.PartialShape, str, list or tuple.");
|
||||
}
|
||||
new_shapes.insert(new_shape);
|
||||
new_shapes.emplace_hint(new_shapes.end(),
|
||||
output_from_handle(self, item.first),
|
||||
partial_shape_from_handle(item.second));
|
||||
}
|
||||
const auto new_variables_shapes = get_variables_shapes(variables_shapes);
|
||||
py::gil_scoped_release release;
|
||||
self.reshape(new_shapes);
|
||||
self.reshape(new_shapes, new_variables_shapes);
|
||||
},
|
||||
py::arg("partial_shapes"),
|
||||
py::arg("variables_shapes") = py::dict(),
|
||||
R"( Reshape model inputs.
|
||||
|
||||
The allowed types of keys in the `partial_shapes` dictionary are:
|
||||
|
|
@ -440,12 +561,34 @@ void regclass_graph_Model(py::module m) {
|
|||
'1..10' - to define bounded dimension
|
||||
'..10' or '1..' to define dimension with only lower or only upper limit
|
||||
|
||||
Reshape model input.
|
||||
The allowed types of keys in the `variables_shapes` dictionary is `str`.
|
||||
The allowed types of values in the `variables_shapes` are:
|
||||
|
||||
(1) `openvino.runtime.PartialShape`
|
||||
(2) `list` consisting of dimensions
|
||||
(3) `tuple` consisting of dimensions
|
||||
(4) `str`, string representation of `openvino.runtime.PartialShape`
|
||||
|
||||
When list or tuple are used to describe dimensions, each dimension can be written in form:
|
||||
|
||||
(1) non-negative `int` which means static value for the dimension
|
||||
(2) `[min, max]`, dynamic dimension where `min` specifies lower bound and `max` specifies upper bound;
|
||||
the range includes both `min` and `max`; using `-1` for `min` or `max` means no known bound (3) `(min,
|
||||
max)`, the same as above (4) `-1` is a dynamic dimension without known bounds (4)
|
||||
`openvino.runtime.Dimension` (5) `str` using next syntax:
|
||||
'?' - to define fully dynamic dimension
|
||||
'1' - to define dimension which length is 1
|
||||
'1..10' - to define bounded dimension
|
||||
'..10' or '1..' to define dimension with only lower or only upper limit
|
||||
|
||||
Reshape model inputs.
|
||||
|
||||
GIL is released while running this function.
|
||||
|
||||
:param partial_shapes: New shapes.
|
||||
:type partial_shapes: Dict[keys, values]
|
||||
:param variables_shapes: New shapes for variables
|
||||
:type variables_shapes: Dict[keys, values]
|
||||
)");
|
||||
|
||||
model.def("get_output_size",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,18 @@ from openvino import (
|
|||
from openvino.runtime import Output
|
||||
from openvino.runtime.op.util import VariableInfo, Variable
|
||||
|
||||
from tests.utils.helpers import generate_add_model, generate_model_with_memory, create_filename_for_test
|
||||
from tests.utils.helpers import (
|
||||
generate_add_model,
|
||||
generate_model_with_memory,
|
||||
create_filename_for_test,
|
||||
)
|
||||
|
||||
|
||||
def make_add_with_variable_model(shape, variable_id: str, dtype=np.float32) -> Model:
|
||||
param1 = ops.parameter(Shape(shape), dtype=dtype, name="data1")
|
||||
param2 = ops.parameter(Shape(shape), dtype=dtype, name="data2")
|
||||
read_value = ops.read_value(param1, variable_id, dtype, shape)
|
||||
return Model(ops.add(read_value, param2), [param1, param2])
|
||||
|
||||
|
||||
def test_descriptor_tensor():
|
||||
|
|
@ -288,6 +299,55 @@ def test_reshape(device):
|
|||
assert compiled_model.input().partial_shape == ref_shape
|
||||
|
||||
|
||||
def test_reshape_with_ports_and_variable():
|
||||
new_shape = PartialShape([46, 1])
|
||||
var_id = "ID1"
|
||||
model = make_add_with_variable_model([1, 5], var_id)
|
||||
for model_input in model.inputs:
|
||||
assert isinstance(model_input, Output)
|
||||
model.reshape({model_input: new_shape}, {var_id: new_shape})
|
||||
assert model_input.partial_shape == new_shape
|
||||
|
||||
|
||||
def test_reshape_with_indexes_and_variable():
|
||||
var_id = "ID1"
|
||||
model = make_add_with_variable_model([1, 5], var_id)
|
||||
new_shape = PartialShape([1, 4])
|
||||
model.reshape(
|
||||
{i: new_shape for i, model_input in enumerate(model.inputs)},
|
||||
{var_id: new_shape},
|
||||
)
|
||||
for model_input in model.inputs:
|
||||
assert model_input.partial_shape == new_shape
|
||||
|
||||
|
||||
def test_reshape_with_names_and_variables():
|
||||
var_id = "ID1"
|
||||
model = make_add_with_variable_model([1, 25], var_id)
|
||||
new_shape = PartialShape([4, 1])
|
||||
model.reshape(
|
||||
{model_input.any_name: new_shape for model_input in model.inputs},
|
||||
{var_id: new_shape},
|
||||
)
|
||||
for model_input in model.inputs:
|
||||
assert model_input.partial_shape == new_shape
|
||||
|
||||
|
||||
def test_reshape_with_variable(device):
|
||||
shape = PartialShape([1, 10])
|
||||
|
||||
param = ops.parameter(shape, dtype=np.float32)
|
||||
read_value = ops.read_value(param, "MyVar", Type.f32, shape)
|
||||
model = Model(ops.relu(read_value), [param])
|
||||
|
||||
ref_shape = model.input().partial_shape
|
||||
ref_shape[0] = 3
|
||||
model.reshape(ref_shape, {"MyVar": ref_shape})
|
||||
core = Core()
|
||||
compiled_model = core.compile_model(model, device)
|
||||
assert compiled_model.input().partial_shape == ref_shape
|
||||
|
||||
|
||||
def test_reshape_with_python_types(device):
|
||||
model = generate_add_model()
|
||||
|
||||
|
|
@ -366,6 +426,85 @@ def test_reshape_with_python_types(device):
|
|||
)
|
||||
|
||||
|
||||
def test_reshape_with_python_types_for_variable(device):
|
||||
var_id = "ID1"
|
||||
model = make_add_with_variable_model([1, 2, 5], var_id)
|
||||
|
||||
def check_shape(new_shape):
|
||||
for model_input in model.inputs:
|
||||
assert model_input.partial_shape == new_shape
|
||||
|
||||
shape1 = [1, 4]
|
||||
new_shapes = {input: PartialShape(shape1) for input in model.inputs}
|
||||
model.reshape(new_shapes, {var_id: shape1})
|
||||
check_shape(PartialShape(shape1))
|
||||
|
||||
shape2 = [1, 6]
|
||||
new_shapes = {input.any_name: PartialShape(shape2) for input in model.inputs}
|
||||
model.reshape(new_shapes, {var_id: shape2})
|
||||
check_shape(PartialShape(shape2))
|
||||
|
||||
shape3 = [1, 8]
|
||||
new_shapes = {i: PartialShape(shape3) for i, _ in enumerate(model.inputs)}
|
||||
model.reshape(new_shapes, {var_id: shape3})
|
||||
check_shape(PartialShape(shape3))
|
||||
|
||||
shape4 = [1, -1]
|
||||
new_shapes = {input: PartialShape(shape4) for input in model.inputs}
|
||||
model.reshape(new_shapes, {var_id: shape4})
|
||||
check_shape(PartialShape([Dimension(1), Dimension(-1)]))
|
||||
|
||||
shape5 = [1, (1, 10)]
|
||||
new_shapes = {input: PartialShape(shape5) for input in model.inputs}
|
||||
model.reshape(new_shapes, {var_id: shape5})
|
||||
check_shape(PartialShape([Dimension(1), Dimension(1, 10)]))
|
||||
|
||||
shape6 = [Dimension(3), Dimension(3, 10)]
|
||||
new_shapes = {input: PartialShape(shape6) for input in model.inputs}
|
||||
model.reshape(new_shapes, {var_id: shape6})
|
||||
check_shape(PartialShape(shape6))
|
||||
|
||||
shape7 = "[1..10, ?]"
|
||||
new_shapes = {input: PartialShape(shape7) for input in model.inputs}
|
||||
model.reshape(new_shapes, {var_id: shape7})
|
||||
check_shape(PartialShape(shape7))
|
||||
|
||||
# reshape mixed keys
|
||||
shape8 = [(1, 20), -1]
|
||||
new_shapes = {"data1": PartialShape(shape8), 1: shape8}
|
||||
model.reshape(new_shapes, {var_id: shape8})
|
||||
check_shape(PartialShape([Dimension(1, 20), Dimension(-1)]))
|
||||
|
||||
# reshape with one input
|
||||
param = ops.parameter([1, 3, 28, 28])
|
||||
read_value = ops.read_value(param, var_id, Type.f32, param.get_partial_shape())
|
||||
model = Model(ops.relu(read_value), [param])
|
||||
|
||||
shape9 = [-1, 3, (28, 56), (28, 56)]
|
||||
model.reshape(shape9, {var_id: shape9})
|
||||
check_shape(PartialShape([Dimension(-1), Dimension(3), Dimension(28, 56), Dimension(28, 56)]))
|
||||
|
||||
shape10 = "[?,3,..224,..224]"
|
||||
model.reshape(shape10, {var_id: shape10})
|
||||
check_shape(PartialShape([Dimension(-1), Dimension(3), Dimension(-1, 224), Dimension(-1, 224)]))
|
||||
|
||||
# check exceptions
|
||||
shape10 = [1, 1, 1, 1]
|
||||
with pytest.raises(TypeError) as e:
|
||||
model.reshape({0: shape10}, {0: shape10})
|
||||
assert (
|
||||
"Incorrect key type <class 'int'> to reshape a model, expected values as str." in str(e.value)
|
||||
)
|
||||
|
||||
with pytest.raises(TypeError) as e:
|
||||
model.reshape({0: shape10}, {var_id: range(1, 9)})
|
||||
assert (
|
||||
"Incorrect value type <class 'range'> to reshape a model, "
|
||||
"expected values as openvino.runtime.PartialShape, str, list or tuple."
|
||||
in str(e.value)
|
||||
)
|
||||
|
||||
|
||||
# request - https://docs.pytest.org/en/7.1.x/reference/reference.html#request
|
||||
def test_serialize_rt_info(request, tmp_path):
|
||||
version = "TestVersion"
|
||||
|
|
|
|||
|
|
@ -140,10 +140,14 @@ public:
|
|||
ov::Output<ov::Node> add_output(const std::string& op_name, size_t output_idx);
|
||||
ov::Output<ov::Node> add_output(const ov::Output<ov::Node>& port);
|
||||
|
||||
void reshape(const ov::PartialShape& partial_shape);
|
||||
void reshape(const std::map<size_t, ov::PartialShape>& partial_shapes);
|
||||
void reshape(const std::map<std::string, ov::PartialShape>& partial_shapes);
|
||||
void reshape(const std::map<ov::Output<ov::Node>, ov::PartialShape>& partial_shapes);
|
||||
void reshape(const ov::PartialShape& partial_shape,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variable_shapes = {});
|
||||
void reshape(const std::map<size_t, ov::PartialShape>& partial_shapes,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variable_shapes = {});
|
||||
void reshape(const std::map<std::string, ov::PartialShape>& partial_shapes,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variable_shapes = {});
|
||||
void reshape(const std::map<ov::Output<ov::Node>, ov::PartialShape>& partial_shapes,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variable_shapes = {});
|
||||
|
||||
/// Return the element type of output i
|
||||
const ov::element::Type& get_output_element_type(size_t i) const;
|
||||
|
|
|
|||
|
|
@ -88,6 +88,43 @@ const std::shared_ptr<ov::Node>& verify_node(const std::shared_ptr<ov::Node>& no
|
|||
return node;
|
||||
}
|
||||
|
||||
std::map<ov::Output<ov::Node>, ov::PartialShape> port_shapes_to_node_shapes(
|
||||
ov::Model* model,
|
||||
const std::map<size_t, ov::PartialShape>& partial_shapes) {
|
||||
std::map<ov::Output<ov::Node>, ov::PartialShape> node_shapes;
|
||||
for (const auto& it : partial_shapes) {
|
||||
const auto port = model->input(it.first);
|
||||
node_shapes[port] = it.second;
|
||||
}
|
||||
return node_shapes;
|
||||
}
|
||||
|
||||
std::map<ov::Output<ov::Node>, ov::PartialShape> tensor_names_shapes_to_node_shapes(
|
||||
ov::Model* model,
|
||||
const std::map<std::string, ov::PartialShape>& partial_shapes) {
|
||||
std::map<ov::Output<ov::Node>, ov::PartialShape> const_pshape;
|
||||
std::unordered_map<ov::Node*, std::string> port_tensor_map;
|
||||
for (const auto& it : partial_shapes) {
|
||||
const auto port = model->input(it.first);
|
||||
if (port_tensor_map.find(port.get_node()) != port_tensor_map.end()) {
|
||||
OPENVINO_ASSERT(it.second == const_pshape.at(port),
|
||||
"Tensor with names {'",
|
||||
it.first,
|
||||
"', '",
|
||||
port_tensor_map[port.get_node()],
|
||||
"'} has "
|
||||
"conflicting shapes ",
|
||||
it.second,
|
||||
" and ",
|
||||
const_pshape.at(port),
|
||||
", but they define the same tensor");
|
||||
}
|
||||
port_tensor_map[port.get_node()] = it.first;
|
||||
const_pshape[port] = it.second;
|
||||
}
|
||||
return const_pshape;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ov::Model::Model(const ResultVector& results, const ov::ParameterVector& parameters, const std::string& name)
|
||||
|
|
@ -740,50 +777,25 @@ ov::Output<ov::Node> ov::Model::input(const std::string& tensor_name) {
|
|||
OPENVINO_THROW("Input for tensor name '", tensor_name, "' is not found.");
|
||||
}
|
||||
|
||||
void ov::Model::reshape(const ov::PartialShape& partial_shape) {
|
||||
OPENVINO_ASSERT(m_parameters.size() == 1,
|
||||
"reshape(const ov::PartialShape&) must be called on a Model with exactly one parameter.");
|
||||
std::map<size_t, ov::PartialShape> shapes;
|
||||
shapes[0] = partial_shape;
|
||||
reshape(shapes);
|
||||
void ov::Model::reshape(const ov::PartialShape& partial_shape,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variable_shapes) {
|
||||
OPENVINO_ASSERT(m_parameters.size() == 1, "must be called on a Model with exactly one parameter.");
|
||||
std::map<size_t, ov::PartialShape> shapes{{0, partial_shape}};
|
||||
reshape(shapes, variable_shapes);
|
||||
}
|
||||
|
||||
void ov::Model::reshape(const std::map<size_t, ov::PartialShape>& partial_shapes) {
|
||||
std::map<ov::Output<ov::Node>, ov::PartialShape> const_pshape;
|
||||
std::unordered_map<ov::Node*, std::string> port_tensor_map;
|
||||
for (const auto& it : partial_shapes) {
|
||||
const auto port = input(it.first);
|
||||
port_tensor_map[port.get_node()] = std::to_string(it.first);
|
||||
const_pshape[port] = it.second;
|
||||
}
|
||||
reshape(const_pshape);
|
||||
void ov::Model::reshape(const std::map<size_t, ov::PartialShape>& partial_shapes,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variable_shapes) {
|
||||
reshape(port_shapes_to_node_shapes(this, partial_shapes), variable_shapes);
|
||||
}
|
||||
|
||||
void ov::Model::reshape(const std::map<std::string, ov::PartialShape>& partial_shapes) {
|
||||
std::map<ov::Output<ov::Node>, ov::PartialShape> const_pshape;
|
||||
std::unordered_map<ov::Node*, std::string> port_tensor_map;
|
||||
for (const auto& it : partial_shapes) {
|
||||
const auto port = input(it.first);
|
||||
if (port_tensor_map.find(port.get_node()) != port_tensor_map.end()) {
|
||||
OPENVINO_ASSERT(it.second == const_pshape.at(port),
|
||||
"Tensor with names {'",
|
||||
it.first,
|
||||
"', '",
|
||||
port_tensor_map[port.get_node()],
|
||||
"'} has "
|
||||
"conflicting shapes ",
|
||||
it.second,
|
||||
" and ",
|
||||
const_pshape.at(port),
|
||||
", but they define the same tensor");
|
||||
}
|
||||
port_tensor_map[port.get_node()] = it.first;
|
||||
const_pshape[port] = it.second;
|
||||
}
|
||||
reshape(const_pshape);
|
||||
void ov::Model::reshape(const std::map<std::string, ov::PartialShape>& partial_shapes,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variable_shapes) {
|
||||
reshape(tensor_names_shapes_to_node_shapes(this, partial_shapes), variable_shapes);
|
||||
}
|
||||
|
||||
void ov::Model::reshape(const std::map<ov::Output<ov::Node>, ov::PartialShape>& partial_shapes) {
|
||||
void ov::Model::reshape(const std::map<ov::Output<ov::Node>, ov::PartialShape>& partial_shapes,
|
||||
const std::unordered_map<std::string, ov::PartialShape>& variables_shapes) {
|
||||
if (partial_shapes.empty())
|
||||
return;
|
||||
|
||||
|
|
@ -818,17 +830,42 @@ void ov::Model::reshape(const std::map<ov::Output<ov::Node>, ov::PartialShape>&
|
|||
if (!need_reshape)
|
||||
return;
|
||||
|
||||
std::unordered_map<op::util::Variable*, PartialShape> new_vars_shapes;
|
||||
std::unordered_map<op::util::Variable*, PartialShape> original_vars_shapes;
|
||||
for (const auto& variable : get_variables()) {
|
||||
const auto& var_info = variable->get_info();
|
||||
|
||||
for (const auto& var_id_new_shape : variables_shapes) {
|
||||
const auto& variable_id = var_id_new_shape.first;
|
||||
const auto& new_shape = var_id_new_shape.second;
|
||||
if (variable_id == var_info.variable_id && new_shape != var_info.data_shape) {
|
||||
original_vars_shapes[variable.get()] = var_info.data_shape;
|
||||
new_vars_shapes[variable.get()] = new_shape;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save original parameters shape
|
||||
std::unordered_map<ov::op::v0::Parameter*, ov::PartialShape> original_input_shapes;
|
||||
for (const auto& param : params) {
|
||||
original_input_shapes[param.get()] = param->get_output_partial_shape(0);
|
||||
}
|
||||
|
||||
auto reshape_only = [&](const std::unordered_map<ov::op::v0::Parameter*, ov::PartialShape>& pshapes) {
|
||||
std::unordered_map<op::util::Variable*, PartialShape> original_var_shapes;
|
||||
for (const auto& v : get_variables()) {
|
||||
original_var_shapes[v.get()] = v->get_info().data_shape;
|
||||
}
|
||||
|
||||
auto reshape_only = [this](const std::unordered_map<op::v0::Parameter*, PartialShape>& pshapes,
|
||||
const std::unordered_map<op::util::Variable*, PartialShape>& vars_shapes) {
|
||||
for (const auto& pshape : pshapes) {
|
||||
pshape.first->set_partial_shape(pshape.second);
|
||||
}
|
||||
|
||||
for (const auto& shape : vars_shapes) {
|
||||
shape.first->update_data_shape(shape.second);
|
||||
}
|
||||
|
||||
validate_nodes_and_infer_types();
|
||||
};
|
||||
|
||||
|
|
@ -837,10 +874,10 @@ void ov::Model::reshape(const std::map<ov::Output<ov::Node>, ov::PartialShape>&
|
|||
ssr_manager.register_pass<ov::pass::SmartReshape>();
|
||||
ssr_manager.run_passes(shared_from_this());
|
||||
|
||||
reshape_only(new_param_shapes);
|
||||
reshape_only(new_param_shapes, new_vars_shapes);
|
||||
} catch (...) {
|
||||
// restore shapes to original ones
|
||||
reshape_only(original_input_shapes);
|
||||
reshape_only(original_input_shapes, original_vars_shapes);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,18 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
#include <shared_node_info.hpp>
|
||||
|
||||
#include "common_test_utils/graph_comparator.hpp"
|
||||
#include "common_test_utils/test_common.hpp"
|
||||
#include "openvino/core/except.hpp"
|
||||
#include "openvino/core/partial_shape.hpp"
|
||||
#include "openvino/opsets/opset8.hpp"
|
||||
#include "shared_node_info.hpp"
|
||||
|
||||
using ov::op::util::Variable;
|
||||
using ov::op::util::VariableInfo;
|
||||
using ov::op::v0::Parameter;
|
||||
using ov::op::v0::Result;
|
||||
|
||||
TEST(model, get_input_by_tensor_name) {
|
||||
auto arg0 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1});
|
||||
|
|
@ -932,7 +937,7 @@ TEST(model_reshape, ReshapeBatchReLUWithOneInput) {
|
|||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({2, 3, 22, 22}));
|
||||
}
|
||||
|
||||
TEST(model_reshape, IncoreectReshapeBatchWithMultipleInputs) {
|
||||
TEST(model_reshape, IncorrectReshapeBatchWithMultipleInputs) {
|
||||
auto arg0 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1, 3, 3, 3});
|
||||
arg0->set_friendly_name("data");
|
||||
arg0->get_output_tensor(0).set_names({"input1"});
|
||||
|
|
@ -957,6 +962,161 @@ TEST(model_reshape, IncoreectReshapeBatchWithMultipleInputs) {
|
|||
EXPECT_THROW(f->reshape(shape), ov::Exception);
|
||||
}
|
||||
|
||||
TEST(model_reshape, ReshapeWithStaticVariableSingleInput) {
|
||||
auto arg0 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 3, 4, 5});
|
||||
|
||||
auto variable = std::make_shared<Variable>(VariableInfo{arg0->get_output_partial_shape(0), ov::element::f32, "ID"});
|
||||
auto read_value = std::make_shared<ov::op::v6::ReadValue>(arg0, variable);
|
||||
auto assign = std::make_shared<ov::op::v6::Assign>(read_value, variable);
|
||||
|
||||
auto result1 = std::make_shared<Result>(assign);
|
||||
|
||||
auto shape_of = std::make_shared<ov::opset8::ShapeOf>(arg0);
|
||||
auto result2 = std::make_shared<Result>(shape_of);
|
||||
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1, result2}, ov::ParameterVector{arg0});
|
||||
|
||||
model->validate_nodes_and_infer_types();
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 3, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({1, 3, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
|
||||
{
|
||||
ov::PartialShape shape({1, 4, 3, 3});
|
||||
model->reshape(shape, {{"ID", shape}});
|
||||
}
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 4, 3, 3}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({1, 4, 3, 3}));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
}
|
||||
|
||||
TEST(model_reshape, ReshapeWithStaticVariablesSingleInput) {
|
||||
auto arg0 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 3, 4, 5});
|
||||
|
||||
auto variable =
|
||||
std::make_shared<Variable>(VariableInfo{arg0->get_output_partial_shape(0), ov::element::f32, "ID1"});
|
||||
auto read_value = std::make_shared<ov::op::v6::ReadValue>(arg0, variable);
|
||||
auto assign = std::make_shared<ov::op::v6::Assign>(read_value, variable);
|
||||
|
||||
auto add = std::make_shared<ov::op::v1::Add>(read_value, read_value);
|
||||
auto var_add = std::make_shared<Variable>(VariableInfo{add->get_output_partial_shape(0), ov::element::f32, "ID2"});
|
||||
auto read_value_add = std::make_shared<ov::op::v6::ReadValue>(add, var_add);
|
||||
auto assign_add = std::make_shared<ov::op::v6::Assign>(read_value_add, var_add);
|
||||
|
||||
auto result1 = std::make_shared<Result>(assign);
|
||||
|
||||
auto shape_of = std::make_shared<ov::opset8::ShapeOf>(add);
|
||||
auto result2 = std::make_shared<Result>(shape_of);
|
||||
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1, result2}, ov::ParameterVector{arg0});
|
||||
|
||||
model->validate_nodes_and_infer_types();
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 3, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({1, 3, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
|
||||
{
|
||||
ov::PartialShape shape({1, 4, 3, 3});
|
||||
model->reshape(shape, {{"ID2", shape}, {"ID1", shape}});
|
||||
}
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 4, 3, 3}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({1, 4, 3, 3}));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
}
|
||||
|
||||
TEST(model_reshape, ReshapeWithDynamicVariableSingleInput) {
|
||||
auto arg0 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 3, 4, 5});
|
||||
arg0->get_output_tensor(0).set_names({"input"});
|
||||
|
||||
auto variable = std::make_shared<Variable>(VariableInfo{ov::PartialShape::dynamic(4), ov::element::f32, "ID"});
|
||||
auto read_value = std::make_shared<ov::op::v6::ReadValue>(arg0, variable);
|
||||
auto assign = std::make_shared<ov::op::v6::Assign>(read_value, variable);
|
||||
|
||||
auto result1 = std::make_shared<Result>(assign);
|
||||
|
||||
auto shape_of = std::make_shared<ov::opset8::ShapeOf>(arg0);
|
||||
auto result2 = std::make_shared<Result>(shape_of);
|
||||
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1, result2}, ov::ParameterVector{arg0});
|
||||
|
||||
model->validate_nodes_and_infer_types();
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 3, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_output_partial_shape(0), ov::PartialShape::dynamic(4));
|
||||
EXPECT_EQ(model->get_results()[1]->get_output_partial_shape(0), ov::Shape({4}));
|
||||
|
||||
model->reshape({{"input", ov::PartialShape{1, 4, 8, 9}}});
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 4, 8, 9}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_output_partial_shape(0), ov::PartialShape::dynamic(4));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
}
|
||||
|
||||
TEST(model_reshape, ReshapeStaticWithVariableMultipleInputs) {
|
||||
auto arg0 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 3, 4, 5});
|
||||
auto arg1 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 2, 4, 5});
|
||||
auto concat = std::make_shared<ov::op::v0::Concat>(ov::NodeVector{arg0, arg1}, 1);
|
||||
|
||||
auto variable =
|
||||
std::make_shared<Variable>(VariableInfo{concat->get_output_partial_shape(0), ov::element::f32, "ID"});
|
||||
auto read_value = std::make_shared<ov::op::v6::ReadValue>(concat, variable);
|
||||
auto assign = std::make_shared<ov::op::v6::Assign>(read_value, variable);
|
||||
|
||||
auto result1 = std::make_shared<Result>(assign);
|
||||
|
||||
auto shape_of = std::make_shared<ov::opset8::ShapeOf>(concat);
|
||||
auto result2 = std::make_shared<Result>(shape_of);
|
||||
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1, result2}, ov::ParameterVector{arg0, arg1});
|
||||
|
||||
model->validate_nodes_and_infer_types();
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 3, 4, 5}));
|
||||
EXPECT_EQ(model->get_parameters()[1]->get_shape(), ov::Shape({1, 2, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({1, 5, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
|
||||
{
|
||||
ov::PartialShape shape({1, 14, 4, 5});
|
||||
model->reshape({{0, shape}, {1, shape}}, {{"ID", {1, 28, 4, 5}}});
|
||||
}
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 14, 4, 5}));
|
||||
EXPECT_EQ(model->get_parameters()[1]->get_shape(), ov::Shape({1, 14, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({1, 28, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
}
|
||||
|
||||
TEST(model_reshape, ReshapeWithStaticVariableIncorrectVariable) {
|
||||
auto arg0 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 3, 4, 5});
|
||||
auto arg1 = std::make_shared<Parameter>(ov::element::f32, ov::PartialShape{1, 2, 4, 5});
|
||||
auto concat = std::make_shared<ov::op::v0::Concat>(ov::NodeVector{arg0, arg1}, 1);
|
||||
|
||||
auto variable =
|
||||
std::make_shared<Variable>(VariableInfo{concat->get_output_partial_shape(0), ov::element::f32, "ID"});
|
||||
auto read_value = std::make_shared<ov::op::v6::ReadValue>(concat, variable);
|
||||
auto assign = std::make_shared<ov::op::v6::Assign>(read_value, variable);
|
||||
|
||||
auto result1 = std::make_shared<Result>(assign);
|
||||
|
||||
auto shape_of = std::make_shared<ov::opset8::ShapeOf>(concat);
|
||||
auto result2 = std::make_shared<Result>(shape_of);
|
||||
auto model = std::make_shared<ov::Model>(ov::ResultVector{result1, result2}, ov::ParameterVector{arg0, arg1});
|
||||
|
||||
model->validate_nodes_and_infer_types();
|
||||
|
||||
EXPECT_EQ(model->get_parameters()[0]->get_shape(), ov::Shape({1, 3, 4, 5}));
|
||||
EXPECT_EQ(model->get_parameters()[1]->get_shape(), ov::Shape({1, 2, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[0]->get_shape(), ov::Shape({1, 5, 4, 5}));
|
||||
EXPECT_EQ(model->get_results()[1]->get_shape(), ov::Shape({4}));
|
||||
|
||||
ov::PartialShape shape({1, 14, 4, 5});
|
||||
ov::PartialShape wrong_var_shape({1, 20, 4, 5});
|
||||
EXPECT_THROW(model->reshape({{0, shape}, {1, shape}}), ov::AssertFailure);
|
||||
EXPECT_THROW(model->reshape({{0, shape}, {1, shape}}, {{"WRONG_ID", {1, 28, 4, 5}}}), ov::AssertFailure);
|
||||
EXPECT_THROW(model->reshape({{0, shape}, {1, shape}}, {{"ID", wrong_var_shape}}), ov::AssertFailure);
|
||||
}
|
||||
|
||||
TEST(model, add_output_tensor_name) {
|
||||
auto arg0 = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape{1});
|
||||
arg0->set_friendly_name("data");
|
||||
|
|
|
|||
Loading…
Reference in New Issue