From e132c650536aa116c03cd67b8ccfdf5b347ae657 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 24 Apr 2025 13:42:29 +0200 Subject: [PATCH 1/5] [circle-resizer] Add ModelEditor This commit adds class responsible for model editing - in the current moment input resizing. ONE-DCO-1.0-Signed-off-by: Mateusz Bencer m.bencer@partner.samsung.com --- compiler/circle-resizer/include/ModelEditor.h | 59 ++++++ compiler/circle-resizer/src/CMakeLists.txt | 3 + compiler/circle-resizer/src/ModelEditor.cpp | 91 +++++++++ compiler/circle-resizer/tests/CMakeLists.txt | 1 + .../circle-resizer/tests/ModelEditor.test.cpp | 176 ++++++++++++++++++ 5 files changed, 330 insertions(+) create mode 100644 compiler/circle-resizer/include/ModelEditor.h create mode 100644 compiler/circle-resizer/src/ModelEditor.cpp create mode 100644 compiler/circle-resizer/tests/ModelEditor.test.cpp diff --git a/compiler/circle-resizer/include/ModelEditor.h b/compiler/circle-resizer/include/ModelEditor.h new file mode 100644 index 00000000000..684f75d7132 --- /dev/null +++ b/compiler/circle-resizer/include/ModelEditor.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CIRCLE_RESIZER_MODEL_EDITOR_H__ +#define __CIRCLE_RESIZER_MODEL_EDITOR_H__ + +#include "Shape.h" +#include "CircleModel.h" + +#include +#include +#include + +namespace circle_resizer +{ + +/** + * The class to modify circle models. + */ +class ModelEditor +{ +public: + /** + * @brief Initialize the editor with CircleModel object. + */ + explicit ModelEditor(std::shared_ptr circle_model); + +public: + /** + * @brief Resize the model. In means changing shape of the inputs + * and propagating changes through the graph. + * + * Exceptions: + * - std::runtime_error if the new_inputs_shapes are invalid. It can happens for scenarios like: + * - new shapes for NOT all inputs are provided + * - an exception was thrown during shape inference pass + */ + ModelEditor &resize_inputs(const std::vector &new_inputs_shapes); + +private: + std::shared_ptr _circle_model; +}; + +} // namespace circle_resizer + +#endif // __CIRCLE_RESIZER_MODEL_EDITOR_H__ diff --git a/compiler/circle-resizer/src/CMakeLists.txt b/compiler/circle-resizer/src/CMakeLists.txt index 85e0380b40e..d099a4991a2 100644 --- a/compiler/circle-resizer/src/CMakeLists.txt +++ b/compiler/circle-resizer/src/CMakeLists.txt @@ -2,6 +2,7 @@ list(APPEND CIRCLE_RESIZER_SOURCES Dim.cpp) list(APPEND CIRCLE_RESIZER_SOURCES Shape.cpp) list(APPEND CIRCLE_RESIZER_SOURCES ShapeParser.cpp) list(APPEND CIRCLE_RESIZER_SOURCES CircleModel.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES ModelEditor.cpp) add_library(circle_resizer_core SHARED "${CIRCLE_RESIZER_SOURCES}") @@ -10,6 +11,8 @@ target_include_directories(circle_resizer_core PUBLIC ../include) target_link_libraries(circle_resizer_core PRIVATE luci_export) target_link_libraries(circle_resizer_core PRIVATE luci_import) target_link_libraries(circle_resizer_core PRIVATE luci_lang) +target_link_libraries(circle_resizer_core PRIVATE luci_pass) +target_link_libraries(circle_resizer_core PRIVATE logo) target_link_libraries(circle_resizer_core PRIVATE mio_circle08) install(TARGETS circle_resizer_core DESTINATION lib) diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp new file mode 100644 index 00000000000..f632e85213e --- /dev/null +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ModelEditor.h" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace circle_resizer; + +namespace +{ + +void change_single_input_shape(luci::CircleInput *circle_input, const Shape &new_shape) +{ + circle_input->rank(new_shape.rank()); + for (uint32_t i = 0; i < new_shape.rank(); ++i) + { + if (new_shape[i].is_dynamic()) + { + circle_input->dim(i) = loco::Dimension(); // empty ctor means dynamic dimension + } + else + { + circle_input->dim(i) = loco::Dimension(static_cast(new_shape[i].value())); + } + } +} + +void change_inputs_shapes(loco::Graph *graph, const std::vector &new_inputs_shapes) +{ + auto graph_inputs = loco::input_nodes(graph); + if (graph_inputs.size() != new_inputs_shapes.size()) + { + throw std::runtime_error("Expected " + std::to_string(graph_inputs.size()) + + " shapes but provided only " + + std::to_string(new_inputs_shapes.size())); + } + for (size_t in_idx = 0; in_idx < new_inputs_shapes.size(); ++in_idx) + { + auto circle_input = loco::must_cast(graph_inputs[in_idx]); + change_single_input_shape(circle_input, new_inputs_shapes[in_idx]); + } +} + +} // namespace + +ModelEditor::ModelEditor(std::shared_ptr circle_model) : _circle_model{circle_model} {} + +ModelEditor &ModelEditor::resize_inputs(const std::vector &new_inputs_shapes) +{ + auto graph = _circle_model->module()->graph(); + change_inputs_shapes(graph, new_inputs_shapes); + + logo::Phase phase; + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + + logo::PhaseRunner phase_runner{graph}; + try + { + phase_runner.run(phase); + } + catch (const std::exception &e) + { + throw std::runtime_error("Exception during shape inference with message: " + + std::string{e.what()}); + } + + return *this; +} diff --git a/compiler/circle-resizer/tests/CMakeLists.txt b/compiler/circle-resizer/tests/CMakeLists.txt index eeebdfe973f..3337718cc34 100644 --- a/compiler/circle-resizer/tests/CMakeLists.txt +++ b/compiler/circle-resizer/tests/CMakeLists.txt @@ -5,6 +5,7 @@ endif(NOT ENABLE_TEST) list(APPEND CIRCLE_RESIZER_TEST_SOURCES Shape.test.cpp) list(APPEND CIRCLE_RESIZER_TEST_SOURCES ShapeParser.test.cpp) list(APPEND CIRCLE_RESIZER_TEST_SOURCES CircleModel.test.cpp) +list(APPEND CIRCLE_RESIZER_TEST_SOURCES ModelEditor.test.cpp) nnas_find_package(GTest REQUIRED) GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES}) diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp new file mode 100644 index 00000000000..303682c791f --- /dev/null +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ModelEditor.h" + +#include +#include + +#include +#include +#include +#include + +using namespace circle_resizer; +using ::testing::HasSubstr; + +class ModelEditorTest : public ::testing::Test +{ +protected: + void SetUp() override + { + char *path = std::getenv("ARTIFACTS_PATH"); + if (path == nullptr) + { + throw std::runtime_error("environmental variable ARTIFACTS_PATH required for circle-resizer " + "tests was not not provided"); + } + _test_models_dir = path; + } + +protected: + std::string _test_models_dir; +}; + +TEST_F(ModelEditorTest, single_input_single_output) +{ + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(circle_model); + const auto new_input_shapes = std::vector{Shape{4, 6}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{4, 1, 6}})); +} + +TEST_F(ModelEditorTest, single_input_two_outputs) +{ + auto circle_model = std::make_shared(_test_models_dir + "/CSE_Quantize_000.circle"); + ModelEditor editor(circle_model); + const auto new_input_shapes = std::vector{Shape{1, 6, 6, 4}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), + (std::vector{Shape{1, 6, 6, 4}, Shape{1, 6, 6, 4}})); +} + +TEST_F(ModelEditorTest, two_inputs_single_output) +{ + auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); + ModelEditor editor(circle_model); + const auto new_input_shapes = std::vector{Shape{1, 5, 5, 3}, Shape{1, 5, 5, 3}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{1, 5, 5, 3}})); +} + +TEST_F(ModelEditorTest, two_inputs_two_outputs) +{ + auto circle_model = + std::make_shared(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); + ModelEditor editor(circle_model); + const auto new_input_shapes = std::vector{Shape{1, 5, 5, 2}, Shape{1, 5, 5, 2}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), + (std::vector{Shape{1, 5, 5, 2}, Shape{1, 5, 5, 2}})); +} + +TEST_F(ModelEditorTest, resize_applied_after_save) +{ + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(circle_model); + std::stringstream out_stream; + const auto new_input_shapes = std::vector{Shape{4, 6}}; + editor.resize_inputs(new_input_shapes); + circle_model->save(out_stream); + const std::string &model_buf_str = out_stream.str(); + std::vector model_buffer(std::begin(model_buf_str), std::end(model_buf_str)); + model_buffer.insert(std::end(model_buffer), std::begin(model_buf_str), std::end(model_buf_str)); + + auto circle_model_from_saved_buffer = std::make_shared(model_buffer); + EXPECT_EQ(circle_model_from_saved_buffer->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model_from_saved_buffer->output_shapes(), (std::vector{Shape{4, 1, 6}})); +} + +TEST_F(ModelEditorTest, single_input_single_output_double_resizing) +{ + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(circle_model); + const auto new_input_shapes = std::vector{Shape{4, 6}}; + editor.resize_inputs(std::vector{Shape{6, 8}}).resize_inputs(new_input_shapes); + // check if the last applied shape is set after double resizing call + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{4, 1, 6}})); +} + +TEST_F(ModelEditorTest, change_input_rank) +{ + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(circle_model); + const auto new_input_shapes = std::vector{Shape{1, 2, 3, 4}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{1, 1, 2, 3, 4}})); +} + +TEST_F(ModelEditorTest, resize_to_dynamic) +{ + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(circle_model); + const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim::dynamic()}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), + (std::vector{Shape{Dim{4}, Dim{1}, Dim::dynamic()}})); +} + +TEST_F(ModelEditorTest, not_all_input_shapes_provided_NEG) +{ + auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); + ModelEditor editor(circle_model); + try + { + editor.resize_inputs(std::vector{Shape{1, 5, 5, 3}}); + FAIL() << "Unexpected successful resizing with invalid shapes."; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Expected 2 shapes but provided only 1")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(ModelEditorTest, exception_during_shape_inference_NEG) +{ + auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); + ModelEditor editor(circle_model); + try + { + editor.resize_inputs(std::vector{Shape{1, 2, 3}, Shape{4, 5, 6}}); + FAIL() << "Unexpected successful resizing with invalid shapes."; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Exception during shape inference with message:")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} From 08b9e36eb43810bbf78831aafda5708fc2cf8e62 Mon Sep 17 00:00:00 2001 From: mbencer Date: Fri, 25 Apr 2025 08:41:38 +0200 Subject: [PATCH 2/5] Apply suggestions from code review Co-authored-by: Hyukjin Jeong --- compiler/circle-resizer/include/ModelEditor.h | 2 +- compiler/circle-resizer/src/ModelEditor.cpp | 7 +++++-- compiler/circle-resizer/tests/ModelEditor.test.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/circle-resizer/include/ModelEditor.h b/compiler/circle-resizer/include/ModelEditor.h index 684f75d7132..910ea19233a 100644 --- a/compiler/circle-resizer/include/ModelEditor.h +++ b/compiler/circle-resizer/include/ModelEditor.h @@ -40,7 +40,7 @@ class ModelEditor public: /** - * @brief Resize the model. In means changing shape of the inputs + * @brief Resize the model. It means changing shape of the inputs * and propagating changes through the graph. * * Exceptions: diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index f632e85213e..c585b06355a 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -64,7 +64,10 @@ void change_inputs_shapes(loco::Graph *graph, const std::vector &new_inpu } // namespace -ModelEditor::ModelEditor(std::shared_ptr circle_model) : _circle_model{circle_model} {} +ModelEditor::ModelEditor(std::shared_ptr circle_model) : _circle_model{circle_model} +{ + assert(circle_model != nullptr); // FIX_CALLER_UNLESS +} ModelEditor &ModelEditor::resize_inputs(const std::vector &new_inputs_shapes) { @@ -83,7 +86,7 @@ ModelEditor &ModelEditor::resize_inputs(const std::vector &new_inputs_sha } catch (const std::exception &e) { - throw std::runtime_error("Exception during shape inference with message: " + + throw std::runtime_error("Exception during resizing with message: " + std::string{e.what()}); } diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp index 303682c791f..2c7d2c2fba8 100644 --- a/compiler/circle-resizer/tests/ModelEditor.test.cpp +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -36,7 +36,7 @@ class ModelEditorTest : public ::testing::Test if (path == nullptr) { throw std::runtime_error("environmental variable ARTIFACTS_PATH required for circle-resizer " - "tests was not not provided"); + "tests was not provided"); } _test_models_dir = path; } From ee1dac38b8e09c21154c4b6fb21a2e28983666ef Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Fri, 25 Apr 2025 08:48:07 +0200 Subject: [PATCH 3/5] styles applied --- compiler/circle-resizer/src/ModelEditor.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index c585b06355a..f569e140618 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -86,8 +86,7 @@ ModelEditor &ModelEditor::resize_inputs(const std::vector &new_inputs_sha } catch (const std::exception &e) { - throw std::runtime_error("Exception during resizing with message: " + - std::string{e.what()}); + throw std::runtime_error("Exception during resizing with message: " + std::string{e.what()}); } return *this; From db82326ed952b1d6ca9356d6623d91fe9ef0268b Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Fri, 25 Apr 2025 13:23:12 +0200 Subject: [PATCH 4/5] unit tests clean-up --- compiler/circle-resizer/src/ModelEditor.cpp | 4 +- .../circle-resizer/tests/ModelEditor.test.cpp | 118 ++++++++++++------ 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index f569e140618..66f45cefce0 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -41,6 +41,7 @@ void change_single_input_shape(luci::CircleInput *circle_input, const Shape &new } else { + // a value here can be in range (0, std::numeric_limits::max()) so the cast is safe circle_input->dim(i) = loco::Dimension(static_cast(new_shape[i].value())); } } @@ -52,8 +53,7 @@ void change_inputs_shapes(loco::Graph *graph, const std::vector &new_inpu if (graph_inputs.size() != new_inputs_shapes.size()) { throw std::runtime_error("Expected " + std::to_string(graph_inputs.size()) + - " shapes but provided only " + - std::to_string(new_inputs_shapes.size())); + " shapes but provided " + std::to_string(new_inputs_shapes.size())); } for (size_t in_idx = 0; in_idx < new_inputs_shapes.size(); ++in_idx) { diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp index 2c7d2c2fba8..41b70789f95 100644 --- a/compiler/circle-resizer/tests/ModelEditor.test.cpp +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -33,7 +33,7 @@ class ModelEditorTest : public ::testing::Test void SetUp() override { char *path = std::getenv("ARTIFACTS_PATH"); - if (path == nullptr) + if (nullptr == path) { throw std::runtime_error("environmental variable ARTIFACTS_PATH required for circle-resizer " "tests was not provided"); @@ -45,50 +45,73 @@ class ModelEditorTest : public ::testing::Test std::string _test_models_dir; }; -TEST_F(ModelEditorTest, single_input_single_output) +TEST_F(ModelEditorTest, basic_tests) { + // single input, single output auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); ModelEditor editor(circle_model); - const auto new_input_shapes = std::vector{Shape{4, 6}}; + auto new_input_shapes = std::vector{Shape{4, 6}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{4, 1, 6}})); -} -TEST_F(ModelEditorTest, single_input_two_outputs) -{ - auto circle_model = std::make_shared(_test_models_dir + "/CSE_Quantize_000.circle"); - ModelEditor editor(circle_model); - const auto new_input_shapes = std::vector{Shape{1, 6, 6, 4}}; + // single input, two outputs + circle_model = std::make_shared(_test_models_dir + "/CSE_Quantize_000.circle"); + editor = ModelEditor(circle_model); + new_input_shapes = std::vector{Shape{1, 6, 6, 4}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{1, 6, 6, 4}, Shape{1, 6, 6, 4}})); -} -TEST_F(ModelEditorTest, two_inputs_single_output) -{ - auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); - ModelEditor editor(circle_model); - const auto new_input_shapes = std::vector{Shape{1, 5, 5, 3}, Shape{1, 5, 5, 3}}; + // two inputs, single output + circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); + editor = ModelEditor(circle_model); + new_input_shapes = std::vector{Shape{1, 5, 5, 3}, Shape{1, 5, 5, 3}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{1, 5, 5, 3}})); + + // two inputs two outputs + circle_model = + std::make_shared(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); + editor = ModelEditor(circle_model); + new_input_shapes = std::vector{Shape{1, 5, 5, 2}, Shape{1, 5, 5, 2}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), + (std::vector{Shape{1, 5, 5, 2}, Shape{1, 5, 5, 2}})); + + // change even the input rank + circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + editor = ModelEditor(circle_model); + new_input_shapes = std::vector{Shape{1, 2, 3, 4}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{1, 1, 2, 3, 4}})); } -TEST_F(ModelEditorTest, two_inputs_two_outputs) +TEST_F(ModelEditorTest, special_cases) { - auto circle_model = - std::make_shared(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); + // resize to dynamic shape + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); ModelEditor editor(circle_model); - const auto new_input_shapes = std::vector{Shape{1, 5, 5, 2}, Shape{1, 5, 5, 2}}; + auto new_input_shapes = std::vector{Shape{Dim{4}, Dim::dynamic()}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); EXPECT_EQ(circle_model->output_shapes(), - (std::vector{Shape{1, 5, 5, 2}, Shape{1, 5, 5, 2}})); + (std::vector{Shape{Dim{4}, Dim{1}, Dim::dynamic()}})); + + // resize to scalars + circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); + editor = ModelEditor(circle_model); + new_input_shapes = std::vector{Shape::scalar(), Shape::scalar()}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape::scalar()})); } -TEST_F(ModelEditorTest, resize_applied_after_save) +TEST_F(ModelEditorTest, resizing_applied_after_save) { auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); ModelEditor editor(circle_model); @@ -105,7 +128,7 @@ TEST_F(ModelEditorTest, resize_applied_after_save) EXPECT_EQ(circle_model_from_saved_buffer->output_shapes(), (std::vector{Shape{4, 1, 6}})); } -TEST_F(ModelEditorTest, single_input_single_output_double_resizing) +TEST_F(ModelEditorTest, double_resizing) { auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); ModelEditor editor(circle_model); @@ -116,39 +139,56 @@ TEST_F(ModelEditorTest, single_input_single_output_double_resizing) EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{4, 1, 6}})); } -TEST_F(ModelEditorTest, change_input_rank) +TEST_F(ModelEditorTest, no_inputs_shapes_provided_NEG) { - auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); ModelEditor editor(circle_model); - const auto new_input_shapes = std::vector{Shape{1, 2, 3, 4}}; - editor.resize_inputs(new_input_shapes); - EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); - EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{1, 1, 2, 3, 4}})); + try + { + editor.resize_inputs({}); + FAIL() << "Unexpected successful resizing with invalid shapes."; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Expected 2 shapes but provided 0")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } } -TEST_F(ModelEditorTest, resize_to_dynamic) +TEST_F(ModelEditorTest, not_all_inputs_shapes_provided_NEG) { - auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); ModelEditor editor(circle_model); - const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim::dynamic()}}; - editor.resize_inputs(new_input_shapes); - EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); - EXPECT_EQ(circle_model->output_shapes(), - (std::vector{Shape{Dim{4}, Dim{1}, Dim::dynamic()}})); + try + { + editor.resize_inputs(std::vector{Shape{1, 5, 5, 3}}); + FAIL() << "Unexpected successful resizing with invalid shapes."; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Expected 2 shapes but provided 1")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } } -TEST_F(ModelEditorTest, not_all_input_shapes_provided_NEG) +TEST_F(ModelEditorTest, to_much_inputs_shapes_provided_NEG) { auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); ModelEditor editor(circle_model); try { - editor.resize_inputs(std::vector{Shape{1, 5, 5, 3}}); + editor.resize_inputs(std::vector{Shape{1, 2}, Shape{3, 4}, Shape{5, 6}}); FAIL() << "Unexpected successful resizing with invalid shapes."; } catch (const std::runtime_error &err) { - EXPECT_THAT(err.what(), HasSubstr("Expected 2 shapes but provided only 1")); + EXPECT_THAT(err.what(), HasSubstr("Expected 2 shapes but provided 3")); } catch (...) { @@ -167,7 +207,7 @@ TEST_F(ModelEditorTest, exception_during_shape_inference_NEG) } catch (const std::runtime_error &err) { - EXPECT_THAT(err.what(), HasSubstr("Exception during shape inference with message:")); + EXPECT_THAT(err.what(), HasSubstr("Exception during resizing with message:")); } catch (...) { From 5403a7651db6bb31eae82affa0818d7ccad25701 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Fri, 25 Apr 2025 13:30:56 +0200 Subject: [PATCH 5/5] remove unnecessary header --- compiler/circle-resizer/tests/ModelEditor.test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp index 41b70789f95..a1eb6e74057 100644 --- a/compiler/circle-resizer/tests/ModelEditor.test.cpp +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include