Added compatibility check of layout with partial shape (#10144)
* Added compatibility check of layout with partial shape
E.g. layout "NC" in not compatible with PartialShape{1,3,224,224}
Check is added:
- For parameter set_layout
- For parameter set_partial_shape
- For result set_layout
- Checked also compatibility for all results after 'validate_and_infer_types'
* Fix incorrect tests
* Fix of more incorrect tests
* Removed couple of obsoleted error-handling tests - these are catched now on earlier stages
Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>
This commit is contained in:
1 parent
ae4c727b31
commit
9f9df184c4
11 files changed
+157
-42
No files matched your search
@@ -30,8 +30,8 @@ def test_ngraph_function_api():
|
||||
|
||||
assert parameter_a.element_type == Type.f32
|
||||
assert parameter_a.partial_shape == PartialShape([2, 2])
|
||||
parameter_a.layout = ov.Layout("NCWH")
|
||||
assert parameter_a.layout == ov.Layout("NCWH")
|
||||
parameter_a.layout = ov.Layout("NC")
|
||||
assert parameter_a.layout == ov.Layout("NC")
|
||||
function = Model(model, [parameter_a, parameter_b, parameter_c], "TestFunction")
|
||||
|
||||
function.get_parameters()[1].set_partial_shape(PartialShape([3, 4, 5]))
|
||||
|
||||
@@ -39,7 +39,7 @@ def test_ngraph_preprocess_mean_vector():
|
||||
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
|
||||
model = parameter_a
|
||||
function = Model(model, [parameter_a], "TestFunction")
|
||||
layout = ov.Layout("NCHW")
|
||||
layout = ov.Layout("NC")
|
||||
|
||||
p = PrePostProcessor(function)
|
||||
p.input().tensor().set_layout(layout)
|
||||
@@ -60,7 +60,7 @@ def test_ngraph_preprocess_scale_vector():
|
||||
parameter_a = ops.parameter(shape, dtype=np.float32, name="A")
|
||||
model = parameter_a
|
||||
function = Model(model, [parameter_a], "TestFunction")
|
||||
layout = ov.Layout("NCHW")
|
||||
layout = ov.Layout("NC")
|
||||
|
||||
p = PrePostProcessor(function)
|
||||
inp = p.input()
|
||||
@@ -142,13 +142,13 @@ def test_ngraph_preprocess_input_output_by_name():
|
||||
|
||||
|
||||
def test_ngraph_preprocess_output_postprocess():
|
||||
shape = [2, 2]
|
||||
shape = [2, 3]
|
||||
parameter_a = ops.parameter(shape, dtype=np.int32, name="A")
|
||||
model = parameter_a
|
||||
function = Model(model, [parameter_a], "TestFunction")
|
||||
layout1 = ov.Layout("NCHW")
|
||||
layout2 = ov.Layout("NHWC")
|
||||
layout3 = [0, 1]
|
||||
layout1 = ov.Layout("NC")
|
||||
layout2 = ov.Layout("CN")
|
||||
layout3 = [1, 0]
|
||||
|
||||
@custom_preprocess_function
|
||||
def custom_postprocess(output: Output):
|
||||
@@ -157,15 +157,16 @@ def test_ngraph_preprocess_output_postprocess():
|
||||
p = PrePostProcessor(function)
|
||||
inp = p.input()
|
||||
inp.tensor().set_layout(layout1)
|
||||
inp.preprocess().convert_element_type(Type.f32).mean([1., 2.])
|
||||
inp.preprocess().convert_element_type(Type.f32).mean([1., 2., 3.])
|
||||
out = p.output()
|
||||
out.model().set_layout(layout1)
|
||||
out.postprocess().convert_element_type(Type.f32) \
|
||||
.convert_layout(layout2) \
|
||||
.convert_layout(layout3).custom(custom_postprocess)
|
||||
function = p.build()
|
||||
|
||||
input_data = np.array([[-1, -2], [-3, -4]]).astype(np.int32)
|
||||
expected_output = np.array([[2, 4], [4, 6]]).astype(np.float32)
|
||||
input_data = np.array([[-1, -2, -3], [-4, -5, -6]]).astype(np.int32)
|
||||
expected_output = np.array([[2, 4, 6], [5, 7, 9]]).astype(np.float32)
|
||||
|
||||
runtime = get_runtime()
|
||||
computation = runtime.computation(function)
|
||||
|
||||
@@ -42,9 +42,9 @@ public:
|
||||
PartialShape& get_partial_shape() {
|
||||
return m_partial_shape;
|
||||
}
|
||||
void set_partial_shape(const PartialShape& partial_shape) {
|
||||
m_partial_shape = partial_shape;
|
||||
}
|
||||
|
||||
void set_partial_shape(const PartialShape& partial_shape);
|
||||
|
||||
const element::Type& get_element_type() const {
|
||||
return m_element_type;
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@ public:
|
||||
static std::tuple<PartialShape, Layout, size_t> find_unsqueeze(const Layout& src_layout,
|
||||
const PartialShape& src_shape,
|
||||
const Layout& dst_layout);
|
||||
static bool is_compatible(const Layout& layout, const PartialShape& shape);
|
||||
};
|
||||
|
||||
Layout LayoutUtils::apply_permutation(const Layout& src_layout, const std::vector<uint64_t>& dims) {
|
||||
@@ -517,6 +518,12 @@ std::tuple<PartialShape, Layout, size_t> LayoutUtils::find_unsqueeze(const Layou
|
||||
}
|
||||
}
|
||||
|
||||
bool LayoutUtils::is_compatible(const Layout& layout, const PartialShape& shape) {
|
||||
auto layout_min_rank = layout.m_left_size + layout.m_right_size;
|
||||
int64_t layout_max_rank = layout.m_dynamic ? -1 : layout_min_rank;
|
||||
return shape.rank().compatible(Dimension(layout_min_rank, layout_max_rank));
|
||||
}
|
||||
|
||||
namespace layout {
|
||||
namespace utils {
|
||||
Layout apply_permutation(const Layout& src_layout, const std::vector<uint64_t>& dims) {
|
||||
@@ -541,6 +548,10 @@ std::tuple<PartialShape, Layout, size_t> find_unsqueeze(const Layout& src_layout
|
||||
return LayoutUtils::find_unsqueeze(src_layout, src_shape, dst_layout);
|
||||
}
|
||||
|
||||
bool is_compatible(const Layout& layout, const PartialShape& shape) {
|
||||
return LayoutUtils::is_compatible(layout, shape);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
// Helper functions
|
||||
@@ -603,6 +614,13 @@ void set_layout(ov::Output<ov::Node> output, const ov::Layout& layout) {
|
||||
if (layout.empty()) {
|
||||
output.get_rt_info().erase(ov::LayoutAttribute::get_type_info_static());
|
||||
} else {
|
||||
OPENVINO_ASSERT(ov::layout::utils::is_compatible(layout, output.get_partial_shape()),
|
||||
"Can't set layout for Parameter/Result ",
|
||||
output,
|
||||
": layout ",
|
||||
layout.to_string(),
|
||||
" is not compatible with shape ",
|
||||
output.get_partial_shape());
|
||||
output.get_rt_info()[ov::LayoutAttribute::get_type_info_static()] = ov::LayoutAttribute(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "openvino/core/layout.hpp"
|
||||
#include "openvino/core/partial_shape.hpp"
|
||||
|
||||
namespace ov {
|
||||
@@ -33,6 +34,8 @@ std::vector<int64_t> find_permutation(const Layout& src_layout,
|
||||
const Layout& dst_layout);
|
||||
Layout apply_permutation(const Layout& src_layout, const std::vector<uint64_t>& dims);
|
||||
|
||||
bool is_compatible(const Layout& layout, const PartialShape& shape);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace layout
|
||||
} // namespace ov
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "itt.hpp"
|
||||
#include "layout_utils.hpp"
|
||||
#include "ngraph/evaluator.hpp"
|
||||
#include "ngraph/function.hpp"
|
||||
#include "ngraph/graph_util.hpp"
|
||||
@@ -253,6 +254,15 @@ void ov::Model::validate_nodes_and_infer_types() const {
|
||||
if (!only_pairs)
|
||||
throw ov::Exception("Model is incorrect. Assign and ReadValue operations must be in pairs on the "
|
||||
"network.");
|
||||
for (const auto& output : outputs()) {
|
||||
OPENVINO_ASSERT(ov::layout::utils::is_compatible(ov::layout::get_layout(output), output.get_partial_shape()),
|
||||
"Result '",
|
||||
output,
|
||||
"' with shape ",
|
||||
output.get_partial_shape(),
|
||||
" is incompatible with layout ",
|
||||
ov::layout::get_layout(output).to_string());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<shared_ptr<ov::Node>> ov::Model::get_ordered_ops() const {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "itt.hpp"
|
||||
#include "layout_utils.hpp"
|
||||
#include "ngraph/attribute_visitor.hpp"
|
||||
|
||||
using namespace std;
|
||||
@@ -56,6 +57,18 @@ void op::Parameter::set_layout(const ov::Layout& layout) {
|
||||
ov::layout::set_layout(output(0), layout);
|
||||
}
|
||||
|
||||
void op::Parameter::set_partial_shape(const PartialShape& partial_shape) {
|
||||
OPENVINO_ASSERT(ov::layout::utils::is_compatible(get_layout(), partial_shape),
|
||||
"Can't set partial shape ",
|
||||
partial_shape,
|
||||
" for Parameter ",
|
||||
*this,
|
||||
" with layout ",
|
||||
get_layout().to_string(),
|
||||
". Layout is not compatible with shape");
|
||||
m_partial_shape = partial_shape;
|
||||
}
|
||||
|
||||
BWDCMP_RTTI_DEFINITION(ov::AttributeAdapter<ParameterVector>);
|
||||
|
||||
ov::AttributeAdapter<ParameterVector>::AttributeAdapter(ParameterVector& ref) : m_ref(ref) {}
|
||||
|
||||
@@ -1438,7 +1438,7 @@ TEST(model, get_batch_size) {
|
||||
TEST(model, get_batch_size_with_conflict) {
|
||||
auto f = bs_utils::create_n_inputs(ov::element::f32,
|
||||
{ov::PartialShape::dynamic(), {5, 6}, {1, 3, 224, 224}, {3, 1}},
|
||||
{"NCHW", "D...", "NCHW", "N???"});
|
||||
{"NCHW", "D...", "NCHW", "N?"});
|
||||
|
||||
// TODO: gtest v.10 limitation. Replace with EXPECT_THAT for gtest >= v1.11
|
||||
try {
|
||||
@@ -1447,7 +1447,7 @@ TEST(model, get_batch_size_with_conflict) {
|
||||
} catch (const ov::Exception& err) {
|
||||
// Verify error message contains conflicting layouts
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("NCHW").to_string()) != std::string::npos) << err.what();
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("N???").to_string()) != std::string::npos) << err.what();
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("N?").to_string()) != std::string::npos) << err.what();
|
||||
// Verify error message doesn't contain non-conflicting layouts
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("D...").to_string()) == std::string::npos) << err.what();
|
||||
EXPECT_TRUE(std::string(err.what()).find("tensor_input_0") == std::string::npos) << err.what();
|
||||
@@ -1525,7 +1525,7 @@ TEST(model, set_batch_size_dynamic_layout) {
|
||||
TEST(model, set_batch_size_with_conflict) {
|
||||
auto f = bs_utils::create_n_inputs(ov::element::f32,
|
||||
{ov::PartialShape::dynamic(), {5, 6}, {1, 3, 224, 224}, {3, 1}},
|
||||
{"NCHW", "D...", "NCHW", "N???"});
|
||||
{"NCHW", "D...", "NCHW", "N?"});
|
||||
|
||||
// TODO: gtest v.10 limitation. Replace with EXPECT_THAT for gtest >= v1.11
|
||||
try {
|
||||
@@ -1534,7 +1534,7 @@ TEST(model, set_batch_size_with_conflict) {
|
||||
} catch (const ov::Exception& err) {
|
||||
// Verify error message contains conflicting layouts
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("NCHW").to_string()) != std::string::npos) << err.what();
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("N???").to_string()) != std::string::npos) << err.what();
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("N?").to_string()) != std::string::npos) << err.what();
|
||||
// Verify error message doesn't contain non-conflicting layouts
|
||||
EXPECT_TRUE(std::string(err.what()).find(ov::Layout("D...").to_string()) == std::string::npos) << err.what();
|
||||
EXPECT_TRUE(std::string(err.what()).find("tensor_input_0") == std::string::npos) << err.what();
|
||||
@@ -1577,3 +1577,88 @@ TEST(model, set_batch_size_validation_throw) {
|
||||
FAIL() << "Expected ov::Exception";
|
||||
}
|
||||
}
|
||||
|
||||
TEST(model, incompatible_layout) {
|
||||
auto f = bs_utils::create_n_inputs(ov::element::f32, {{1, 3, 224, 224}}, {"NCHW"});
|
||||
using callback = std::function<void()>;
|
||||
auto verify_ex = [&](const callback& cb, const std::string& msg) {
|
||||
try {
|
||||
cb();
|
||||
FAIL() << "set_layout shall throw";
|
||||
} catch (const ov::Exception& err) {
|
||||
// Verify error message contains conflicting layouts
|
||||
EXPECT_TRUE(std::string(err.what()).find(msg) != std::string::npos) << err.what();
|
||||
} catch (...) {
|
||||
FAIL() << "Expected ov::Exception";
|
||||
}
|
||||
};
|
||||
auto verify_ex_set_layout = [&](const ov::Layout& layout) {
|
||||
auto msg = layout.to_string();
|
||||
verify_ex(
|
||||
[&]() {
|
||||
ov::layout::set_layout(f->input(), layout);
|
||||
},
|
||||
msg);
|
||||
};
|
||||
verify_ex_set_layout("HWC");
|
||||
verify_ex_set_layout("NDCHW");
|
||||
verify_ex_set_layout("ND...CHW");
|
||||
EXPECT_NO_THROW(ov::layout::set_layout(f->input(), "H...WC"));
|
||||
EXPECT_NO_THROW(ov::layout::set_layout(f->input(), "...NCHW"));
|
||||
EXPECT_NO_THROW(f->get_parameters()[0]->set_layout("NCHW..."));
|
||||
EXPECT_NO_THROW(f->get_parameters()[0]->set_layout("NCHW"));
|
||||
|
||||
auto verify_ex_set_layout_param = [&](const ov::Layout& layout) {
|
||||
auto msg = layout.to_string();
|
||||
verify_ex(
|
||||
[&]() {
|
||||
f->get_parameters()[0]->set_layout(layout);
|
||||
},
|
||||
msg);
|
||||
};
|
||||
verify_ex_set_layout_param("HWC");
|
||||
verify_ex_set_layout_param("NDCHW");
|
||||
verify_ex_set_layout_param("ND...CHW");
|
||||
|
||||
auto verify_ex_set_partial_shape = [&](const ov::PartialShape& shape) {
|
||||
std::stringstream msgStr;
|
||||
msgStr << shape;
|
||||
auto msg = msgStr.str();
|
||||
verify_ex(
|
||||
[&]() {
|
||||
f->get_parameters()[0]->set_partial_shape(shape);
|
||||
},
|
||||
msg);
|
||||
};
|
||||
verify_ex_set_partial_shape({1, 2, 3, 4, 5});
|
||||
verify_ex_set_partial_shape({1, 2, 3});
|
||||
EXPECT_NO_THROW(f->get_parameters()[0]->set_partial_shape(ov::PartialShape::dynamic()));
|
||||
EXPECT_NO_THROW(f->get_parameters()[0]->set_partial_shape(ov::PartialShape{1, 3, 224, 224}));
|
||||
|
||||
auto verify_ex_set_layout_result = [&](const ov::Layout& layout) {
|
||||
auto msg = layout.to_string();
|
||||
verify_ex(
|
||||
[&]() {
|
||||
ov::layout::set_layout(f->output(), layout);
|
||||
},
|
||||
msg);
|
||||
};
|
||||
verify_ex_set_layout_result("HWC");
|
||||
verify_ex_set_layout_result("NDCHW");
|
||||
verify_ex_set_layout_result("ND...CHW");
|
||||
|
||||
auto verify_ex_set_layout_result_validate = [&](const ov::PartialShape& param_shape, const ov::Layout& layout) {
|
||||
auto msg = layout.to_string();
|
||||
f = bs_utils::create_n_inputs(ov::element::f32, {ov::PartialShape::dynamic()}, {"..."});
|
||||
verify_ex(
|
||||
[&]() {
|
||||
f->get_parameters()[0]->set_partial_shape(param_shape);
|
||||
ov::layout::set_layout(f->output(), layout);
|
||||
f->validate_nodes_and_infer_types();
|
||||
},
|
||||
msg);
|
||||
};
|
||||
verify_ex_set_layout_result_validate({1, 2, 3, 4}, "HWC");
|
||||
verify_ex_set_layout_result_validate({1, 2, 3, 4}, "NDHWC");
|
||||
verify_ex_set_layout_result_validate({1, 2, 3, 4}, "ND...HWC");
|
||||
}
|
||||
@@ -317,19 +317,6 @@ TEST_P(OVInferRequestBatchedTests, SetTensors_Batch1) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(OVInferRequestBatchedTests, SetInputTensors_Batch_Incorrect) {
|
||||
size_t batch = 3;
|
||||
auto one_shape = Shape{1, 3, 3, 3};
|
||||
auto batch_shape = Shape{batch, 3, 3, 3};
|
||||
auto model = OVInferRequestBatchedTests::create_n_inputs(1, element::f32, batch_shape, "DCHWN");
|
||||
const std::string tensor_name = "tensor_input0";
|
||||
auto execNet = ie->compile_model(model, targetDevice);
|
||||
ov::InferRequest req;
|
||||
req = execNet.create_infer_request();
|
||||
std::vector<ov::Tensor> tensors(batch, ov::Tensor(element::f32, one_shape));
|
||||
ASSERT_THROW(req.set_tensors(tensor_name, tensors), ov::Exception);
|
||||
}
|
||||
|
||||
TEST_P(OVInferRequestBatchedTests, SetInputTensors_Get_Tensor_Not_Allowed) {
|
||||
size_t batch = 3;
|
||||
auto one_shape = Shape{1, 3, 3, 3};
|
||||
|
||||
@@ -309,15 +309,15 @@ TEST(NetworkContext_CNNNetwork, HashWithFutureRt_int64) {
|
||||
}
|
||||
|
||||
TEST(NetworkContext_CNNNetwork, HashWithLayout) {
|
||||
auto net1 = createNetworkWithLayout("NCHW");
|
||||
auto net2 = createNetworkWithLayout("nchw");
|
||||
auto net3 = createNetworkWithLayout("?CHW");
|
||||
auto net3_1 = createNetworkWithLayout("?CH?");
|
||||
auto net1 = createNetworkWithLayout("NCH");
|
||||
auto net2 = createNetworkWithLayout("nch");
|
||||
auto net3 = createNetworkWithLayout("?CH");
|
||||
auto net3_1 = createNetworkWithLayout("?C?");
|
||||
auto net4 = createNetworkWithLayout("");
|
||||
auto fun5 = create_simple_function();
|
||||
fun5->get_parameters()[0]->set_layout("NCHW");
|
||||
fun5->get_parameters()[0]->set_layout("NCH");
|
||||
fun5->get_parameters()[0]->set_layout("");
|
||||
fun5->get_results()[0]->set_layout("NHCW");
|
||||
fun5->get_results()[0]->set_layout("NHC");
|
||||
fun5->get_results()[0]->set_layout(ov::Layout());
|
||||
auto net5 = CNNNetwork(fun5);
|
||||
|
||||
|
||||
@@ -551,12 +551,10 @@ class TestPreprocessingMOC(UnitTestWithMockedTelemetry):
|
||||
with self.assertRaisesRegex(Error, '.*2.*inputs.*input1.*input2.*'):
|
||||
process_function(ov_function=function, argv=argv)
|
||||
|
||||
def test_reverse_channels_bad_layout(self):
|
||||
argv = Namespace(reverse_input_channels=True, mean_scale_values=None, scale=None)
|
||||
def test_incompatible_layout(self):
|
||||
function = create_function2(shape1=[1, 224, 224, 3], shape2=[1, 4, 224, 224])
|
||||
function.get_parameters()[0].layout = Layout("NDHWC")
|
||||
with self.assertRaisesRegex(Error, '.*input1.*'):
|
||||
process_function(ov_function=function, argv=argv)
|
||||
with self.assertRaisesRegex(Exception, '.*input1.*'):
|
||||
function.get_parameters()[0].layout = Layout("NDHWC")
|
||||
|
||||
def test_guess_layout_reverse_channels_dont_apply_to_4(self):
|
||||
argv = Namespace(reverse_input_channels=True, mean_scale_values=None, scale=None)
|
||||
|
||||
Reference in new issue
Block a user