From 84b57317d5d64b0157f7ceb575b6178956a437e7 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Fri, 21 Feb 2025 08:22:00 +0100 Subject: [PATCH 01/39] [circle-resizer] Introduce a new tool to models resizing This commit introduces a new tool for Circle model resizing. ONE-DCO-1.0-Signed-off-by: Mateusz Bencer --- compiler/circle-resizer/CMakeLists.txt | 41 ++++++ .../circle-resizer/include/CircleResizer.h | 49 +++++++ compiler/circle-resizer/include/Shape.h | 43 ++++++ compiler/circle-resizer/include/ShapeParser.h | 29 ++++ compiler/circle-resizer/requires.cmake | 5 + compiler/circle-resizer/src/CircleResizer.cpp | 136 ++++++++++++++++++ compiler/circle-resizer/src/Shape.cpp | 35 +++++ compiler/circle-resizer/src/ShapeParser.cpp | 76 ++++++++++ compiler/circle-resizer/src/main.cpp | 27 ++++ .../tests/CircleResizer.test.cpp | 51 +++++++ .../circle-resizer/tests/ShapeParser.test.cpp | 78 ++++++++++ .../DynInputs_Add_001/test.recipe | 28 ++++ .../DynInputs_Add_001/test.reverse | 0 13 files changed, 598 insertions(+) create mode 100644 compiler/circle-resizer/CMakeLists.txt create mode 100644 compiler/circle-resizer/include/CircleResizer.h create mode 100644 compiler/circle-resizer/include/Shape.h create mode 100644 compiler/circle-resizer/include/ShapeParser.h create mode 100644 compiler/circle-resizer/requires.cmake create mode 100644 compiler/circle-resizer/src/CircleResizer.cpp create mode 100644 compiler/circle-resizer/src/Shape.cpp create mode 100644 compiler/circle-resizer/src/ShapeParser.cpp create mode 100644 compiler/circle-resizer/src/main.cpp create mode 100644 compiler/circle-resizer/tests/CircleResizer.test.cpp create mode 100644 compiler/circle-resizer/tests/ShapeParser.test.cpp create mode 100644 res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/DynInputs_Add_001/test.reverse diff --git a/compiler/circle-resizer/CMakeLists.txt b/compiler/circle-resizer/CMakeLists.txt new file mode 100644 index 00000000000..9e1de16e591 --- /dev/null +++ b/compiler/circle-resizer/CMakeLists.txt @@ -0,0 +1,41 @@ +list(APPEND CIRCLE_RESIZER_SOURCES src/Shape.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES src/ShapeParser.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES src/CircleResizer.cpp) + +add_library(circle_resizer_core STATIC "${CIRCLE_RESIZER_SOURCES}") + +target_include_directories(circle_resizer_core PUBLIC include) + +target_link_libraries(circle_resizer_core PRIVATE mio_circle08) +target_link_libraries(circle_resizer_core PRIVATE logo_core) +target_link_libraries(circle_resizer_core PRIVATE luci_pass) +target_link_libraries(circle_resizer_core PRIVATE luci_lang) +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 logo) +target_link_libraries(circle_resizer_core PRIVATE luci_log) + +add_executable(circle_resizer src/main.cpp) +target_link_libraries(circle_resizer PRIVATE circle_resizer_core) + +set_target_properties(circle_resizer PROPERTIES + OUTPUT_NAME "circle-resizer" +) + +install(TARGETS circle_resizer DESTINATION bin) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +list(APPEND CIRCLE_RESIZER_TEST_SOURCES tests/ShapeParser.test.cpp) +list(APPEND CIRCLE_RESIZER_TEST_SOURCES tests/CircleResizer.test.cpp) + +nnas_find_package(GTest REQUIRED) +GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES}) +target_link_libraries(circle_resizer_unit_test circle_resizer_core) + +get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) +set_tests_properties(circle_resizer_unit_test + PROPERTIES + ENVIRONMENT "ARTIFACTS_PATH=${ARTIFACTS_PATH}") diff --git a/compiler/circle-resizer/include/CircleResizer.h b/compiler/circle-resizer/include/CircleResizer.h new file mode 100644 index 00000000000..52ab334f19f --- /dev/null +++ b/compiler/circle-resizer/include/CircleResizer.h @@ -0,0 +1,49 @@ +/* + * 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_H__ +#define __CIRCLE_RESIZER_H__ + +#include "Shape.h" + +#include +#include +#include + +namespace luci { +class Module; +} + +namespace circle_resizer +{ + class CircleResizer { + public: + explicit CircleResizer(const std::string& model_path); + // to satisfy forward declaration + unique_ptr + ~CircleResizer(); + + public: + void resize_model(const std::vector& shapes); + void save_model(const std::string& output_path) const; + std::vector model_buffer() const; + + private: + std::string _model_path; + std::unique_ptr _module; + }; +} // namespace circle_resizer + +#endif // __CIRCLE_RESIZER_H__ diff --git a/compiler/circle-resizer/include/Shape.h b/compiler/circle-resizer/include/Shape.h new file mode 100644 index 00000000000..b8790b68b18 --- /dev/null +++ b/compiler/circle-resizer/include/Shape.h @@ -0,0 +1,43 @@ +/* + * 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_SHAPE_H__ +#define __CIRCLE_RESIZER_SHAPE_H__ + +#include +#include +#include + +namespace circle_resizer +{ + class Dim { + public: + explicit Dim(int32_t dim); + + public: + bool is_dynamic(); + int32_t value() const; + bool operator==(const Dim& rhs) const; + + private: + // Note that in the future, we might need to support dimension with lower and upper bounds + int32_t _dim_value; + }; + + using Shape = std::vector; +} // namespace circle_resizer + +#endif // __CIRCLE_RESIZER_SHAPE_H__ diff --git a/compiler/circle-resizer/include/ShapeParser.h b/compiler/circle-resizer/include/ShapeParser.h new file mode 100644 index 00000000000..6b0343a9ffb --- /dev/null +++ b/compiler/circle-resizer/include/ShapeParser.h @@ -0,0 +1,29 @@ +/* + * 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_SHAPE_PARSER_H__ +#define __CIRCLE_RESIZER_SHAPE_PARSER_H__ + +#include "Shape.h" + +#include +#include + +namespace circle_resizer { + std::vector parse_shapes(std::string shapes_str); +} // circle_resizer + +#endif // __CIRCLE_RESIZER_SHAPE_PARSER_H__ diff --git a/compiler/circle-resizer/requires.cmake b/compiler/circle-resizer/requires.cmake new file mode 100644 index 00000000000..624621b1872 --- /dev/null +++ b/compiler/circle-resizer/requires.cmake @@ -0,0 +1,5 @@ +require("mio-circle08") +require("logo-core") +require("luci") +require("logo") +require("common-artifacts") diff --git a/compiler/circle-resizer/src/CircleResizer.cpp b/compiler/circle-resizer/src/CircleResizer.cpp new file mode 100644 index 00000000000..0cb9a02d8c9 --- /dev/null +++ b/compiler/circle-resizer/src/CircleResizer.cpp @@ -0,0 +1,136 @@ +/* + * 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 "CircleResizer.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "luci/Import/GraphBuilderRegistry.h" + +#include + +#include +#include +#include +#include + +using namespace circle_resizer; + +namespace { +std::vector read_model(const std::string& model_path) { + std::ifstream file_stream(model_path, std::ios::in | std::ios::binary | std::ifstream::ate); + if (!file_stream.is_open()) { + throw std::runtime_error("Failed to open file: " + model_path); + } + + std::streamsize size = file_stream.tellg(); + file_stream.seekg(0, std::ios::beg); + + std::vector buffer(size); + if (!file_stream.read(reinterpret_cast(buffer.data()), size)) { + throw std::runtime_error("Failed to read file: " + model_path); + } + + return buffer; +} + +void replace_tensor_shape(::flatbuffers::Vector* tensor_shape, const Shape& new_shape) +{ + const auto shape_size = tensor_shape->size(); + if(shape_size != new_shape.size()) + { + throw std::runtime_error("Provided shape size: " + std::to_string(new_shape.size()) + " is different from expected: " + std::to_string(shape_size)); + } + for (uint32_t dim_idx = 0; dim_idx < shape_size; ++dim_idx) + { + tensor_shape->Mutate(dim_idx, new_shape[dim_idx].value()); + } +} + +} // namespace + +CircleResizer::CircleResizer(const std::string& model_path) +: _model_path{model_path} +{ +} + +void CircleResizer::resize_model(const std::vector& shapes) +{ + auto model_buffer = read_model(_model_path); + auto model = circle::GetMutableModel(model_buffer.data()); + if (!model) + { + throw std::runtime_error("Incorrect model format"); + } + auto subgraphs = model->mutable_subgraphs(); + if (!subgraphs || subgraphs->size() != 1) + { + throw std::runtime_error("Many subgraphs are not supported"); + } + auto subgraph = subgraphs->GetMutableObject(0); + const auto inputs_number = subgraph->inputs()->size(); + if(!inputs_number == shapes.size()) + { + throw std::runtime_error("Expected input shapes: " + std::to_string(inputs_number) + " while provided: " + std::to_string(shapes.size())); + } + for(int in_idx = 0; in_idx < inputs_number; ++in_idx) + { + const auto in_tensor_idx = subgraph->inputs()->Get(in_idx); + auto input_shape = subgraph->mutable_tensors()->GetMutableObject(in_tensor_idx)->mutable_shape(); + replace_tensor_shape(input_shape, shapes[in_idx]); + } + + logo::Phase phase; + + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + + flatbuffers::Verifier verifier{model_buffer.data(), model_buffer.size()}; + if (!circle::VerifyModelBuffer(verifier)) + { + throw std::runtime_error("Verification of the model failed"); + } + + const luci::GraphBuilderSource *source_ptr = &luci::GraphBuilderRegistry::get(); + luci::Importer importer(source_ptr); + _module = importer.importModule(model_buffer.data(), model_buffer.size()); + + auto graph = _module->graph(); + logo::PhaseRunner phase_runner{graph}; + phase_runner.run(phase); +} + +void CircleResizer::save_model(const std::string& output_path) const +{ + luci::CircleExporter exporter; + luci::CircleFileExpContract contract(_module.get(), output_path); + + if (!exporter.invoke(&contract)) + { + throw std::runtime_error("Saving model failed"); + } +} + +CircleResizer::~CircleResizer() = default; + diff --git a/compiler/circle-resizer/src/Shape.cpp b/compiler/circle-resizer/src/Shape.cpp new file mode 100644 index 00000000000..e3c18b353dd --- /dev/null +++ b/compiler/circle-resizer/src/Shape.cpp @@ -0,0 +1,35 @@ +/* + * 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 "Shape.h" + +using namespace circle_resizer; + +Dim::Dim(int32_t dim) : _dim_value{dim} { + if (dim < -1) { + throw std::runtime_error("Invalid value of dimension: " + dim); + } +} + +bool Dim::is_dynamic() { return _dim_value == -1; } + +int32_t Dim::value() const { + return _dim_value; +} + +bool Dim::operator==(const Dim& rhs) const { + return value() == rhs.value(); +} diff --git a/compiler/circle-resizer/src/ShapeParser.cpp b/compiler/circle-resizer/src/ShapeParser.cpp new file mode 100644 index 00000000000..5a37353f270 --- /dev/null +++ b/compiler/circle-resizer/src/ShapeParser.cpp @@ -0,0 +1,76 @@ +/* + * 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 "ShapeParser.h" + +#include +#include +#include + +using namespace circle_resizer; + +namespace { + bool is_blank(const std::string& s) + { + return !s.empty() && std::find_if(s.begin(), + s.end(), [](unsigned char c) { return !std::isblank(c); }) == s.end(); + } + + Shape parse_shape(std::string shape_str) { + Shape result_shape; + std::stringstream shape_stream(shape_str); + std::string token; + try + { + while (std::getline(shape_stream, token, ',')) + { + result_shape.push_back(Dim{std::stoi(token)}); + } + } + catch (...) + { + throw std::invalid_argument("Error during shape processing: " + shape_str); + } + if(result_shape.empty()) { + throw std::invalid_argument("No shapes found in input string: " + shape_str); + } + return result_shape; + } +} // namespace + +std::vector circle_resizer::parse_shapes(std::string shapes_str) +{ + std::vector result_shapes; + std::stringstream shapes_stream(shapes_str); + std::string token; + size_t begin_pos = 0, end_pos=0; + while ( (begin_pos = shapes_str.find("[")) != std::string::npos && (end_pos = shapes_str.find("]")) != std::string::npos ) { + token = shapes_str.substr(begin_pos + 1, end_pos); + result_shapes.push_back(parse_shape(token)); + shapes_str.erase(0, end_pos + 1); + } + + if(result_shapes.empty()) { + throw std::invalid_argument("No shapes found in input string: " + shapes_str); + } + + // the rest of input not processed by loop above cannot be processed properly + if(shapes_str.size() > 0 && !is_blank(shapes_str)) { + throw std::invalid_argument("The part of input shape: " + shapes_str + " cannot be processed"); + } + + return result_shapes; +} diff --git a/compiler/circle-resizer/src/main.cpp b/compiler/circle-resizer/src/main.cpp new file mode 100644 index 00000000000..816a56c4161 --- /dev/null +++ b/compiler/circle-resizer/src/main.cpp @@ -0,0 +1,27 @@ +/* + * 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 "CircleResizer.h" +#include "Shape.h" + +using namespace circle_resizer; + +int main(int argc, char *argv[]) { + CircleResizer resizer(argv[1]); + resizer.resize_model({Shape{Dim{1}, Dim{3}}}); // experiment + resizer.save_model(argv[2]); + return 0; +} diff --git a/compiler/circle-resizer/tests/CircleResizer.test.cpp b/compiler/circle-resizer/tests/CircleResizer.test.cpp new file mode 100644 index 00000000000..1aa49363ea6 --- /dev/null +++ b/compiler/circle-resizer/tests/CircleResizer.test.cpp @@ -0,0 +1,51 @@ +/* + * 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 "CircleResizer.h" + +#include +#include +#include +#include + + +using namespace circle_resizer; + +class CircleResizerTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + _test_models_dir = std::getenv("ARTIFACTS_PATH"); + assert(!_test_models_dir.empty()); + std::cout << "---: " << _test_models_dir << "---" << "\n";; + } +protected: + std::string _test_models_dir; + +protected: + bool verify_output_shapes(const std::vector& expected_shapes) + { + return true; + } +}; + +TEST_F(CircleResizerTest, basic_test) +{ + CircleResizer resizer(_test_models_dir + "/DynInputs_Add_001.circle"); + resizer.resize_model({Shape{Dim{1}, Dim{5}, Dim{1}}, Shape{Dim{1}, Dim{5}, Dim{1}}}); + resizer.save_model("resized.circle"); +} diff --git a/compiler/circle-resizer/tests/ShapeParser.test.cpp b/compiler/circle-resizer/tests/ShapeParser.test.cpp new file mode 100644 index 00000000000..399fefbe0c5 --- /dev/null +++ b/compiler/circle-resizer/tests/ShapeParser.test.cpp @@ -0,0 +1,78 @@ +/* + * 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 "ShapeParser.h" + +#include + +#include +#include +#include +#include + +using namespace circle_resizer; + +class ParseShapeTestFixture : public ::testing::TestWithParam>> {}; + +TEST_P(ParseShapeTestFixture, proper_shape_returned) { + const auto [input_shape_str, expected_shapes] = GetParam(); + std::vector result_shapes; + EXPECT_NO_THROW(result_shapes = parse_shapes(input_shape_str)); + ASSERT_EQ(result_shapes, expected_shapes); +} + +INSTANTIATE_TEST_SUITE_P( + ParseShapeTest, + ParseShapeTestFixture, + ::testing::Values( + // single shape + std::make_tuple("[3,4]", std::vector{Shape{Dim{3}, Dim{4}}}), + std::make_tuple("[3]", std::vector{Shape{Dim{3}}}), + std::make_tuple("[-1]", std::vector{Shape{Dim{-1}}}), + std::make_tuple("[ 5, 6]", std::vector{Shape{Dim{5}, Dim{6}}}), + std::make_tuple("[3 , 4]", std::vector{Shape{Dim{3}, Dim{4}}}), + std::make_tuple("[-1 , 4]", std::vector{Shape{Dim{-1}, Dim{4}}}), + // many shapes + std::make_tuple("[3,4],[5,6]", std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), + std::make_tuple("[1],[2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [3, 4] , [5,6]", std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), + std::make_tuple(" [ 1 ] ,[ 2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [ 1 ] ,[ 2] ", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [1,2],[3,4,5],[6,7,8,9]", std::vector{Shape{Dim{1}, Dim{2}}, Shape{Dim{3}, Dim{4}, Dim{5}}, Shape{Dim{6}, Dim{7}, Dim{8}, Dim{9}}}) + )); + +class InvalidArgParseShapeTestFixture : public ::testing::TestWithParam {}; + +TEST_P(InvalidArgParseShapeTestFixture, invalid_input) { + EXPECT_THROW(parse_shapes(GetParam()), std::invalid_argument); +} + +INSTANTIATE_TEST_SUITE_P( + InvalidArgParseShape, + InvalidArgParseShapeTestFixture, + ::testing::Values( + std::string{""}, + std::string{"]"}, + std::string{"1a,2"}, + std::string{"[-2]"}, + std::string{"[-2,1,3]"}, + std::string{"-1"}, + std::string{"7,7"}, + std::string{"8"}, + std::string{"[8],9"}, + std::string{"1,2"}, + std::string{"[1],[2],"} + )); diff --git a/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe b/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe new file mode 100644 index 00000000000..14239ca91e8 --- /dev/null +++ b/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe @@ -0,0 +1,28 @@ +# This recipe is to test zero size tensor for luci and luci-interpreter +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 0 dim: 1 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 1 dim: 0 dim: 1 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 0 dim: 1 } +} +operation { + type: "Add" + input: "ifm1" + input: "ifm2" + output: "ofm" + add_options { + activation: NONE + } +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.reverse b/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.reverse new file mode 100644 index 00000000000..e69de29bb2d From 5f4135b38e54c62d227a722c7889da387384396d Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Fri, 21 Feb 2025 16:43:21 +0100 Subject: [PATCH 02/39] input_shapes and output_shapes methods --- .../circle-resizer/include/CircleResizer.h | 5 +- compiler/circle-resizer/src/CircleResizer.cpp | 41 ++++++++++++---- .../tests/CircleResizer.test.cpp | 31 ++++++++---- .../luci/Service/CircleShapeInference.h | 2 +- .../luci/service/src/Nodes/CircleOutput.cpp | 47 +++++++++++++++++++ 5 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 compiler/luci/service/src/Nodes/CircleOutput.cpp diff --git a/compiler/circle-resizer/include/CircleResizer.h b/compiler/circle-resizer/include/CircleResizer.h index 52ab334f19f..d8490b67393 100644 --- a/compiler/circle-resizer/include/CircleResizer.h +++ b/compiler/circle-resizer/include/CircleResizer.h @@ -38,7 +38,10 @@ namespace circle_resizer public: void resize_model(const std::vector& shapes); void save_model(const std::string& output_path) const; - std::vector model_buffer() const; + + public: + std::vector input_shapes() const; + std::vector output_shapes() const; private: std::string _model_path; diff --git a/compiler/circle-resizer/src/CircleResizer.cpp b/compiler/circle-resizer/src/CircleResizer.cpp index 0cb9a02d8c9..c0ce5185a78 100644 --- a/compiler/circle-resizer/src/CircleResizer.cpp +++ b/compiler/circle-resizer/src/CircleResizer.cpp @@ -25,7 +25,7 @@ #include #include #include -#include "luci/Import/GraphBuilderRegistry.h" +#include #include @@ -67,6 +67,23 @@ void replace_tensor_shape(::flatbuffers::Vector* tensor_shape, const Sh } } +template +std::vector extract_shapes(const std::vector& nodes) +{ + std::vector shapes; + for(const auto& loco_node : nodes) + { + shapes.push_back(Shape{}); + const auto circle_node = loco::must_cast(loco_node); + for (uint32_t dim_idx = 0; dim_idx < circle_node->rank(); dim_idx++) + { + const int32_t dim_val = circle_node->dim(dim_idx).value(); + shapes.back().push_back(Dim{dim_val}); + } + } + return shapes; +} + } // namespace CircleResizer::CircleResizer(const std::string& model_path) @@ -100,12 +117,6 @@ void CircleResizer::resize_model(const std::vector& shapes) replace_tensor_shape(input_shape, shapes[in_idx]); } - logo::Phase phase; - - phase.emplace_back(std::make_unique()); - phase.emplace_back(std::make_unique()); - phase.emplace_back(std::make_unique()); - flatbuffers::Verifier verifier{model_buffer.data(), model_buffer.size()}; if (!circle::VerifyModelBuffer(verifier)) { @@ -116,6 +127,11 @@ void CircleResizer::resize_model(const std::vector& shapes) luci::Importer importer(source_ptr); _module = importer.importModule(model_buffer.data(), model_buffer.size()); + logo::Phase phase; + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + phase.emplace_back(std::make_unique()); + auto graph = _module->graph(); logo::PhaseRunner phase_runner{graph}; phase_runner.run(phase); @@ -132,5 +148,14 @@ void CircleResizer::save_model(const std::string& output_path) const } } -CircleResizer::~CircleResizer() = default; +std::vector CircleResizer::input_shapes() const +{ + return extract_shapes(loco::input_nodes(_module->graph())); +} + +std::vector CircleResizer::output_shapes() const +{ + return extract_shapes(loco::output_nodes(_module->graph())); +} +CircleResizer::~CircleResizer() = default; diff --git a/compiler/circle-resizer/tests/CircleResizer.test.cpp b/compiler/circle-resizer/tests/CircleResizer.test.cpp index 1aa49363ea6..8b90e751b24 100644 --- a/compiler/circle-resizer/tests/CircleResizer.test.cpp +++ b/compiler/circle-resizer/tests/CircleResizer.test.cpp @@ -20,10 +20,26 @@ #include #include #include - +#include using namespace circle_resizer; +namespace { + +// TODO: Remove, just for debug +void print_shapes(const std::vector& shapes) { + for (const auto& shape : shapes) { + std::cout << "["; + for (const auto& dim : shape) { + std::cout << dim.value() << ","; + } + std::cout << "],"; + } + std::cout << std::endl; +} + +} // namespace + class CircleResizerTest : public ::testing::Test { protected: @@ -31,21 +47,16 @@ class CircleResizerTest : public ::testing::Test { _test_models_dir = std::getenv("ARTIFACTS_PATH"); assert(!_test_models_dir.empty()); - std::cout << "---: " << _test_models_dir << "---" << "\n";; } protected: std::string _test_models_dir; - -protected: - bool verify_output_shapes(const std::vector& expected_shapes) - { - return true; - } }; TEST_F(CircleResizerTest, basic_test) { CircleResizer resizer(_test_models_dir + "/DynInputs_Add_001.circle"); - resizer.resize_model({Shape{Dim{1}, Dim{5}, Dim{1}}, Shape{Dim{1}, Dim{5}, Dim{1}}}); - resizer.save_model("resized.circle"); + const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}, Shape{Dim{1}, Dim{5}, Dim{1}}}; + resizer.resize_model(new_input_shapes); + ASSERT_EQ(resizer.input_shapes(), new_input_shapes); + ASSERT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}})); } diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInference.h b/compiler/luci/service/include/luci/Service/CircleShapeInference.h index 6c16cb6d210..0aaac56c620 100644 --- a/compiler/luci/service/include/luci/Service/CircleShapeInference.h +++ b/compiler/luci/service/include/luci/Service/CircleShapeInference.h @@ -173,7 +173,7 @@ class Algorithm final : public luci::CircleNodeVisitor // loco::TensorShape visit(const luci::CircleInput *node) final; // loco::TensorShape visit(const luci::CircleNonMaxSuppressionV4Out *node) final; // loco::TensorShape visit(const luci::CircleNonMaxSuppressionV5Out *node) final; - // loco::TensorShape visit(const luci::CircleOutput *node) final; + loco::TensorShape visit(const luci::CircleOutput *node) final; // loco::TensorShape visit(const luci::CircleOutputDummy *node) final; // loco::TensorShape visit(const luci::CircleOutputExclude *node) final; // loco::TensorShape visit(const luci::CircleSplitOut *node) final; diff --git a/compiler/luci/service/src/Nodes/CircleOutput.cpp b/compiler/luci/service/src/Nodes/CircleOutput.cpp new file mode 100644 index 00000000000..995f116b034 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleOutput.cpp @@ -0,0 +1,47 @@ +/* + * 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 "luci/Service/CircleShapeInference.h" + +#include "CircleShapeInferenceHelper.h" +#include + +namespace luci +{ + +namespace sinf +{ +namespace { +void print_shape(const loco::TensorShape &shape) +{ + for(int i=0;i(node->from()); + return sinf::circle_shape(from_shape); +} + +} // namespace sinf + +} // namespace luci + From 4361786f36046384a87592a7f932610efaf3270c Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Fri, 21 Feb 2025 18:31:28 +0100 Subject: [PATCH 03/39] styles applied --- .../circle-resizer/include/CircleResizer.h | 40 ++++---- compiler/circle-resizer/include/Shape.h | 25 ++--- compiler/circle-resizer/include/ShapeParser.h | 7 +- compiler/circle-resizer/src/CircleResizer.cpp | 66 +++++++------- compiler/circle-resizer/src/Shape.cpp | 18 ++-- compiler/circle-resizer/src/ShapeParser.cpp | 91 ++++++++++--------- compiler/circle-resizer/src/main.cpp | 3 +- .../tests/CircleResizer.test.cpp | 36 +++++--- .../circle-resizer/tests/ShapeParser.test.cpp | 85 +++++++++-------- .../luci/service/src/Nodes/CircleOutput.cpp | 12 +-- 10 files changed, 196 insertions(+), 187 deletions(-) diff --git a/compiler/circle-resizer/include/CircleResizer.h b/compiler/circle-resizer/include/CircleResizer.h index d8490b67393..8baa62b7d19 100644 --- a/compiler/circle-resizer/include/CircleResizer.h +++ b/compiler/circle-resizer/include/CircleResizer.h @@ -23,30 +23,32 @@ #include #include -namespace luci { +namespace luci +{ class Module; } namespace circle_resizer { - class CircleResizer { - public: - explicit CircleResizer(const std::string& model_path); - // to satisfy forward declaration + unique_ptr - ~CircleResizer(); - - public: - void resize_model(const std::vector& shapes); - void save_model(const std::string& output_path) const; - - public: - std::vector input_shapes() const; - std::vector output_shapes() const; - - private: - std::string _model_path; - std::unique_ptr _module; - }; +class CircleResizer +{ +public: + explicit CircleResizer(const std::string &model_path); + // to satisfy forward declaration + unique_ptr + ~CircleResizer(); + +public: + void resize_model(const std::vector &shapes); + void save_model(const std::string &output_path) const; + +public: + std::vector input_shapes() const; + std::vector output_shapes() const; + +private: + std::string _model_path; + std::unique_ptr _module; +}; } // namespace circle_resizer #endif // __CIRCLE_RESIZER_H__ diff --git a/compiler/circle-resizer/include/Shape.h b/compiler/circle-resizer/include/Shape.h index b8790b68b18..1938261176d 100644 --- a/compiler/circle-resizer/include/Shape.h +++ b/compiler/circle-resizer/include/Shape.h @@ -23,21 +23,22 @@ namespace circle_resizer { - class Dim { - public: - explicit Dim(int32_t dim); +class Dim +{ +public: + explicit Dim(int32_t dim); - public: - bool is_dynamic(); - int32_t value() const; - bool operator==(const Dim& rhs) const; +public: + bool is_dynamic(); + int32_t value() const; + bool operator==(const Dim &rhs) const; - private: - // Note that in the future, we might need to support dimension with lower and upper bounds - int32_t _dim_value; - }; +private: + // Note that in the future, we might need to support dimension with lower and upper bounds + int32_t _dim_value; +}; - using Shape = std::vector; +using Shape = std::vector; } // namespace circle_resizer #endif // __CIRCLE_RESIZER_SHAPE_H__ diff --git a/compiler/circle-resizer/include/ShapeParser.h b/compiler/circle-resizer/include/ShapeParser.h index 6b0343a9ffb..368ca9cdec9 100644 --- a/compiler/circle-resizer/include/ShapeParser.h +++ b/compiler/circle-resizer/include/ShapeParser.h @@ -22,8 +22,9 @@ #include #include -namespace circle_resizer { - std::vector parse_shapes(std::string shapes_str); -} // circle_resizer +namespace circle_resizer +{ +std::vector parse_shapes(std::string shapes_str); +} // namespace circle_resizer #endif // __CIRCLE_RESIZER_SHAPE_PARSER_H__ diff --git a/compiler/circle-resizer/src/CircleResizer.cpp b/compiler/circle-resizer/src/CircleResizer.cpp index c0ce5185a78..c6b1d6b5119 100644 --- a/compiler/circle-resizer/src/CircleResizer.cpp +++ b/compiler/circle-resizer/src/CircleResizer.cpp @@ -36,30 +36,35 @@ using namespace circle_resizer; -namespace { -std::vector read_model(const std::string& model_path) { - std::ifstream file_stream(model_path, std::ios::in | std::ios::binary | std::ifstream::ate); - if (!file_stream.is_open()) { - throw std::runtime_error("Failed to open file: " + model_path); - } +namespace +{ +std::vector read_model(const std::string &model_path) +{ + std::ifstream file_stream(model_path, std::ios::in | std::ios::binary | std::ifstream::ate); + if (!file_stream.is_open()) + { + throw std::runtime_error("Failed to open file: " + model_path); + } - std::streamsize size = file_stream.tellg(); - file_stream.seekg(0, std::ios::beg); + std::streamsize size = file_stream.tellg(); + file_stream.seekg(0, std::ios::beg); - std::vector buffer(size); - if (!file_stream.read(reinterpret_cast(buffer.data()), size)) { - throw std::runtime_error("Failed to read file: " + model_path); - } + std::vector buffer(size); + if (!file_stream.read(reinterpret_cast(buffer.data()), size)) + { + throw std::runtime_error("Failed to read file: " + model_path); + } - return buffer; + return buffer; } -void replace_tensor_shape(::flatbuffers::Vector* tensor_shape, const Shape& new_shape) +void replace_tensor_shape(::flatbuffers::Vector *tensor_shape, const Shape &new_shape) { const auto shape_size = tensor_shape->size(); - if(shape_size != new_shape.size()) + if (shape_size != new_shape.size()) { - throw std::runtime_error("Provided shape size: " + std::to_string(new_shape.size()) + " is different from expected: " + std::to_string(shape_size)); + throw std::runtime_error("Provided shape size: " + std::to_string(new_shape.size()) + + " is different from expected: " + std::to_string(shape_size)); } for (uint32_t dim_idx = 0; dim_idx < shape_size; ++dim_idx) { @@ -67,11 +72,11 @@ void replace_tensor_shape(::flatbuffers::Vector* tensor_shape, const Sh } } -template -std::vector extract_shapes(const std::vector& nodes) +template +std::vector extract_shapes(const std::vector &nodes) { std::vector shapes; - for(const auto& loco_node : nodes) + for (const auto &loco_node : nodes) { shapes.push_back(Shape{}); const auto circle_node = loco::must_cast(loco_node); @@ -86,34 +91,33 @@ std::vector extract_shapes(const std::vector& nodes) } // namespace -CircleResizer::CircleResizer(const std::string& model_path) -: _model_path{model_path} -{ -} +CircleResizer::CircleResizer(const std::string &model_path) : _model_path{model_path} {} -void CircleResizer::resize_model(const std::vector& shapes) +void CircleResizer::resize_model(const std::vector &shapes) { auto model_buffer = read_model(_model_path); auto model = circle::GetMutableModel(model_buffer.data()); if (!model) { - throw std::runtime_error("Incorrect model format"); + throw std::runtime_error("Incorrect model format"); } auto subgraphs = model->mutable_subgraphs(); if (!subgraphs || subgraphs->size() != 1) { - throw std::runtime_error("Many subgraphs are not supported"); + throw std::runtime_error("Many subgraphs are not supported"); } auto subgraph = subgraphs->GetMutableObject(0); const auto inputs_number = subgraph->inputs()->size(); - if(!inputs_number == shapes.size()) + if (!inputs_number == shapes.size()) { - throw std::runtime_error("Expected input shapes: " + std::to_string(inputs_number) + " while provided: " + std::to_string(shapes.size())); + throw std::runtime_error("Expected input shapes: " + std::to_string(inputs_number) + + " while provided: " + std::to_string(shapes.size())); } - for(int in_idx = 0; in_idx < inputs_number; ++in_idx) + for (int in_idx = 0; in_idx < inputs_number; ++in_idx) { const auto in_tensor_idx = subgraph->inputs()->Get(in_idx); - auto input_shape = subgraph->mutable_tensors()->GetMutableObject(in_tensor_idx)->mutable_shape(); + auto input_shape = + subgraph->mutable_tensors()->GetMutableObject(in_tensor_idx)->mutable_shape(); replace_tensor_shape(input_shape, shapes[in_idx]); } @@ -137,7 +141,7 @@ void CircleResizer::resize_model(const std::vector& shapes) phase_runner.run(phase); } -void CircleResizer::save_model(const std::string& output_path) const +void CircleResizer::save_model(const std::string &output_path) const { luci::CircleExporter exporter; luci::CircleFileExpContract contract(_module.get(), output_path); diff --git a/compiler/circle-resizer/src/Shape.cpp b/compiler/circle-resizer/src/Shape.cpp index e3c18b353dd..ad0a639bc0d 100644 --- a/compiler/circle-resizer/src/Shape.cpp +++ b/compiler/circle-resizer/src/Shape.cpp @@ -18,18 +18,16 @@ using namespace circle_resizer; -Dim::Dim(int32_t dim) : _dim_value{dim} { - if (dim < -1) { - throw std::runtime_error("Invalid value of dimension: " + dim); - } +Dim::Dim(int32_t dim) : _dim_value{dim} +{ + if (dim < -1) + { + throw std::runtime_error("Invalid value of dimension: " + dim); + } } bool Dim::is_dynamic() { return _dim_value == -1; } -int32_t Dim::value() const { - return _dim_value; -} +int32_t Dim::value() const { return _dim_value; } -bool Dim::operator==(const Dim& rhs) const { - return value() == rhs.value(); -} +bool Dim::operator==(const Dim &rhs) const { return value() == rhs.value(); } diff --git a/compiler/circle-resizer/src/ShapeParser.cpp b/compiler/circle-resizer/src/ShapeParser.cpp index 5a37353f270..7e917c83793 100644 --- a/compiler/circle-resizer/src/ShapeParser.cpp +++ b/compiler/circle-resizer/src/ShapeParser.cpp @@ -22,55 +22,62 @@ using namespace circle_resizer; -namespace { - bool is_blank(const std::string& s) - { - return !s.empty() && std::find_if(s.begin(), - s.end(), [](unsigned char c) { return !std::isblank(c); }) == s.end(); - } +namespace +{ +bool is_blank(const std::string &s) +{ + return !s.empty() && std::find_if(s.begin(), s.end(), + [](unsigned char c) { return !std::isblank(c); }) == s.end(); +} - Shape parse_shape(std::string shape_str) { - Shape result_shape; - std::stringstream shape_stream(shape_str); - std::string token; - try - { - while (std::getline(shape_stream, token, ',')) - { - result_shape.push_back(Dim{std::stoi(token)}); - } - } - catch (...) - { - throw std::invalid_argument("Error during shape processing: " + shape_str); - } - if(result_shape.empty()) { - throw std::invalid_argument("No shapes found in input string: " + shape_str); - } - return result_shape; +Shape parse_shape(std::string shape_str) +{ + Shape result_shape; + std::stringstream shape_stream(shape_str); + std::string token; + try + { + while (std::getline(shape_stream, token, ',')) + { + result_shape.push_back(Dim{std::stoi(token)}); } + } + catch (...) + { + throw std::invalid_argument("Error during shape processing: " + shape_str); + } + if (result_shape.empty()) + { + throw std::invalid_argument("No shapes found in input string: " + shape_str); + } + return result_shape; +} } // namespace std::vector circle_resizer::parse_shapes(std::string shapes_str) { - std::vector result_shapes; - std::stringstream shapes_stream(shapes_str); - std::string token; - size_t begin_pos = 0, end_pos=0; - while ( (begin_pos = shapes_str.find("[")) != std::string::npos && (end_pos = shapes_str.find("]")) != std::string::npos ) { - token = shapes_str.substr(begin_pos + 1, end_pos); - result_shapes.push_back(parse_shape(token)); - shapes_str.erase(0, end_pos + 1); - } + std::vector result_shapes; + std::stringstream shapes_stream(shapes_str); + std::string token; + size_t begin_pos = 0, end_pos = 0; + while ((begin_pos = shapes_str.find("[")) != std::string::npos && + (end_pos = shapes_str.find("]")) != std::string::npos) + { + token = shapes_str.substr(begin_pos + 1, end_pos); + result_shapes.push_back(parse_shape(token)); + shapes_str.erase(0, end_pos + 1); + } - if(result_shapes.empty()) { - throw std::invalid_argument("No shapes found in input string: " + shapes_str); - } + if (result_shapes.empty()) + { + throw std::invalid_argument("No shapes found in input string: " + shapes_str); + } - // the rest of input not processed by loop above cannot be processed properly - if(shapes_str.size() > 0 && !is_blank(shapes_str)) { - throw std::invalid_argument("The part of input shape: " + shapes_str + " cannot be processed"); - } + // the rest of input not processed by loop above cannot be processed properly + if (shapes_str.size() > 0 && !is_blank(shapes_str)) + { + throw std::invalid_argument("The part of input shape: " + shapes_str + " cannot be processed"); + } - return result_shapes; + return result_shapes; } diff --git a/compiler/circle-resizer/src/main.cpp b/compiler/circle-resizer/src/main.cpp index 816a56c4161..557db6e7c63 100644 --- a/compiler/circle-resizer/src/main.cpp +++ b/compiler/circle-resizer/src/main.cpp @@ -19,7 +19,8 @@ using namespace circle_resizer; -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ CircleResizer resizer(argv[1]); resizer.resize_model({Shape{Dim{1}, Dim{3}}}); // experiment resizer.save_model(argv[2]); diff --git a/compiler/circle-resizer/tests/CircleResizer.test.cpp b/compiler/circle-resizer/tests/CircleResizer.test.cpp index 8b90e751b24..2f9941a4f99 100644 --- a/compiler/circle-resizer/tests/CircleResizer.test.cpp +++ b/compiler/circle-resizer/tests/CircleResizer.test.cpp @@ -24,18 +24,22 @@ using namespace circle_resizer; -namespace { +namespace +{ // TODO: Remove, just for debug -void print_shapes(const std::vector& shapes) { - for (const auto& shape : shapes) { - std::cout << "["; - for (const auto& dim : shape) { - std::cout << dim.value() << ","; - } - std::cout << "],"; +void print_shapes(const std::vector &shapes) +{ + for (const auto &shape : shapes) + { + std::cout << "["; + for (const auto &dim : shape) + { + std::cout << dim.value() << ","; } - std::cout << std::endl; + std::cout << "],"; + } + std::cout << std::endl; } } // namespace @@ -48,15 +52,17 @@ class CircleResizerTest : public ::testing::Test _test_models_dir = std::getenv("ARTIFACTS_PATH"); assert(!_test_models_dir.empty()); } + protected: - std::string _test_models_dir; + std::string _test_models_dir; }; TEST_F(CircleResizerTest, basic_test) { - CircleResizer resizer(_test_models_dir + "/DynInputs_Add_001.circle"); - const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}, Shape{Dim{1}, Dim{5}, Dim{1}}}; - resizer.resize_model(new_input_shapes); - ASSERT_EQ(resizer.input_shapes(), new_input_shapes); - ASSERT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}})); + CircleResizer resizer(_test_models_dir + "/DynInputs_Add_001.circle"); + const auto new_input_shapes = + std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}, Shape{Dim{1}, Dim{5}, Dim{1}}}; + resizer.resize_model(new_input_shapes); + ASSERT_EQ(resizer.input_shapes(), new_input_shapes); + ASSERT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}})); } diff --git a/compiler/circle-resizer/tests/ShapeParser.test.cpp b/compiler/circle-resizer/tests/ShapeParser.test.cpp index 399fefbe0c5..b217cd6250d 100644 --- a/compiler/circle-resizer/tests/ShapeParser.test.cpp +++ b/compiler/circle-resizer/tests/ShapeParser.test.cpp @@ -25,54 +25,53 @@ using namespace circle_resizer; -class ParseShapeTestFixture : public ::testing::TestWithParam>> {}; +class ParseShapeTestFixture + : public ::testing::TestWithParam>> +{ +}; -TEST_P(ParseShapeTestFixture, proper_shape_returned) { - const auto [input_shape_str, expected_shapes] = GetParam(); - std::vector result_shapes; - EXPECT_NO_THROW(result_shapes = parse_shapes(input_shape_str)); - ASSERT_EQ(result_shapes, expected_shapes); +TEST_P(ParseShapeTestFixture, proper_shape_returned) +{ + const auto [input_shape_str, expected_shapes] = GetParam(); + std::vector result_shapes; + EXPECT_NO_THROW(result_shapes = parse_shapes(input_shape_str)); + ASSERT_EQ(result_shapes, expected_shapes); } INSTANTIATE_TEST_SUITE_P( - ParseShapeTest, - ParseShapeTestFixture, - ::testing::Values( - // single shape - std::make_tuple("[3,4]", std::vector{Shape{Dim{3}, Dim{4}}}), - std::make_tuple("[3]", std::vector{Shape{Dim{3}}}), - std::make_tuple("[-1]", std::vector{Shape{Dim{-1}}}), - std::make_tuple("[ 5, 6]", std::vector{Shape{Dim{5}, Dim{6}}}), - std::make_tuple("[3 , 4]", std::vector{Shape{Dim{3}, Dim{4}}}), - std::make_tuple("[-1 , 4]", std::vector{Shape{Dim{-1}, Dim{4}}}), - // many shapes - std::make_tuple("[3,4],[5,6]", std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), - std::make_tuple("[1],[2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), - std::make_tuple(" [3, 4] , [5,6]", std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), - std::make_tuple(" [ 1 ] ,[ 2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), - std::make_tuple(" [ 1 ] ,[ 2] ", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), - std::make_tuple(" [1,2],[3,4,5],[6,7,8,9]", std::vector{Shape{Dim{1}, Dim{2}}, Shape{Dim{3}, Dim{4}, Dim{5}}, Shape{Dim{6}, Dim{7}, Dim{8}, Dim{9}}}) - )); + ParseShapeTest, ParseShapeTestFixture, + ::testing::Values( + // single shape + std::make_tuple("[3,4]", std::vector{Shape{Dim{3}, Dim{4}}}), + std::make_tuple("[3]", std::vector{Shape{Dim{3}}}), + std::make_tuple("[-1]", std::vector{Shape{Dim{-1}}}), + std::make_tuple("[ 5, 6]", std::vector{Shape{Dim{5}, Dim{6}}}), + std::make_tuple("[3 , 4]", std::vector{Shape{Dim{3}, Dim{4}}}), + std::make_tuple("[-1 , 4]", std::vector{Shape{Dim{-1}, Dim{4}}}), + // many shapes + std::make_tuple("[3,4],[5,6]", + std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), + std::make_tuple("[1],[2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [3, 4] , [5,6]", + std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), + std::make_tuple(" [ 1 ] ,[ 2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [ 1 ] ,[ 2] ", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [1,2],[3,4,5],[6,7,8,9]", + std::vector{Shape{Dim{1}, Dim{2}}, Shape{Dim{3}, Dim{4}, Dim{5}}, + Shape{Dim{6}, Dim{7}, Dim{8}, Dim{9}}}))); -class InvalidArgParseShapeTestFixture : public ::testing::TestWithParam {}; +class InvalidArgParseShapeTestFixture : public ::testing::TestWithParam +{ +}; -TEST_P(InvalidArgParseShapeTestFixture, invalid_input) { - EXPECT_THROW(parse_shapes(GetParam()), std::invalid_argument); +TEST_P(InvalidArgParseShapeTestFixture, invalid_input) +{ + EXPECT_THROW(parse_shapes(GetParam()), std::invalid_argument); } -INSTANTIATE_TEST_SUITE_P( - InvalidArgParseShape, - InvalidArgParseShapeTestFixture, - ::testing::Values( - std::string{""}, - std::string{"]"}, - std::string{"1a,2"}, - std::string{"[-2]"}, - std::string{"[-2,1,3]"}, - std::string{"-1"}, - std::string{"7,7"}, - std::string{"8"}, - std::string{"[8],9"}, - std::string{"1,2"}, - std::string{"[1],[2],"} - )); +INSTANTIATE_TEST_SUITE_P(InvalidArgParseShape, InvalidArgParseShapeTestFixture, + ::testing::Values(std::string{""}, std::string{"]"}, std::string{"1a,2"}, + std::string{"[-2]"}, std::string{"[-2,1,3]"}, + std::string{"-1"}, std::string{"7,7"}, std::string{"8"}, + std::string{"[8],9"}, std::string{"1,2"}, + std::string{"[1],[2],"})); diff --git a/compiler/luci/service/src/Nodes/CircleOutput.cpp b/compiler/luci/service/src/Nodes/CircleOutput.cpp index 995f116b034..efcf8fb6e12 100644 --- a/compiler/luci/service/src/Nodes/CircleOutput.cpp +++ b/compiler/luci/service/src/Nodes/CircleOutput.cpp @@ -24,16 +24,7 @@ namespace luci namespace sinf { -namespace { -void print_shape(const loco::TensorShape &shape) -{ - for(int i=0;i Date: Mon, 24 Feb 2025 10:12:42 +0100 Subject: [PATCH 04/39] working on tests --- .../tests/CircleResizer.test.cpp | 45 ++++++++++++++++--- .../circle-resizer/tests/ShapeParser.test.cpp | 2 +- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/compiler/circle-resizer/tests/CircleResizer.test.cpp b/compiler/circle-resizer/tests/CircleResizer.test.cpp index 2f9941a4f99..5f0661504c7 100644 --- a/compiler/circle-resizer/tests/CircleResizer.test.cpp +++ b/compiler/circle-resizer/tests/CircleResizer.test.cpp @@ -57,12 +57,47 @@ class CircleResizerTest : public ::testing::Test std::string _test_models_dir; }; -TEST_F(CircleResizerTest, basic_test) + +TEST_F(CircleResizerTest, single_input_single_output) +{ + CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); + const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; + resizer.resize_model(new_input_shapes); + EXPECT_EQ(resizer.input_shapes(), new_input_shapes); + EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); +} + +TEST_F(CircleResizerTest, single_input_two_outputs) { - CircleResizer resizer(_test_models_dir + "/DynInputs_Add_001.circle"); + CircleResizer resizer(_test_models_dir + "/CSE_Quantize_000.circle"); + const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; + resizer.resize_model(new_input_shapes); + EXPECT_EQ(resizer.input_shapes(), new_input_shapes); + EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); +} + +TEST_F(CircleResizerTest, two_inputs_single_output) +{ + CircleResizer resizer(_test_models_dir + "/Add_000.circle"); const auto new_input_shapes = - std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}, Shape{Dim{1}, Dim{5}, Dim{1}}}; + std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; resizer.resize_model(new_input_shapes); - ASSERT_EQ(resizer.input_shapes(), new_input_shapes); - ASSERT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{1}}})); + EXPECT_EQ(resizer.input_shapes(), new_input_shapes); + EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); } + +TEST_F(CircleResizerTest, two_inputs_two_outputs) +{ + CircleResizer resizer(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); + const auto new_input_shapes = + std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; + resizer.resize_model(new_input_shapes); + EXPECT_EQ(resizer.input_shapes(), new_input_shapes); + EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); +} + +// TEST_F(CircleResizerTest, neg_model_file_not_exist) +// { +// CircleResizer resizer(_test_models_dir + "/not_existed.circle"); +// EXPECT_THROW() +// } diff --git a/compiler/circle-resizer/tests/ShapeParser.test.cpp b/compiler/circle-resizer/tests/ShapeParser.test.cpp index b217cd6250d..1595acad486 100644 --- a/compiler/circle-resizer/tests/ShapeParser.test.cpp +++ b/compiler/circle-resizer/tests/ShapeParser.test.cpp @@ -64,7 +64,7 @@ class InvalidArgParseShapeTestFixture : public ::testing::TestWithParam Date: Mon, 24 Feb 2025 16:38:14 +0100 Subject: [PATCH 05/39] introduced model data --- compiler/circle-resizer/CMakeLists.txt | 2 + .../circle-resizer/include/CircleResizer.h | 20 ++-- compiler/circle-resizer/include/ModelData.h | 52 +++++++++ compiler/circle-resizer/src/CircleResizer.cpp | 44 ++++--- compiler/circle-resizer/src/ModelData.cpp | 52 +++++++++ .../tests/CircleResizer.test.cpp | 107 +++++++++++++++--- .../luci/service/src/Nodes/CircleOutput.cpp | 1 - 7 files changed, 227 insertions(+), 51 deletions(-) create mode 100644 compiler/circle-resizer/include/ModelData.h create mode 100644 compiler/circle-resizer/src/ModelData.cpp diff --git a/compiler/circle-resizer/CMakeLists.txt b/compiler/circle-resizer/CMakeLists.txt index 9e1de16e591..7cecf4c69e3 100644 --- a/compiler/circle-resizer/CMakeLists.txt +++ b/compiler/circle-resizer/CMakeLists.txt @@ -1,5 +1,6 @@ list(APPEND CIRCLE_RESIZER_SOURCES src/Shape.cpp) list(APPEND CIRCLE_RESIZER_SOURCES src/ShapeParser.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES src/ModelData.cpp) list(APPEND CIRCLE_RESIZER_SOURCES src/CircleResizer.cpp) add_library(circle_resizer_core STATIC "${CIRCLE_RESIZER_SOURCES}") @@ -34,6 +35,7 @@ list(APPEND CIRCLE_RESIZER_TEST_SOURCES tests/CircleResizer.test.cpp) nnas_find_package(GTest REQUIRED) GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES}) target_link_libraries(circle_resizer_unit_test circle_resizer_core) +target_link_libraries(circle_resizer_unit_test oops) get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) set_tests_properties(circle_resizer_unit_test diff --git a/compiler/circle-resizer/include/CircleResizer.h b/compiler/circle-resizer/include/CircleResizer.h index 8baa62b7d19..3f8e916e6de 100644 --- a/compiler/circle-resizer/include/CircleResizer.h +++ b/compiler/circle-resizer/include/CircleResizer.h @@ -18,36 +18,30 @@ #define __CIRCLE_RESIZER_H__ #include "Shape.h" +#include "ModelData.h" #include -#include #include -namespace luci -{ -class Module; -} - namespace circle_resizer { class CircleResizer { public: + explicit CircleResizer(const std::vector &model_buffer); explicit CircleResizer(const std::string &model_path); - // to satisfy forward declaration + unique_ptr - ~CircleResizer(); public: void resize_model(const std::vector &shapes); - void save_model(const std::string &output_path) const; + void save_model(const std::string &output_path); public: - std::vector input_shapes() const; - std::vector output_shapes() const; + // cannot be const because of loco::Graph limitations + std::vector input_shapes(); + std::vector output_shapes(); private: - std::string _model_path; - std::unique_ptr _module; + ModelData _model_data; }; } // namespace circle_resizer diff --git a/compiler/circle-resizer/include/ModelData.h b/compiler/circle-resizer/include/ModelData.h new file mode 100644 index 00000000000..fd920b8083a --- /dev/null +++ b/compiler/circle-resizer/include/ModelData.h @@ -0,0 +1,52 @@ +/* + * 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_H__ +#define __CIRCLE_RESIZER_MODEL_H__ + +#include +#include +#include + +namespace luci +{ +class Module; +} + +namespace circle_resizer +{ +// DESIGN NOTE: The purpose of the class is to keep buffer and module synchronized +class ModelData +{ + +public: + explicit ModelData(const std::vector &buffer); + void invalidate(const std::vector &buffer); + // to satisfy forward declaration + unique_ptr + ~ModelData(); + +public: + std::vector &buffer(); + luci::Module *module(); + +private: + std::vector _buffer; + std::unique_ptr _module; +}; + +} // namespace circle_resizer + +#endif // __CIRCLE_RESIZER_H__ diff --git a/compiler/circle-resizer/src/CircleResizer.cpp b/compiler/circle-resizer/src/CircleResizer.cpp index c6b1d6b5119..3d5feec972a 100644 --- a/compiler/circle-resizer/src/CircleResizer.cpp +++ b/compiler/circle-resizer/src/CircleResizer.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -63,7 +62,7 @@ void replace_tensor_shape(::flatbuffers::Vector *tensor_shape, const Sh const auto shape_size = tensor_shape->size(); if (shape_size != new_shape.size()) { - throw std::runtime_error("Provided shape size: " + std::to_string(new_shape.size()) + + throw std::runtime_error("Provided shape rank: " + std::to_string(new_shape.size()) + " is different from expected: " + std::to_string(shape_size)); } for (uint32_t dim_idx = 0; dim_idx < shape_size; ++dim_idx) @@ -91,12 +90,18 @@ std::vector extract_shapes(const std::vector &nodes) } // namespace -CircleResizer::CircleResizer(const std::string &model_path) : _model_path{model_path} {} +CircleResizer::CircleResizer(const std::vector &model_buffer) + : _model_data{ModelData(model_buffer)} +{ +} + +CircleResizer::CircleResizer(const std::string &model_path) : CircleResizer(read_model(model_path)) +{ +} void CircleResizer::resize_model(const std::vector &shapes) { - auto model_buffer = read_model(_model_path); - auto model = circle::GetMutableModel(model_buffer.data()); + auto model = circle::GetMutableModel(_model_data.buffer().data()); if (!model) { throw std::runtime_error("Incorrect model format"); @@ -108,7 +113,7 @@ void CircleResizer::resize_model(const std::vector &shapes) } auto subgraph = subgraphs->GetMutableObject(0); const auto inputs_number = subgraph->inputs()->size(); - if (!inputs_number == shapes.size()) + if (inputs_number != shapes.size()) { throw std::runtime_error("Expected input shapes: " + std::to_string(inputs_number) + " while provided: " + std::to_string(shapes.size())); @@ -121,30 +126,23 @@ void CircleResizer::resize_model(const std::vector &shapes) replace_tensor_shape(input_shape, shapes[in_idx]); } - flatbuffers::Verifier verifier{model_buffer.data(), model_buffer.size()}; - if (!circle::VerifyModelBuffer(verifier)) - { - throw std::runtime_error("Verification of the model failed"); - } - - const luci::GraphBuilderSource *source_ptr = &luci::GraphBuilderRegistry::get(); - luci::Importer importer(source_ptr); - _module = importer.importModule(model_buffer.data(), model_buffer.size()); + // invalidate after changing input shape + _model_data.invalidate(_model_data.buffer()); logo::Phase phase; phase.emplace_back(std::make_unique()); phase.emplace_back(std::make_unique()); phase.emplace_back(std::make_unique()); - auto graph = _module->graph(); + auto graph = _model_data.module()->graph(); logo::PhaseRunner phase_runner{graph}; phase_runner.run(phase); } -void CircleResizer::save_model(const std::string &output_path) const +void CircleResizer::save_model(const std::string &output_path) { luci::CircleExporter exporter; - luci::CircleFileExpContract contract(_module.get(), output_path); + luci::CircleFileExpContract contract(_model_data.module(), output_path); if (!exporter.invoke(&contract)) { @@ -152,14 +150,12 @@ void CircleResizer::save_model(const std::string &output_path) const } } -std::vector CircleResizer::input_shapes() const +std::vector CircleResizer::input_shapes() { - return extract_shapes(loco::input_nodes(_module->graph())); + return extract_shapes(loco::input_nodes(_model_data.module()->graph())); } -std::vector CircleResizer::output_shapes() const +std::vector CircleResizer::output_shapes() { - return extract_shapes(loco::output_nodes(_module->graph())); + return extract_shapes(loco::output_nodes(_model_data.module()->graph())); } - -CircleResizer::~CircleResizer() = default; diff --git a/compiler/circle-resizer/src/ModelData.cpp b/compiler/circle-resizer/src/ModelData.cpp new file mode 100644 index 00000000000..6b26e5db6da --- /dev/null +++ b/compiler/circle-resizer/src/ModelData.cpp @@ -0,0 +1,52 @@ +/* + * 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 "ModelData.h" + +#include +#include + +using namespace circle_resizer; + +namespace +{ +std::unique_ptr load_module(const std::vector &model_buffer) +{ + flatbuffers::Verifier verifier{model_buffer.data(), model_buffer.size()}; + if (!circle::VerifyModelBuffer(verifier)) + { + throw std::runtime_error("Verification of the model failed"); + } + + const luci::GraphBuilderSource *source_ptr = &luci::GraphBuilderRegistry::get(); + luci::Importer importer(source_ptr); + return importer.importModule(model_buffer.data(), model_buffer.size()); +} +} // namespace + +ModelData::ModelData(const std::vector &buffer) { invalidate(buffer); } + +void ModelData::invalidate(const std::vector &buffer) +{ + _buffer = buffer; + _module = load_module(buffer); +} + +std::vector &ModelData::buffer() { return _buffer; } + +luci::Module *ModelData::module() { return _module.get(); } + +ModelData::~ModelData() = default; diff --git a/compiler/circle-resizer/tests/CircleResizer.test.cpp b/compiler/circle-resizer/tests/CircleResizer.test.cpp index 5f0661504c7..99771e759a3 100644 --- a/compiler/circle-resizer/tests/CircleResizer.test.cpp +++ b/compiler/circle-resizer/tests/CircleResizer.test.cpp @@ -15,14 +15,18 @@ */ #include "CircleResizer.h" +#include "oops/UserExn.h" +#include #include + #include #include #include #include using namespace circle_resizer; +using ::testing::HasSubstr; namespace { @@ -49,7 +53,8 @@ class CircleResizerTest : public ::testing::Test protected: virtual void SetUp() { - _test_models_dir = std::getenv("ARTIFACTS_PATH"); + // _test_models_dir = std::getenv("ARTIFACTS_PATH"); + _test_models_dir = "/home/m.bencer/workspace/ONE_fork/build/compiler/common-artifacts"; assert(!_test_models_dir.empty()); } @@ -57,7 +62,6 @@ class CircleResizerTest : public ::testing::Test std::string _test_models_dir; }; - TEST_F(CircleResizerTest, single_input_single_output) { CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); @@ -73,14 +77,15 @@ TEST_F(CircleResizerTest, single_input_two_outputs) const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; resizer.resize_model(new_input_shapes); EXPECT_EQ(resizer.input_shapes(), new_input_shapes); - EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); + EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, + Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); } TEST_F(CircleResizerTest, two_inputs_single_output) { CircleResizer resizer(_test_models_dir + "/Add_000.circle"); - const auto new_input_shapes = - std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; + const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, + Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; resizer.resize_model(new_input_shapes); EXPECT_EQ(resizer.input_shapes(), new_input_shapes); EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); @@ -89,15 +94,91 @@ TEST_F(CircleResizerTest, two_inputs_single_output) TEST_F(CircleResizerTest, two_inputs_two_outputs) { CircleResizer resizer(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); - const auto new_input_shapes = - std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; + const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, + Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; resizer.resize_model(new_input_shapes); EXPECT_EQ(resizer.input_shapes(), new_input_shapes); - EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); + EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, + Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); +} + +TEST_F(CircleResizerTest, neg_model_file_not_exist) +{ + auto file_name = "/not_existed.circle"; + try + { + CircleResizer(_test_models_dir + file_name); + FAIL() << "Expected std::runtime_error"; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Failed to open file")); + EXPECT_THAT(err.what(), HasSubstr(file_name)); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(CircleResizerTest, neg_invalid_model) +{ + try + { + CircleResizer(std::vector{1, 2, 3, 4, 5}); + FAIL() << "Expected std::runtime_error"; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Verification of the model failed")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(CircleResizerTest, neg_not_all_input_shapes_provided) +{ + CircleResizer resizer(_test_models_dir + "/Add_000.circle"); + try + { + resizer.resize_model(std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}); + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Expected input shapes: 2 while provided: 1")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } } -// TEST_F(CircleResizerTest, neg_model_file_not_exist) -// { -// CircleResizer resizer(_test_models_dir + "/not_existed.circle"); -// EXPECT_THROW() -// } +TEST_F(CircleResizerTest, neg_incorrect_rank_of_new_shape) +{ + CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); + try + { + resizer.resize_model(std::vector{Shape{Dim{3}}}); + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Provided shape rank: 1 is different from expected: 2")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +// /home/m.bencer/workspace/ONE_fork/res/TensorFlowLiteRecipes/DepthwiseConv2D_000/test.recipe +TEST_F(CircleResizerTest, neg_shape_inference_failed) +{ + CircleResizer resizer(_test_models_dir + "/DepthwiseConv2D_000.circle"); + // dim: 1 dim: 64 dim: 64 dim: 8 + // dim: 1 dim: 3 dim: 3 dim: 8 + EXPECT_THROW(resizer.resize_model(std::vector{Shape{Dim{1}, Dim{64}, Dim{64}, Dim{8}}, + Shape{Dim{1}, Dim{2}, Dim{2}, Dim{3}}}), + oops::UserExn); +} diff --git a/compiler/luci/service/src/Nodes/CircleOutput.cpp b/compiler/luci/service/src/Nodes/CircleOutput.cpp index efcf8fb6e12..1f83f161cd6 100644 --- a/compiler/luci/service/src/Nodes/CircleOutput.cpp +++ b/compiler/luci/service/src/Nodes/CircleOutput.cpp @@ -25,7 +25,6 @@ namespace luci namespace sinf { - loco::TensorShape Algorithm::visit(const luci::CircleOutput *node) { const auto from_shape = loco::must_cast(node->from()); From db6f4206256e3de9b399eef22f1fb717eb114db9 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Tue, 25 Feb 2025 17:40:39 +0100 Subject: [PATCH 06/39] refactor --- .../circle-resizer/include/CircleResizer.h | 1 + compiler/circle-resizer/include/ModelData.h | 4 +- compiler/circle-resizer/src/CircleResizer.cpp | 26 ++++--- compiler/circle-resizer/src/ModelData.cpp | 65 +++++++++++++++-- .../tests/CircleResizer.test.cpp | 72 ++++++++++++------- 5 files changed, 124 insertions(+), 44 deletions(-) diff --git a/compiler/circle-resizer/include/CircleResizer.h b/compiler/circle-resizer/include/CircleResizer.h index 3f8e916e6de..aedf0e335ea 100644 --- a/compiler/circle-resizer/include/CircleResizer.h +++ b/compiler/circle-resizer/include/CircleResizer.h @@ -33,6 +33,7 @@ class CircleResizer public: void resize_model(const std::vector &shapes); + void save_model(std::ostream &stream); void save_model(const std::string &output_path); public: diff --git a/compiler/circle-resizer/include/ModelData.h b/compiler/circle-resizer/include/ModelData.h index fd920b8083a..579f9f8a3c8 100644 --- a/compiler/circle-resizer/include/ModelData.h +++ b/compiler/circle-resizer/include/ModelData.h @@ -34,7 +34,8 @@ class ModelData public: explicit ModelData(const std::vector &buffer); - void invalidate(const std::vector &buffer); + void invalidate_module(); + void invalidate_buffer(); // to satisfy forward declaration + unique_ptr ~ModelData(); @@ -43,6 +44,7 @@ class ModelData luci::Module *module(); private: + bool _module_invalidated = false, _buffer_invalidated = false; std::vector _buffer; std::unique_ptr _module; }; diff --git a/compiler/circle-resizer/src/CircleResizer.cpp b/compiler/circle-resizer/src/CircleResizer.cpp index 3d5feec972a..e8bb8e7c521 100644 --- a/compiler/circle-resizer/src/CircleResizer.cpp +++ b/compiler/circle-resizer/src/CircleResizer.cpp @@ -19,15 +19,11 @@ #include #include -#include -#include #include #include #include #include -#include - #include #include #include @@ -127,7 +123,7 @@ void CircleResizer::resize_model(const std::vector &shapes) } // invalidate after changing input shape - _model_data.invalidate(_model_data.buffer()); + _model_data.invalidate_module(); logo::Phase phase; phase.emplace_back(std::make_unique()); @@ -137,19 +133,27 @@ void CircleResizer::resize_model(const std::vector &shapes) auto graph = _model_data.module()->graph(); logo::PhaseRunner phase_runner{graph}; phase_runner.run(phase); + + // invalidate after shape inference + _model_data.invalidate_buffer(); } -void CircleResizer::save_model(const std::string &output_path) +void CircleResizer::save_model(std::ostream &stream) { - luci::CircleExporter exporter; - luci::CircleFileExpContract contract(_model_data.module(), output_path); - - if (!exporter.invoke(&contract)) + stream.write(reinterpret_cast(_model_data.buffer().data()), + _model_data.buffer().size()); + if (!stream.good()) { - throw std::runtime_error("Saving model failed"); + throw std::runtime_error("Failed to write to output stream"); } } +void CircleResizer::save_model(const std::string &output_path) +{ + std::ofstream out_stream(output_path, std::ios::out | std::ios::binary); + save_model(out_stream); +} + std::vector CircleResizer::input_shapes() { return extract_shapes(loco::input_nodes(_model_data.module()->graph())); diff --git a/compiler/circle-resizer/src/ModelData.cpp b/compiler/circle-resizer/src/ModelData.cpp index 6b26e5db6da..e03b122c7e1 100644 --- a/compiler/circle-resizer/src/ModelData.cpp +++ b/compiler/circle-resizer/src/ModelData.cpp @@ -17,7 +17,10 @@ #include "ModelData.h" #include + #include +#include +#include using namespace circle_resizer; @@ -35,18 +38,66 @@ std::unique_ptr load_module(const std::vector &model_buff luci::Importer importer(source_ptr); return importer.importModule(model_buffer.data(), model_buffer.size()); } -} // namespace +class BufferModelContract : public luci::CircleExporter::Contract +{ +public: + BufferModelContract(luci::Module *module) + : _module(module), _buffer{std::make_unique>()} + { + } + + luci::Module *module() const override { return _module; } -ModelData::ModelData(const std::vector &buffer) { invalidate(buffer); } + bool store(const char *ptr, const size_t size) const override + { + _buffer->resize(size); + std::copy(ptr, ptr + size, _buffer->begin()); + return true; + } + + std::vector get_buffer() { return *_buffer; } + +private: + luci::Module *_module; + std::unique_ptr> _buffer; +}; + +} // namespace -void ModelData::invalidate(const std::vector &buffer) +ModelData::ModelData(const std::vector &buffer) + : _buffer{buffer}, _module{load_module(buffer)} { - _buffer = buffer; - _module = load_module(buffer); } -std::vector &ModelData::buffer() { return _buffer; } +void ModelData::invalidate_module() { _module_invalidated = true; } -luci::Module *ModelData::module() { return _module.get(); } +void ModelData::invalidate_buffer() { _buffer_invalidated = true; } + +std::vector &ModelData::buffer() +{ + if (_buffer_invalidated) + { + luci::CircleExporter exporter; + BufferModelContract contract(module()); + + if (!exporter.invoke(&contract)) + { + throw std::runtime_error("Exporting buffer from the model failed"); + } + _buffer = contract.get_buffer(); + _buffer_invalidated = false; + } + return _buffer; +} + +luci::Module *ModelData::module() +{ + if (_module_invalidated) + { + _module = load_module(_buffer); + _module_invalidated = false; + } + return _module.get(); +} ModelData::~ModelData() = default; diff --git a/compiler/circle-resizer/tests/CircleResizer.test.cpp b/compiler/circle-resizer/tests/CircleResizer.test.cpp index 99771e759a3..f28749dc327 100644 --- a/compiler/circle-resizer/tests/CircleResizer.test.cpp +++ b/compiler/circle-resizer/tests/CircleResizer.test.cpp @@ -28,33 +28,12 @@ using namespace circle_resizer; using ::testing::HasSubstr; -namespace -{ - -// TODO: Remove, just for debug -void print_shapes(const std::vector &shapes) -{ - for (const auto &shape : shapes) - { - std::cout << "["; - for (const auto &dim : shape) - { - std::cout << dim.value() << ","; - } - std::cout << "],"; - } - std::cout << std::endl; -} - -} // namespace - class CircleResizerTest : public ::testing::Test { protected: virtual void SetUp() { - // _test_models_dir = std::getenv("ARTIFACTS_PATH"); - _test_models_dir = "/home/m.bencer/workspace/ONE_fork/build/compiler/common-artifacts"; + _test_models_dir = std::getenv("ARTIFACTS_PATH"); assert(!_test_models_dir.empty()); } @@ -172,13 +151,56 @@ TEST_F(CircleResizerTest, neg_incorrect_rank_of_new_shape) } } -// /home/m.bencer/workspace/ONE_fork/res/TensorFlowLiteRecipes/DepthwiseConv2D_000/test.recipe TEST_F(CircleResizerTest, neg_shape_inference_failed) { CircleResizer resizer(_test_models_dir + "/DepthwiseConv2D_000.circle"); - // dim: 1 dim: 64 dim: 64 dim: 8 - // dim: 1 dim: 3 dim: 3 dim: 8 EXPECT_THROW(resizer.resize_model(std::vector{Shape{Dim{1}, Dim{64}, Dim{64}, Dim{8}}, Shape{Dim{1}, Dim{2}, Dim{2}, Dim{3}}}), oops::UserExn); } + +TEST_F(CircleResizerTest, save_model_without_change) +{ + CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); + std::stringstream out_stream; + resizer.save_model(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)); + CircleResizer resizer2(model_buffer); + EXPECT_EQ(resizer2.input_shapes(), (std::vector{Shape{Dim{3}, Dim{3}}})); + EXPECT_EQ(resizer2.output_shapes(), (std::vector{Shape{Dim{3}, Dim{1}, Dim{3}}})); +} + +TEST_F(CircleResizerTest, save_model_after_resizing) +{ + CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); + std::stringstream out_stream; + const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; + resizer.resize_model(new_input_shapes); + resizer.save_model(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)); + CircleResizer resizer2(model_buffer); + EXPECT_EQ(resizer2.input_shapes(), new_input_shapes); + EXPECT_EQ(resizer2.output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); +} + +TEST_F(CircleResizerTest, neg_incorrect_output_stream) +{ + CircleResizer resizer(_test_models_dir + "/Add_000.circle"); + std::ofstream out_stream; + try + { + resizer.save_model(out_stream); + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Failed to write to output stream")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} From 195dad40b6a1535d3a80568ea73a827b65d063e2 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 26 Feb 2025 09:05:14 +0100 Subject: [PATCH 07/39] initial driver implementation --- compiler/circle-resizer/CMakeLists.txt | 4 +- compiler/circle-resizer/requires.cmake | 2 + compiler/circle-resizer/src/Driver.cpp | 111 +++++++++++++++++++++++++ compiler/circle-resizer/src/main.cpp | 28 ------- 4 files changed, 116 insertions(+), 29 deletions(-) create mode 100644 compiler/circle-resizer/src/Driver.cpp delete mode 100644 compiler/circle-resizer/src/main.cpp diff --git a/compiler/circle-resizer/CMakeLists.txt b/compiler/circle-resizer/CMakeLists.txt index 7cecf4c69e3..45c00717ca8 100644 --- a/compiler/circle-resizer/CMakeLists.txt +++ b/compiler/circle-resizer/CMakeLists.txt @@ -16,8 +16,10 @@ target_link_libraries(circle_resizer_core PRIVATE luci_import) target_link_libraries(circle_resizer_core PRIVATE logo) target_link_libraries(circle_resizer_core PRIVATE luci_log) -add_executable(circle_resizer src/main.cpp) +add_executable(circle_resizer src/Driver.cpp) target_link_libraries(circle_resizer PRIVATE circle_resizer_core) +target_link_libraries(circle_resizer PRIVATE arser) +target_link_libraries(circle_resizer PRIVATE safemain) set_target_properties(circle_resizer PROPERTIES OUTPUT_NAME "circle-resizer" diff --git a/compiler/circle-resizer/requires.cmake b/compiler/circle-resizer/requires.cmake index 624621b1872..a07a9fe361d 100644 --- a/compiler/circle-resizer/requires.cmake +++ b/compiler/circle-resizer/requires.cmake @@ -1,3 +1,5 @@ +require("arser") +require("safemain") require("mio-circle08") require("logo-core") require("luci") diff --git a/compiler/circle-resizer/src/Driver.cpp b/compiler/circle-resizer/src/Driver.cpp new file mode 100644 index 00000000000..a01356467b9 --- /dev/null +++ b/compiler/circle-resizer/src/Driver.cpp @@ -0,0 +1,111 @@ + +/* + * 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 "CircleResizer.h" +#include "ShapeParser.h" + +#include +#include +#include +#include + +using namespace circle_resizer; + +namespace +{ +std::string to_string(const Shape &shape) +{ + std::stringstream stream; + stream << "["; + for (size_t pos = 0; pos < shape.size(); ++pos) + { + stream << shape[pos].value(); + if (pos < shape.size() - 1) + { + stream << ","; + } + } + stream << "]"; + return stream.str(); +} +} // namespace + +int entry(const int argc, char **argv) +{ + arser::Arser arser("circle-resizer"); + + arser.add_argument("--input_model") + .nargs(1) + .type(arser::DataType::STR) + .required(true) + .help("Input model filepath (.circle)"); + + arser.add_argument("--output_model") + .nargs(1) + .type(arser::DataType::STR) + .required(true) + .help("Resized output model filepath (.circle)"); + + arser.add_argument("--input_shapes") + .nargs(1) + .type(arser::DataType::STR) + .required(true) + .help("New inputs shapes in in comma separated format. An example for 2 inputs: [1,2],[3,4]."); + + try + { + arser.parse(argc, argv); + + const auto input_path = arser.get("--input_model"); + + CircleResizer resizer(input_path); + const auto input_shapes = resizer.input_shapes(); + std::cout << "Input shapes before resizing:" << std::endl; + for (size_t in_idx = 0; in_idx < input_shapes.size(); ++in_idx) + { + std::cout << in_idx + 1 << ". " << to_string(input_shapes[in_idx]) << std::endl; + } + + auto output_shapes = resizer.output_shapes(); + std::cout << "Output shapes before resizing:" << std::endl; + for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) + { + std::cout << out_idx + 1 << ". " << to_string(output_shapes[out_idx]) << std::endl; + } + + const auto output_path = arser.get("--output_model"); + const auto new_input_shapes_str = arser.get("--input_shapes"); + + resizer.resize_model(parse_shapes(new_input_shapes_str)); + + output_shapes = resizer.output_shapes(); + std::cout << "Output shapes after resizing:" << std::endl; + for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) + { + std::cout << out_idx + 1 << ". " << to_string(output_shapes[out_idx]) << std::endl; + } + + resizer.save_model(output_path); + std::cout << "Resizing complete, the model saved to: " << output_path << std::endl; + } + catch (const std::runtime_error &err) + { + std::cout << err.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/compiler/circle-resizer/src/main.cpp b/compiler/circle-resizer/src/main.cpp deleted file mode 100644 index 557db6e7c63..00000000000 --- a/compiler/circle-resizer/src/main.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 "CircleResizer.h" -#include "Shape.h" - -using namespace circle_resizer; - -int main(int argc, char *argv[]) -{ - CircleResizer resizer(argv[1]); - resizer.resize_model({Shape{Dim{1}, Dim{3}}}); // experiment - resizer.save_model(argv[2]); - return 0; -} From e83a9552e37894926ac2d55ba18e1b884c76ed66 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Mon, 3 Mar 2025 11:43:51 +0100 Subject: [PATCH 08/39] initial version of python api --- compiler/circle-resizer/CMakeLists.txt | 49 ++----------------- compiler/circle-resizer/app/CMakeLists.txt | 10 ++++ .../circle-resizer/{src => app}/Driver.cpp | 0 compiler/circle-resizer/python/CMakeLists.txt | 34 +++++++++++++ .../circle-resizer/python/include/Shape.h | 24 +++++++++ compiler/circle-resizer/python/src/Shape.cpp | 3 ++ compiler/circle-resizer/src/CMakeLists.txt | 17 +++++++ compiler/circle-resizer/tests/CMakeLists.txt | 16 ++++++ infra/nnfw/CMakeLists.txt | 4 +- 9 files changed, 110 insertions(+), 47 deletions(-) create mode 100644 compiler/circle-resizer/app/CMakeLists.txt rename compiler/circle-resizer/{src => app}/Driver.cpp (100%) create mode 100644 compiler/circle-resizer/python/CMakeLists.txt create mode 100644 compiler/circle-resizer/python/include/Shape.h create mode 100644 compiler/circle-resizer/python/src/Shape.cpp create mode 100644 compiler/circle-resizer/src/CMakeLists.txt create mode 100644 compiler/circle-resizer/tests/CMakeLists.txt diff --git a/compiler/circle-resizer/CMakeLists.txt b/compiler/circle-resizer/CMakeLists.txt index 45c00717ca8..4e6b23a2c6d 100644 --- a/compiler/circle-resizer/CMakeLists.txt +++ b/compiler/circle-resizer/CMakeLists.txt @@ -1,45 +1,4 @@ -list(APPEND CIRCLE_RESIZER_SOURCES src/Shape.cpp) -list(APPEND CIRCLE_RESIZER_SOURCES src/ShapeParser.cpp) -list(APPEND CIRCLE_RESIZER_SOURCES src/ModelData.cpp) -list(APPEND CIRCLE_RESIZER_SOURCES src/CircleResizer.cpp) - -add_library(circle_resizer_core STATIC "${CIRCLE_RESIZER_SOURCES}") - -target_include_directories(circle_resizer_core PUBLIC include) - -target_link_libraries(circle_resizer_core PRIVATE mio_circle08) -target_link_libraries(circle_resizer_core PRIVATE logo_core) -target_link_libraries(circle_resizer_core PRIVATE luci_pass) -target_link_libraries(circle_resizer_core PRIVATE luci_lang) -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 logo) -target_link_libraries(circle_resizer_core PRIVATE luci_log) - -add_executable(circle_resizer src/Driver.cpp) -target_link_libraries(circle_resizer PRIVATE circle_resizer_core) -target_link_libraries(circle_resizer PRIVATE arser) -target_link_libraries(circle_resizer PRIVATE safemain) - -set_target_properties(circle_resizer PROPERTIES - OUTPUT_NAME "circle-resizer" -) - -install(TARGETS circle_resizer DESTINATION bin) - -if(NOT ENABLE_TEST) - return() -endif(NOT ENABLE_TEST) - -list(APPEND CIRCLE_RESIZER_TEST_SOURCES tests/ShapeParser.test.cpp) -list(APPEND CIRCLE_RESIZER_TEST_SOURCES tests/CircleResizer.test.cpp) - -nnas_find_package(GTest REQUIRED) -GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES}) -target_link_libraries(circle_resizer_unit_test circle_resizer_core) -target_link_libraries(circle_resizer_unit_test oops) - -get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) -set_tests_properties(circle_resizer_unit_test - PROPERTIES - ENVIRONMENT "ARTIFACTS_PATH=${ARTIFACTS_PATH}") +add_subdirectory(src) +add_subdirectory(app) +add_subdirectory(python) +add_subdirectory(tests) diff --git a/compiler/circle-resizer/app/CMakeLists.txt b/compiler/circle-resizer/app/CMakeLists.txt new file mode 100644 index 00000000000..15943439228 --- /dev/null +++ b/compiler/circle-resizer/app/CMakeLists.txt @@ -0,0 +1,10 @@ +add_executable(circle_resizer Driver.cpp) +target_link_libraries(circle_resizer PRIVATE circle_resizer_core) +target_link_libraries(circle_resizer PRIVATE arser) +target_link_libraries(circle_resizer PRIVATE safemain) + +set_target_properties(circle_resizer PROPERTIES + OUTPUT_NAME "circle-resizer" +) + +install(TARGETS circle_resizer DESTINATION bin) diff --git a/compiler/circle-resizer/src/Driver.cpp b/compiler/circle-resizer/app/Driver.cpp similarity index 100% rename from compiler/circle-resizer/src/Driver.cpp rename to compiler/circle-resizer/app/Driver.cpp diff --git a/compiler/circle-resizer/python/CMakeLists.txt b/compiler/circle-resizer/python/CMakeLists.txt new file mode 100644 index 00000000000..0247c5118ae --- /dev/null +++ b/compiler/circle-resizer/python/CMakeLists.txt @@ -0,0 +1,34 @@ +# FindPythonLibs is deprecated since 3.12, and recommand to use FindPython. +# But on cross compile, FindPython is not working for target environment +# For workaround, use PythonLibs +# refer to https://github.com/Samsung/ONE/issues/11368 for more details +find_package(PythonLibs REQUIRED) +set(PYTHON_MODULE_PREFIX "lib" CACHE INTERNAL "Cross python lib prefix") +set(PYTHON_MODULE_EXTENSION ".so" CACHE INTERNAL "Cross python lib extension") + +# Disable pybind11 python search mechanism +set(PYTHONLIBS_FOUND TRUE CACHE INTERNAL "") + +nnas_find_package(Pybind11) +if(NOT Pybind11_FOUND) + message(STATUS "circle-resizer: FAILED (Pybind11 is missing)") + return() +endif() + +list(APPEND CIRCLE_RESIZER_PYTHON_API src/Shape.cpp) + +add_library(circle_resizer_python_api MODULE ${CIRCLE_RESIZER_PYTHON_API}) +target_link_libraries(circle_resizer_python_api INTERFACE pybind11::module) + +set_target_properties(circle_resizer_python_api PROPERTIES CXX_VISIBILITY_PRESET "hidden" + CUDA_VISIBILITY_PRESET "hidden") + +target_include_directories(circle_resizer_python_api PRIVATE include) + +target_include_directories(circle_resizer_python_api PRIVATE ${PYTHON_INCLUDE_DIRS}) +target_include_directories(circle_resizer_python_api PRIVATE ${Pybind11_INCLUDE_DIRS}) +# target_link_libraries(circle_resizer_python_api PRIVATE ${PYTHON_LIBRARIES}) +target_link_libraries(circle_resizer_python_api PRIVATE circle_resizer_core) + +# Install the Python module +install(TARGETS circle_resizer_python_api DESTINATION lib) diff --git a/compiler/circle-resizer/python/include/Shape.h b/compiler/circle-resizer/python/include/Shape.h new file mode 100644 index 00000000000..a1cfae65204 --- /dev/null +++ b/compiler/circle-resizer/python/include/Shape.h @@ -0,0 +1,24 @@ +/* + * 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_BINDINGS_SHAPE_H__ +#define __CIRCLE_RESIZER_BINDINGS_SHAPE_H__ + +#include "Shape.h" + +// namespace py = pybind11; + +#endif // __CIRCLE_RESIZER_BINDINGS_SHAPE_H__ diff --git a/compiler/circle-resizer/python/src/Shape.cpp b/compiler/circle-resizer/python/src/Shape.cpp new file mode 100644 index 00000000000..f18b1ae4827 --- /dev/null +++ b/compiler/circle-resizer/python/src/Shape.cpp @@ -0,0 +1,3 @@ +#include "Shape.h" + +#include diff --git a/compiler/circle-resizer/src/CMakeLists.txt b/compiler/circle-resizer/src/CMakeLists.txt new file mode 100644 index 00000000000..0844da12641 --- /dev/null +++ b/compiler/circle-resizer/src/CMakeLists.txt @@ -0,0 +1,17 @@ +list(APPEND CIRCLE_RESIZER_SOURCES Shape.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES ShapeParser.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES ModelData.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES CircleResizer.cpp) + +add_library(circle_resizer_core STATIC "${CIRCLE_RESIZER_SOURCES}") + +target_include_directories(circle_resizer_core PUBLIC ../include) + +target_link_libraries(circle_resizer_core PRIVATE mio_circle08) +target_link_libraries(circle_resizer_core PRIVATE logo_core) +target_link_libraries(circle_resizer_core PRIVATE luci_pass) +target_link_libraries(circle_resizer_core PRIVATE luci_lang) +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 logo) +target_link_libraries(circle_resizer_core PRIVATE luci_log) diff --git a/compiler/circle-resizer/tests/CMakeLists.txt b/compiler/circle-resizer/tests/CMakeLists.txt new file mode 100644 index 00000000000..d2d4577955b --- /dev/null +++ b/compiler/circle-resizer/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +list(APPEND CIRCLE_RESIZER_TEST_SOURCES ShapeParser.test.cpp) +list(APPEND CIRCLE_RESIZER_TEST_SOURCES CircleResizer.test.cpp) + +nnas_find_package(GTest REQUIRED) +GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES}) +target_link_libraries(circle_resizer_unit_test circle_resizer_core) +target_link_libraries(circle_resizer_unit_test oops) + +get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) +set_tests_properties(circle_resizer_unit_test + PROPERTIES + ENVIRONMENT "ARTIFACTS_PATH=${ARTIFACTS_PATH}") diff --git a/infra/nnfw/CMakeLists.txt b/infra/nnfw/CMakeLists.txt index 5209f6d4948..8d67137e8f5 100644 --- a/infra/nnfw/CMakeLists.txt +++ b/infra/nnfw/CMakeLists.txt @@ -41,7 +41,7 @@ endmacro(nnas_include) # - this may drop warnings like "-- Could NOT find Eigen (missing: Eigen_DIR) # nnfw_find_package(Eigen QUIET): Load settings silently, without warnings # nnfw_find_package(Eigen REQUIRED): Load settings but stop with error when failed -macro(nnfw_find_package PREFIX) +macro(nnfw_find_package PREFIX) # find_package(${PREFIX} CONFIG NO_DEFAULT_PATH PATHS ${CMAKE_SOURCE_DIR}/cmake/packages ${ARGN} @@ -49,7 +49,7 @@ macro(nnfw_find_package PREFIX) endmacro(nnfw_find_package) # Common 'find_package()' wrapper to find in infra/cmake/packages folder -macro(nnas_find_package PREFIX) +macro(nnas_find_package PREFIX) # find_package(${PREFIX} CONFIG NO_DEFAULT_PATH PATHS ${NNAS_PROJECT_SOURCE_DIR}/infra/cmake/packages ${ARGN} From dd953aa3a0d74304f4fb5a1a2f64c3e9bf7ce579 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 5 Mar 2025 11:40:02 +0100 Subject: [PATCH 09/39] first working python api --- compiler/circle-resizer/python/CMakeLists.txt | 60 ++++++++++++++----- .../circle-resizer/python/include/Shape.h | 24 -------- compiler/circle-resizer/python/src/Shape.cpp | 10 ++++ compiler/circle-resizer/src/CMakeLists.txt | 4 +- compiler/circle-resizer/src/Shape.cpp | 2 + 5 files changed, 60 insertions(+), 40 deletions(-) delete mode 100644 compiler/circle-resizer/python/include/Shape.h diff --git a/compiler/circle-resizer/python/CMakeLists.txt b/compiler/circle-resizer/python/CMakeLists.txt index 0247c5118ae..a6b4917e032 100644 --- a/compiler/circle-resizer/python/CMakeLists.txt +++ b/compiler/circle-resizer/python/CMakeLists.txt @@ -1,13 +1,46 @@ -# FindPythonLibs is deprecated since 3.12, and recommand to use FindPython. -# But on cross compile, FindPython is not working for target environment -# For workaround, use PythonLibs -# refer to https://github.com/Samsung/ONE/issues/11368 for more details -find_package(PythonLibs REQUIRED) -set(PYTHON_MODULE_PREFIX "lib" CACHE INTERNAL "Cross python lib prefix") -set(PYTHON_MODULE_EXTENSION ".so" CACHE INTERNAL "Cross python lib extension") +# NOTE find_package will try to use at least python3.8 as follows depending on platform version +# Ubuntu18.04; explictly installed python3.8 (default is python3.6) +# Ubuntu20.04; default python3.8 +# Ubuntu22.04; default python3.10 +# Ubuntu24.04; explictly installed python3.8 (default is python3.12) +# refer https://github.com/Samsung/ONE/issues/9962 +if(CMAKE_VERSION VERSION_LESS 3.12) + find_package(PythonInterp 3.8 QUIET) + find_package(PythonLibs 3.8 QUIET) -# Disable pybind11 python search mechanism -set(PYTHONLIBS_FOUND TRUE CACHE INTERNAL "") + if(NOT ${PYTHONINTERP_FOUND}) + message(STATUS "Build circle-resizer: FAILED (Python3 is missing)") + return() + endif() + + if(${PYTHON_VERSION_MINOR} LESS 8) + message(STATUS "Build circle-resizer: FAILED (Install Python version higher than or equal to 3.8)") + return() + endif() +else() + find_package(Python 3.8 EXACT COMPONENTS Development QUIET) + if(NOT Python_FOUND) + find_package(Python 3.8 COMPONENTS Development QUIET) + endif() + + # Require same python version of common-artifacts + if(Python_VERSION VERSION_GREATER_EQUAL 3.12) + message(STATUS "Build dalgona: FALSE (Python version 3.12 or higher is not supported yet)") + return() + endif() + if(Python_VERSION VERSION_LESS 3.8) + message(STATUS "Build dalgona: FAILED (Install Python version 3.8 or 3.10)") + return() + endif() + + if(NOT Python_Development_FOUND) + message(STATUS "Build dalgona: FAILED (Python3 development package is missing)") + return() + endif() + + set(PYTHON_INCLUDE_DIRS ${Python_INCLUDE_DIRS}) + set(PYTHON_LIBRARIES ${Python_LIBRARIES}) +endif() nnas_find_package(Pybind11) if(NOT Pybind11_FOUND) @@ -17,18 +50,15 @@ endif() list(APPEND CIRCLE_RESIZER_PYTHON_API src/Shape.cpp) -add_library(circle_resizer_python_api MODULE ${CIRCLE_RESIZER_PYTHON_API}) +add_library(circle_resizer_python_api SHARED ${CIRCLE_RESIZER_PYTHON_API}) target_link_libraries(circle_resizer_python_api INTERFACE pybind11::module) +target_link_libraries(circle_resizer_python_api PRIVATE circle_resizer_core) set_target_properties(circle_resizer_python_api PROPERTIES CXX_VISIBILITY_PRESET "hidden" CUDA_VISIBILITY_PRESET "hidden") - -target_include_directories(circle_resizer_python_api PRIVATE include) +set_target_properties(circle_resizer_python_api PROPERTIES PREFIX "") target_include_directories(circle_resizer_python_api PRIVATE ${PYTHON_INCLUDE_DIRS}) target_include_directories(circle_resizer_python_api PRIVATE ${Pybind11_INCLUDE_DIRS}) -# target_link_libraries(circle_resizer_python_api PRIVATE ${PYTHON_LIBRARIES}) -target_link_libraries(circle_resizer_python_api PRIVATE circle_resizer_core) -# Install the Python module install(TARGETS circle_resizer_python_api DESTINATION lib) diff --git a/compiler/circle-resizer/python/include/Shape.h b/compiler/circle-resizer/python/include/Shape.h deleted file mode 100644 index a1cfae65204..00000000000 --- a/compiler/circle-resizer/python/include/Shape.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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_BINDINGS_SHAPE_H__ -#define __CIRCLE_RESIZER_BINDINGS_SHAPE_H__ - -#include "Shape.h" - -// namespace py = pybind11; - -#endif // __CIRCLE_RESIZER_BINDINGS_SHAPE_H__ diff --git a/compiler/circle-resizer/python/src/Shape.cpp b/compiler/circle-resizer/python/src/Shape.cpp index f18b1ae4827..3c9b6c25612 100644 --- a/compiler/circle-resizer/python/src/Shape.cpp +++ b/compiler/circle-resizer/python/src/Shape.cpp @@ -1,3 +1,13 @@ #include "Shape.h" #include + +namespace py = pybind11; +using namespace circle_resizer; + +PYBIND11_MODULE(circle_resizer_python_api, m) +{ + m.doc() = "circle_resizer::Shape"; + + py::class_(m, "Dim").def(py::init()).def("value", &Dim::value); +} diff --git a/compiler/circle-resizer/src/CMakeLists.txt b/compiler/circle-resizer/src/CMakeLists.txt index 0844da12641..7e8ae844b7a 100644 --- a/compiler/circle-resizer/src/CMakeLists.txt +++ b/compiler/circle-resizer/src/CMakeLists.txt @@ -3,7 +3,7 @@ list(APPEND CIRCLE_RESIZER_SOURCES ShapeParser.cpp) list(APPEND CIRCLE_RESIZER_SOURCES ModelData.cpp) list(APPEND CIRCLE_RESIZER_SOURCES CircleResizer.cpp) -add_library(circle_resizer_core STATIC "${CIRCLE_RESIZER_SOURCES}") +add_library(circle_resizer_core SHARED "${CIRCLE_RESIZER_SOURCES}") target_include_directories(circle_resizer_core PUBLIC ../include) @@ -15,3 +15,5 @@ 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 logo) target_link_libraries(circle_resizer_core PRIVATE luci_log) + +install(TARGETS circle_resizer_core DESTINATION lib) diff --git a/compiler/circle-resizer/src/Shape.cpp b/compiler/circle-resizer/src/Shape.cpp index ad0a639bc0d..91c63c0aa39 100644 --- a/compiler/circle-resizer/src/Shape.cpp +++ b/compiler/circle-resizer/src/Shape.cpp @@ -16,6 +16,8 @@ #include "Shape.h" +#include + using namespace circle_resizer; Dim::Dim(int32_t dim) : _dim_value{dim} From c89181986a87bc13603405ceb09f70c41e56f0fd Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Mon, 10 Mar 2025 18:05:22 +0100 Subject: [PATCH 10/39] initial version of setup.py --- compiler/circle-resizer/README | 3 ++ .../python/circle_resizer/__init__.py | 1 + compiler/circle-resizer/python/setup.py | 45 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 compiler/circle-resizer/README create mode 100644 compiler/circle-resizer/python/circle_resizer/__init__.py create mode 100644 compiler/circle-resizer/python/setup.py diff --git a/compiler/circle-resizer/README b/compiler/circle-resizer/README new file mode 100644 index 00000000000..bca8079e2cc --- /dev/null +++ b/compiler/circle-resizer/README @@ -0,0 +1,3 @@ +```py +CMAKE_INSTALL_PREFIX=~/workspace/one_install_nncc python setup.py bdist_wheel +``` diff --git a/compiler/circle-resizer/python/circle_resizer/__init__.py b/compiler/circle-resizer/python/circle_resizer/__init__.py new file mode 100644 index 00000000000..0ccc84209bf --- /dev/null +++ b/compiler/circle-resizer/python/circle_resizer/__init__.py @@ -0,0 +1 @@ +from circle_resizer.circle_resizer_python_api import Dim diff --git a/compiler/circle-resizer/python/setup.py b/compiler/circle-resizer/python/setup.py new file mode 100644 index 00000000000..8235c128c17 --- /dev/null +++ b/compiler/circle-resizer/python/setup.py @@ -0,0 +1,45 @@ +from setuptools import setup, find_packages +import os +import pathlib +import shutil + +install_dir = os.environ.get("CMAKE_INSTALL_PREFIX") +if install_dir is None: + print( + 'You have to set CMAKE_INSTALL_PREFIX env variable with value passed to nncc cmake as install dir' + ) + exit(-1) + +lib_dir = pathlib.Path(f'{install_dir}/lib') +resizer_dependent_so_libs = [ + 'libluci_export.so', + 'libluci_import.so', + 'libluci_pass.so', + 'libluci_lang.so', + 'libloco.so', + 'libcircle_resizer_core.so', + 'circle_resizer_python_api.so', +] + +package_name = 'circle_resizer' +package_data_path = (pathlib.Path(__file__).parent / package_name).resolve() +package_data_path.mkdir(parents=True, exist_ok=True) + +for idx, lib_path in enumerate(resizer_dependent_so_libs): + shutil.copy((lib_dir / lib_path).resolve(), package_data_path) + # update path to relative to package_data_path (expected by the setup function) + resizer_dependent_so_libs[idx] = os.path.join(package_data_path, lib_path) + +setup( + name="circle-resizer", + version='0.0.0', + description='circle_resizer API binding', + long_description='It provides circle-resizer Python API', + url='https://github.com/Samsung/ONE', + license='Apache-2.0, MIT, BSD-2-Clause, BSD-3-Clause, Mozilla Public License 2.0', + has_ext_modules=lambda: True, + include_package_data=True, + packages=find_packages(), + package_data={package_name: resizer_dependent_so_libs}, + zip_safe=False, +) From a34d7f5ba233ed76a9b54a7c7388bbeda5364abb Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Mon, 10 Mar 2025 18:38:14 +0100 Subject: [PATCH 11/39] missing copyright --- compiler/circle-resizer/python/src/Shape.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compiler/circle-resizer/python/src/Shape.cpp b/compiler/circle-resizer/python/src/Shape.cpp index 3c9b6c25612..c54c2877273 100644 --- a/compiler/circle-resizer/python/src/Shape.cpp +++ b/compiler/circle-resizer/python/src/Shape.cpp @@ -1,3 +1,20 @@ + +/* + * 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 "Shape.h" #include From 28094bd98e9ea1a2e14bad130dfba5d1b14a33ee Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Mon, 10 Mar 2025 18:44:05 +0100 Subject: [PATCH 12/39] removed pybind11::module from linked libraries --- compiler/circle-resizer/python/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/circle-resizer/python/CMakeLists.txt b/compiler/circle-resizer/python/CMakeLists.txt index a6b4917e032..e9b6c51bc11 100644 --- a/compiler/circle-resizer/python/CMakeLists.txt +++ b/compiler/circle-resizer/python/CMakeLists.txt @@ -51,7 +51,6 @@ endif() list(APPEND CIRCLE_RESIZER_PYTHON_API src/Shape.cpp) add_library(circle_resizer_python_api SHARED ${CIRCLE_RESIZER_PYTHON_API}) -target_link_libraries(circle_resizer_python_api INTERFACE pybind11::module) target_link_libraries(circle_resizer_python_api PRIVATE circle_resizer_core) set_target_properties(circle_resizer_python_api PROPERTIES CXX_VISIBILITY_PRESET "hidden" From 472b8548bd6780d9e7f4a73753f960da42a5f022 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Tue, 11 Mar 2025 16:14:06 +0100 Subject: [PATCH 13/39] shape bindings --- compiler/circle-resizer/python/CMakeLists.txt | 4 +--- .../python/circle_resizer/__init__.py | 1 + .../{Shape.cpp => CircleResizerBindings.cpp} | 19 +++++++++++++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) rename compiler/circle-resizer/python/src/{Shape.cpp => CircleResizerBindings.cpp} (62%) diff --git a/compiler/circle-resizer/python/CMakeLists.txt b/compiler/circle-resizer/python/CMakeLists.txt index e9b6c51bc11..171a304781d 100644 --- a/compiler/circle-resizer/python/CMakeLists.txt +++ b/compiler/circle-resizer/python/CMakeLists.txt @@ -48,9 +48,7 @@ if(NOT Pybind11_FOUND) return() endif() -list(APPEND CIRCLE_RESIZER_PYTHON_API src/Shape.cpp) - -add_library(circle_resizer_python_api SHARED ${CIRCLE_RESIZER_PYTHON_API}) +add_library(circle_resizer_python_api SHARED src/CircleResizerBindings.cpp) target_link_libraries(circle_resizer_python_api PRIVATE circle_resizer_core) set_target_properties(circle_resizer_python_api PROPERTIES CXX_VISIBILITY_PRESET "hidden" diff --git a/compiler/circle-resizer/python/circle_resizer/__init__.py b/compiler/circle-resizer/python/circle_resizer/__init__.py index 0ccc84209bf..d635e459978 100644 --- a/compiler/circle-resizer/python/circle_resizer/__init__.py +++ b/compiler/circle-resizer/python/circle_resizer/__init__.py @@ -1 +1,2 @@ from circle_resizer.circle_resizer_python_api import Dim +from circle_resizer.circle_resizer_python_api import Shape diff --git a/compiler/circle-resizer/python/src/Shape.cpp b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp similarity index 62% rename from compiler/circle-resizer/python/src/Shape.cpp rename to compiler/circle-resizer/python/src/CircleResizerBindings.cpp index c54c2877273..e26a605eb44 100644 --- a/compiler/circle-resizer/python/src/Shape.cpp +++ b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp @@ -18,13 +18,28 @@ #include "Shape.h" #include +#include namespace py = pybind11; using namespace circle_resizer; +PYBIND11_MAKE_OPAQUE(Shape); + PYBIND11_MODULE(circle_resizer_python_api, m) { - m.doc() = "circle_resizer::Shape"; + m.doc() = "circle-resizer module"; + + py::class_ dim(m, "Dim"); + dim.doc() = "circle_resizer::Dim"; + dim.def(py::init()); + dim.def("is_dynamic", &Dim::is_dynamic); + dim.def("value", &Dim::value); + dim.def( + "__eq__", + [](const Shape& rhs, const Shape& lhs) { + return rhs == lhs; + }); - py::class_(m, "Dim").def(py::init()).def("value", &Dim::value); + auto shape = py::bind_vector(m, "Shape"); + shape.doc() = "circle_resizer::Shape"; } From 79d98868b920e453510f85e521159a95dd51b951 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Tue, 11 Mar 2025 16:35:05 +0100 Subject: [PATCH 14/39] initial refactor --- compiler/circle-resizer/app/Driver.cpp | 16 +- compiler/circle-resizer/include/ModelData.h | 17 +- .../{CircleResizer.h => ModelEditor.h} | 17 +- .../python/src/CircleResizerBindings.cpp | 6 +- compiler/circle-resizer/src/CMakeLists.txt | 2 +- compiler/circle-resizer/src/ModelData.cpp | 68 ++++++ .../{CircleResizer.cpp => ModelEditor.cpp} | 86 +------- compiler/circle-resizer/tests/CMakeLists.txt | 3 +- .../tests/CircleResizer.test.cpp | 206 ------------------ .../circle-resizer/tests/ModelData.test.cpp | 97 +++++++++ .../circle-resizer/tests/ModelEditor.test.cpp | 182 ++++++++++++++++ 11 files changed, 385 insertions(+), 315 deletions(-) rename compiler/circle-resizer/include/{CircleResizer.h => ModelEditor.h} (66%) rename compiler/circle-resizer/src/{CircleResizer.cpp => ModelEditor.cpp} (54%) delete mode 100644 compiler/circle-resizer/tests/CircleResizer.test.cpp create mode 100644 compiler/circle-resizer/tests/ModelData.test.cpp create mode 100644 compiler/circle-resizer/tests/ModelEditor.test.cpp diff --git a/compiler/circle-resizer/app/Driver.cpp b/compiler/circle-resizer/app/Driver.cpp index a01356467b9..82fa37abead 100644 --- a/compiler/circle-resizer/app/Driver.cpp +++ b/compiler/circle-resizer/app/Driver.cpp @@ -15,13 +15,14 @@ * limitations under the License. */ -#include "CircleResizer.h" +#include "ModelEditor.h" #include "ShapeParser.h" #include #include #include #include +#include using namespace circle_resizer; @@ -72,15 +73,16 @@ int entry(const int argc, char **argv) const auto input_path = arser.get("--input_model"); - CircleResizer resizer(input_path); - const auto input_shapes = resizer.input_shapes(); + auto model_data = std::make_shared(input_path); + ModelEditor resizer(model_data); + const auto input_shapes = model_data->input_shapes(); std::cout << "Input shapes before resizing:" << std::endl; for (size_t in_idx = 0; in_idx < input_shapes.size(); ++in_idx) { std::cout << in_idx + 1 << ". " << to_string(input_shapes[in_idx]) << std::endl; } - auto output_shapes = resizer.output_shapes(); + auto output_shapes = model_data->output_shapes(); std::cout << "Output shapes before resizing:" << std::endl; for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) { @@ -90,16 +92,16 @@ int entry(const int argc, char **argv) const auto output_path = arser.get("--output_model"); const auto new_input_shapes_str = arser.get("--input_shapes"); - resizer.resize_model(parse_shapes(new_input_shapes_str)); + resizer.resize_inputs(parse_shapes(new_input_shapes_str)); - output_shapes = resizer.output_shapes(); + output_shapes = model_data->output_shapes(); std::cout << "Output shapes after resizing:" << std::endl; for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) { std::cout << out_idx + 1 << ". " << to_string(output_shapes[out_idx]) << std::endl; } - resizer.save_model(output_path); + model_data->save(output_path); std::cout << "Resizing complete, the model saved to: " << output_path << std::endl; } catch (const std::runtime_error &err) diff --git a/compiler/circle-resizer/include/ModelData.h b/compiler/circle-resizer/include/ModelData.h index 579f9f8a3c8..ee180650f56 100644 --- a/compiler/circle-resizer/include/ModelData.h +++ b/compiler/circle-resizer/include/ModelData.h @@ -17,6 +17,8 @@ #ifndef __CIRCLE_RESIZER_MODEL_H__ #define __CIRCLE_RESIZER_MODEL_H__ +#include "Shape.h" + #include #include #include @@ -31,24 +33,29 @@ namespace circle_resizer // DESIGN NOTE: The purpose of the class is to keep buffer and module synchronized class ModelData { - public: explicit ModelData(const std::vector &buffer); - void invalidate_module(); - void invalidate_buffer(); + explicit ModelData(const std::string &model_path); // to satisfy forward declaration + unique_ptr ~ModelData(); -public: + void invalidate_module(); + void invalidate_buffer(); + std::vector &buffer(); luci::Module *module(); + std::vector input_shapes(); + std::vector output_shapes(); + + void save(std::ostream &stream); + void save(const std::string &output_path); + private: bool _module_invalidated = false, _buffer_invalidated = false; std::vector _buffer; std::unique_ptr _module; }; - } // namespace circle_resizer #endif // __CIRCLE_RESIZER_H__ diff --git a/compiler/circle-resizer/include/CircleResizer.h b/compiler/circle-resizer/include/ModelEditor.h similarity index 66% rename from compiler/circle-resizer/include/CircleResizer.h rename to compiler/circle-resizer/include/ModelEditor.h index aedf0e335ea..07ec23af217 100644 --- a/compiler/circle-resizer/include/CircleResizer.h +++ b/compiler/circle-resizer/include/ModelEditor.h @@ -22,27 +22,20 @@ #include #include +#include namespace circle_resizer { -class CircleResizer +class ModelEditor { public: - explicit CircleResizer(const std::vector &model_buffer); - explicit CircleResizer(const std::string &model_path); + explicit ModelEditor(std::shared_ptr model_data); public: - void resize_model(const std::vector &shapes); - void save_model(std::ostream &stream); - void save_model(const std::string &output_path); - -public: - // cannot be const because of loco::Graph limitations - std::vector input_shapes(); - std::vector output_shapes(); + ModelEditor &resize_inputs(const std::vector &shapes); private: - ModelData _model_data; + std::shared_ptr _model_data; }; } // namespace circle_resizer diff --git a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp index e26a605eb44..afeedf212b7 100644 --- a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp +++ b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp @@ -34,11 +34,7 @@ PYBIND11_MODULE(circle_resizer_python_api, m) dim.def(py::init()); dim.def("is_dynamic", &Dim::is_dynamic); dim.def("value", &Dim::value); - dim.def( - "__eq__", - [](const Shape& rhs, const Shape& lhs) { - return rhs == lhs; - }); + dim.def("__eq__", [](const Shape &rhs, const Shape &lhs) { return rhs == lhs; }); auto shape = py::bind_vector(m, "Shape"); shape.doc() = "circle_resizer::Shape"; diff --git a/compiler/circle-resizer/src/CMakeLists.txt b/compiler/circle-resizer/src/CMakeLists.txt index 7e8ae844b7a..c84e23cf55b 100644 --- a/compiler/circle-resizer/src/CMakeLists.txt +++ b/compiler/circle-resizer/src/CMakeLists.txt @@ -1,7 +1,7 @@ list(APPEND CIRCLE_RESIZER_SOURCES Shape.cpp) list(APPEND CIRCLE_RESIZER_SOURCES ShapeParser.cpp) list(APPEND CIRCLE_RESIZER_SOURCES ModelData.cpp) -list(APPEND CIRCLE_RESIZER_SOURCES CircleResizer.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES ModelEditor.cpp) add_library(circle_resizer_core SHARED "${CIRCLE_RESIZER_SOURCES}") diff --git a/compiler/circle-resizer/src/ModelData.cpp b/compiler/circle-resizer/src/ModelData.cpp index e03b122c7e1..52024ef69b9 100644 --- a/compiler/circle-resizer/src/ModelData.cpp +++ b/compiler/circle-resizer/src/ModelData.cpp @@ -22,10 +22,33 @@ #include #include +#include +#include + using namespace circle_resizer; namespace { +std::vector read_model(const std::string &model_path) +{ + std::ifstream file_stream(model_path, std::ios::in | std::ios::binary | std::ifstream::ate); + if (!file_stream.is_open()) + { + throw std::runtime_error("Failed to open file: " + model_path); + } + + std::streamsize size = file_stream.tellg(); + file_stream.seekg(0, std::ios::beg); + + std::vector buffer(size); + if (!file_stream.read(reinterpret_cast(buffer.data()), size)) + { + throw std::runtime_error("Failed to read file: " + model_path); + } + + return buffer; +} + std::unique_ptr load_module(const std::vector &model_buffer) { flatbuffers::Verifier verifier{model_buffer.data(), model_buffer.size()}; @@ -38,6 +61,7 @@ std::unique_ptr load_module(const std::vector &model_buff luci::Importer importer(source_ptr); return importer.importModule(model_buffer.data(), model_buffer.size()); } + class BufferModelContract : public luci::CircleExporter::Contract { public: @@ -62,6 +86,23 @@ class BufferModelContract : public luci::CircleExporter::Contract std::unique_ptr> _buffer; }; +template +std::vector extract_shapes(const std::vector &nodes) +{ + std::vector shapes; + for (const auto &loco_node : nodes) + { + shapes.push_back(Shape{}); + const auto circle_node = loco::must_cast(loco_node); + for (uint32_t dim_idx = 0; dim_idx < circle_node->rank(); dim_idx++) + { + const int32_t dim_val = circle_node->dim(dim_idx).value(); + shapes.back().push_back(Dim{dim_val}); + } + } + return shapes; +} + } // namespace ModelData::ModelData(const std::vector &buffer) @@ -69,6 +110,8 @@ ModelData::ModelData(const std::vector &buffer) { } +ModelData::ModelData(const std::string &model_path) : ModelData(read_model(model_path)) {} + void ModelData::invalidate_module() { _module_invalidated = true; } void ModelData::invalidate_buffer() { _buffer_invalidated = true; } @@ -100,4 +143,29 @@ luci::Module *ModelData::module() return _module.get(); } +void ModelData::save(std::ostream &stream) +{ + stream.write(reinterpret_cast(buffer().data()), buffer().size()); + if (!stream.good()) + { + throw std::runtime_error("Failed to write to output stream"); + } +} + +void ModelData::save(const std::string &output_path) +{ + std::ofstream out_stream(output_path, std::ios::out | std::ios::binary); + save(out_stream); +} + +std::vector ModelData::input_shapes() +{ + return extract_shapes(loco::input_nodes(module()->graph())); +} + +std::vector ModelData::output_shapes() +{ + return extract_shapes(loco::output_nodes(module()->graph())); +} + ModelData::~ModelData() = default; diff --git a/compiler/circle-resizer/src/CircleResizer.cpp b/compiler/circle-resizer/src/ModelEditor.cpp similarity index 54% rename from compiler/circle-resizer/src/CircleResizer.cpp rename to compiler/circle-resizer/src/ModelEditor.cpp index e8bb8e7c521..cfcfca69556 100644 --- a/compiler/circle-resizer/src/CircleResizer.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "CircleResizer.h" +#include "ModelEditor.h" #include @@ -25,34 +25,12 @@ #include #include -#include #include -#include using namespace circle_resizer; namespace { -std::vector read_model(const std::string &model_path) -{ - std::ifstream file_stream(model_path, std::ios::in | std::ios::binary | std::ifstream::ate); - if (!file_stream.is_open()) - { - throw std::runtime_error("Failed to open file: " + model_path); - } - - std::streamsize size = file_stream.tellg(); - file_stream.seekg(0, std::ios::beg); - - std::vector buffer(size); - if (!file_stream.read(reinterpret_cast(buffer.data()), size)) - { - throw std::runtime_error("Failed to read file: " + model_path); - } - - return buffer; -} - void replace_tensor_shape(::flatbuffers::Vector *tensor_shape, const Shape &new_shape) { const auto shape_size = tensor_shape->size(); @@ -67,37 +45,13 @@ void replace_tensor_shape(::flatbuffers::Vector *tensor_shape, const Sh } } -template -std::vector extract_shapes(const std::vector &nodes) -{ - std::vector shapes; - for (const auto &loco_node : nodes) - { - shapes.push_back(Shape{}); - const auto circle_node = loco::must_cast(loco_node); - for (uint32_t dim_idx = 0; dim_idx < circle_node->rank(); dim_idx++) - { - const int32_t dim_val = circle_node->dim(dim_idx).value(); - shapes.back().push_back(Dim{dim_val}); - } - } - return shapes; -} - } // namespace -CircleResizer::CircleResizer(const std::vector &model_buffer) - : _model_data{ModelData(model_buffer)} -{ -} +ModelEditor::ModelEditor(std::shared_ptr model_data) : _model_data{model_data} {} -CircleResizer::CircleResizer(const std::string &model_path) : CircleResizer(read_model(model_path)) +ModelEditor &ModelEditor::resize_inputs(const std::vector &shapes) { -} - -void CircleResizer::resize_model(const std::vector &shapes) -{ - auto model = circle::GetMutableModel(_model_data.buffer().data()); + auto model = circle::GetMutableModel(_model_data->buffer().data()); if (!model) { throw std::runtime_error("Incorrect model format"); @@ -123,43 +77,19 @@ void CircleResizer::resize_model(const std::vector &shapes) } // invalidate after changing input shape - _model_data.invalidate_module(); + _model_data->invalidate_module(); logo::Phase phase; phase.emplace_back(std::make_unique()); phase.emplace_back(std::make_unique()); phase.emplace_back(std::make_unique()); - auto graph = _model_data.module()->graph(); + auto graph = _model_data->module()->graph(); logo::PhaseRunner phase_runner{graph}; phase_runner.run(phase); // invalidate after shape inference - _model_data.invalidate_buffer(); -} - -void CircleResizer::save_model(std::ostream &stream) -{ - stream.write(reinterpret_cast(_model_data.buffer().data()), - _model_data.buffer().size()); - if (!stream.good()) - { - throw std::runtime_error("Failed to write to output stream"); - } -} - -void CircleResizer::save_model(const std::string &output_path) -{ - std::ofstream out_stream(output_path, std::ios::out | std::ios::binary); - save_model(out_stream); -} + _model_data->invalidate_buffer(); -std::vector CircleResizer::input_shapes() -{ - return extract_shapes(loco::input_nodes(_model_data.module()->graph())); -} - -std::vector CircleResizer::output_shapes() -{ - return extract_shapes(loco::output_nodes(_model_data.module()->graph())); + return *this; } diff --git a/compiler/circle-resizer/tests/CMakeLists.txt b/compiler/circle-resizer/tests/CMakeLists.txt index d2d4577955b..2d49d0b8b8d 100644 --- a/compiler/circle-resizer/tests/CMakeLists.txt +++ b/compiler/circle-resizer/tests/CMakeLists.txt @@ -3,7 +3,8 @@ if(NOT ENABLE_TEST) endif(NOT ENABLE_TEST) list(APPEND CIRCLE_RESIZER_TEST_SOURCES ShapeParser.test.cpp) -list(APPEND CIRCLE_RESIZER_TEST_SOURCES CircleResizer.test.cpp) +list(APPEND CIRCLE_RESIZER_TEST_SOURCES ModelData.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/CircleResizer.test.cpp b/compiler/circle-resizer/tests/CircleResizer.test.cpp deleted file mode 100644 index f28749dc327..00000000000 --- a/compiler/circle-resizer/tests/CircleResizer.test.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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 "CircleResizer.h" -#include "oops/UserExn.h" - -#include -#include - -#include -#include -#include -#include - -using namespace circle_resizer; -using ::testing::HasSubstr; - -class CircleResizerTest : public ::testing::Test -{ -protected: - virtual void SetUp() - { - _test_models_dir = std::getenv("ARTIFACTS_PATH"); - assert(!_test_models_dir.empty()); - } - -protected: - std::string _test_models_dir; -}; - -TEST_F(CircleResizerTest, single_input_single_output) -{ - CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); - const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; - resizer.resize_model(new_input_shapes); - EXPECT_EQ(resizer.input_shapes(), new_input_shapes); - EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); -} - -TEST_F(CircleResizerTest, single_input_two_outputs) -{ - CircleResizer resizer(_test_models_dir + "/CSE_Quantize_000.circle"); - const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; - resizer.resize_model(new_input_shapes); - EXPECT_EQ(resizer.input_shapes(), new_input_shapes); - EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, - Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); -} - -TEST_F(CircleResizerTest, two_inputs_single_output) -{ - CircleResizer resizer(_test_models_dir + "/Add_000.circle"); - const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; - resizer.resize_model(new_input_shapes); - EXPECT_EQ(resizer.input_shapes(), new_input_shapes); - EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); -} - -TEST_F(CircleResizerTest, two_inputs_two_outputs) -{ - CircleResizer resizer(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); - const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; - resizer.resize_model(new_input_shapes); - EXPECT_EQ(resizer.input_shapes(), new_input_shapes); - EXPECT_EQ(resizer.output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); -} - -TEST_F(CircleResizerTest, neg_model_file_not_exist) -{ - auto file_name = "/not_existed.circle"; - try - { - CircleResizer(_test_models_dir + file_name); - FAIL() << "Expected std::runtime_error"; - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Failed to open file")); - EXPECT_THAT(err.what(), HasSubstr(file_name)); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} - -TEST_F(CircleResizerTest, neg_invalid_model) -{ - try - { - CircleResizer(std::vector{1, 2, 3, 4, 5}); - FAIL() << "Expected std::runtime_error"; - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Verification of the model failed")); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} - -TEST_F(CircleResizerTest, neg_not_all_input_shapes_provided) -{ - CircleResizer resizer(_test_models_dir + "/Add_000.circle"); - try - { - resizer.resize_model(std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}); - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Expected input shapes: 2 while provided: 1")); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} - -TEST_F(CircleResizerTest, neg_incorrect_rank_of_new_shape) -{ - CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); - try - { - resizer.resize_model(std::vector{Shape{Dim{3}}}); - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Provided shape rank: 1 is different from expected: 2")); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} - -TEST_F(CircleResizerTest, neg_shape_inference_failed) -{ - CircleResizer resizer(_test_models_dir + "/DepthwiseConv2D_000.circle"); - EXPECT_THROW(resizer.resize_model(std::vector{Shape{Dim{1}, Dim{64}, Dim{64}, Dim{8}}, - Shape{Dim{1}, Dim{2}, Dim{2}, Dim{3}}}), - oops::UserExn); -} - -TEST_F(CircleResizerTest, save_model_without_change) -{ - CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); - std::stringstream out_stream; - resizer.save_model(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)); - CircleResizer resizer2(model_buffer); - EXPECT_EQ(resizer2.input_shapes(), (std::vector{Shape{Dim{3}, Dim{3}}})); - EXPECT_EQ(resizer2.output_shapes(), (std::vector{Shape{Dim{3}, Dim{1}, Dim{3}}})); -} - -TEST_F(CircleResizerTest, save_model_after_resizing) -{ - CircleResizer resizer(_test_models_dir + "/ExpandDims_000.circle"); - std::stringstream out_stream; - const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; - resizer.resize_model(new_input_shapes); - resizer.save_model(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)); - CircleResizer resizer2(model_buffer); - EXPECT_EQ(resizer2.input_shapes(), new_input_shapes); - EXPECT_EQ(resizer2.output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); -} - -TEST_F(CircleResizerTest, neg_incorrect_output_stream) -{ - CircleResizer resizer(_test_models_dir + "/Add_000.circle"); - std::ofstream out_stream; - try - { - resizer.save_model(out_stream); - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Failed to write to output stream")); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} diff --git a/compiler/circle-resizer/tests/ModelData.test.cpp b/compiler/circle-resizer/tests/ModelData.test.cpp new file mode 100644 index 00000000000..78980de9d03 --- /dev/null +++ b/compiler/circle-resizer/tests/ModelData.test.cpp @@ -0,0 +1,97 @@ +/* + * 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 "ModelData.h" + +#include +#include + +#include + +using namespace circle_resizer; +using ::testing::HasSubstr; + +class ModelDataTest : 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(ModelDataTest, neg_model_file_not_exist) +{ + auto file_name = "/not_existed.circle"; + try + { + ModelData model_data(file_name); + FAIL() << "Expected std::runtime_error"; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Failed to open file")); + EXPECT_THAT(err.what(), HasSubstr(file_name)); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(ModelDataTest, neg_invalid_model) +{ + try + { + ModelData(std::vector{1, 2, 3, 4, 5}); + FAIL() << "Expected std::runtime_error"; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Verification of the model failed")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(ModelDataTest, neg_incorrect_output_stream) +{ + auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); + std::ofstream out_stream; + try + { + model_data->save(out_stream); + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Failed to write to output stream")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp new file mode 100644 index 00000000000..c79b6db9aa7 --- /dev/null +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -0,0 +1,182 @@ +/* + * 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 "oops/UserExn.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 model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(model_data); + const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(model_data->input_shapes(), new_input_shapes); + EXPECT_EQ(model_data->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); +} + +TEST_F(ModelEditorTest, single_input_two_outputs) +{ + auto model_data = std::make_shared(_test_models_dir + "/CSE_Quantize_000.circle"); + ModelEditor editor(model_data); + const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(model_data->input_shapes(), new_input_shapes); + EXPECT_EQ(model_data->output_shapes(), + (std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, + Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); +} + +TEST_F(ModelEditorTest, two_inputs_single_output) +{ + auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); + ModelEditor editor(model_data); + const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, + Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(model_data->input_shapes(), new_input_shapes); + EXPECT_EQ(model_data->output_shapes(), + (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); +} + +TEST_F(ModelEditorTest, two_inputs_two_outputs) +{ + auto model_data = + std::make_shared(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); + ModelEditor editor(model_data); + const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, + Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; + editor.resize_inputs(new_input_shapes); + EXPECT_EQ(model_data->input_shapes(), new_input_shapes); + EXPECT_EQ(model_data->output_shapes(), + (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, + Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); +} + +TEST_F(ModelEditorTest, neg_not_all_input_shapes_provided) +{ + auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); + ModelEditor editor(model_data); + try + { + editor.resize_inputs(std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}); + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Expected input shapes: 2 while provided: 1")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(ModelEditorTest, neg_incorrect_rank_of_new_shape) +{ + auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(model_data); + try + { + editor.resize_inputs(std::vector{Shape{Dim{3}}}); + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Provided shape rank: 1 is different from expected: 2")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(ModelEditorTest, neg_shape_inference_failed) +{ + auto model_data = std::make_shared(_test_models_dir + "/DepthwiseConv2D_000.circle"); + ModelEditor editor(model_data); + EXPECT_THROW(editor.resize_inputs(std::vector{Shape{Dim{1}, Dim{64}, Dim{64}, Dim{8}}, + Shape{Dim{1}, Dim{2}, Dim{2}, Dim{3}}}), + oops::UserExn); +} + +TEST_F(ModelEditorTest, save_without_change) +{ + auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(model_data); + std::stringstream out_stream; + model_data->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 model_data_2 = std::make_shared(model_buffer); + ModelEditor editor_2(model_data_2); + EXPECT_EQ(model_data_2->input_shapes(), (std::vector{Shape{Dim{3}, Dim{3}}})); + EXPECT_EQ(model_data_2->output_shapes(), (std::vector{Shape{Dim{3}, Dim{1}, Dim{3}}})); +} + +TEST_F(ModelEditorTest, save_after_resizing) +{ + auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(model_data); + std::stringstream out_stream; + const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; + editor.resize_inputs(new_input_shapes); + model_data->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 model_data_2 = std::make_shared(model_buffer); + ModelEditor editor_2(model_data_2); + EXPECT_EQ(model_data_2->input_shapes(), new_input_shapes); + EXPECT_EQ(model_data_2->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); +} + +TEST_F(ModelEditorTest, single_input_single_output_double_resizing) +{ + auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(model_data); + const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; + editor.resize_inputs(std::vector{Shape{Dim{6}, Dim{8}}}).resize_inputs(new_input_shapes); + EXPECT_EQ(model_data->input_shapes(), new_input_shapes); + EXPECT_EQ(model_data->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); +} From a0aa5d13a253eead0314e7c0963926462fae9028 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 12 Mar 2025 08:50:01 +0100 Subject: [PATCH 15/39] removed not used files --- .../DynInputs_Add_001/test.recipe | 28 ------------------- .../DynInputs_Add_001/test.reverse | 0 2 files changed, 28 deletions(-) delete mode 100644 res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe delete mode 100644 res/TensorFlowLiteRecipes/DynInputs_Add_001/test.reverse diff --git a/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe b/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe deleted file mode 100644 index 14239ca91e8..00000000000 --- a/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.recipe +++ /dev/null @@ -1,28 +0,0 @@ -# This recipe is to test zero size tensor for luci and luci-interpreter -operand { - name: "ifm1" - type: FLOAT32 - shape { dim: 1 dim: 0 dim: 1 } -} -operand { - name: "ifm2" - type: FLOAT32 - shape { dim: 1 dim: 0 dim: 1 } -} -operand { - name: "ofm" - type: FLOAT32 - shape { dim: 1 dim: 0 dim: 1 } -} -operation { - type: "Add" - input: "ifm1" - input: "ifm2" - output: "ofm" - add_options { - activation: NONE - } -} -input: "ifm1" -input: "ifm2" -output: "ofm" diff --git a/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.reverse b/res/TensorFlowLiteRecipes/DynInputs_Add_001/test.reverse deleted file mode 100644 index e69de29bb2d..00000000000 From 4d44e59cb89a5dbd012bf7a30deac4cc1371d9f6 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 13 Mar 2025 15:56:13 +0100 Subject: [PATCH 16/39] extend python api --- .../python/circle_resizer/__init__.py | 2 + .../python/src/CircleResizerBindings.cpp | 51 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/compiler/circle-resizer/python/circle_resizer/__init__.py b/compiler/circle-resizer/python/circle_resizer/__init__.py index d635e459978..4aeafd1d7b9 100644 --- a/compiler/circle-resizer/python/circle_resizer/__init__.py +++ b/compiler/circle-resizer/python/circle_resizer/__init__.py @@ -1,2 +1,4 @@ from circle_resizer.circle_resizer_python_api import Dim from circle_resizer.circle_resizer_python_api import Shape +from circle_resizer.circle_resizer_python_api import ModelData +from circle_resizer.circle_resizer_python_api import ModelEditor diff --git a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp index afeedf212b7..cf007c44c1d 100644 --- a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp +++ b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp @@ -16,8 +16,16 @@ */ #include "Shape.h" +#include "ModelData.h" +#include "ModelEditor.h" + +#include +#include +#include +#include #include +#include #include namespace py = pybind11; @@ -34,8 +42,49 @@ PYBIND11_MODULE(circle_resizer_python_api, m) dim.def(py::init()); dim.def("is_dynamic", &Dim::is_dynamic); dim.def("value", &Dim::value); - dim.def("__eq__", [](const Shape &rhs, const Shape &lhs) { return rhs == lhs; }); + dim.def("__eq__", [](const Dim &rhs, const Dim &lhs) { return rhs.value() == lhs.value(); }); + dim.def("__str__", [](const Dim &self) { return std::to_string(self.value()); }); auto shape = py::bind_vector(m, "Shape"); shape.doc() = "circle_resizer::Shape"; + shape.def("__eq__", [](const Shape &rhs, const Shape &lhs) { + if (rhs.size() != lhs.size()) + { + return false; + } + for (int i = 0; i < rhs.size(); ++i) + { + if (!(rhs[i] == lhs[i])) + { + return false; + } + } + return true; + }); + shape.def("__str__", [](const Shape &shape) { + std::stringstream ss; + ss << "["; + for (int i = 0; i < shape.size() - 1; ++i) + { + ss << shape[i].value() << ", "; + } + ss << shape.back().value() << "]"; + return ss.str(); + }); + + py::class_ model_data(m, "ModelData"); + model_data.doc() = "circle_resizer::ModelData"; + model_data.def(py::init &>(), py::arg("buffer")); + model_data.def(py::init(), py::arg("model_path")); + model_data.def("buffer", &ModelData::buffer); + model_data.def("input_shapes", &ModelData::input_shapes); + model_data.def("output_shapes", &ModelData::output_shapes); + model_data.def("save", py::overload_cast(&ModelData::save), py::arg("stream")); + model_data.def("save", py::overload_cast(&ModelData::save), + py::arg("output_path")); + + py::class_ model_editor(m, "ModelEditor"); + model_editor.doc() = "circle_resizer::ModelEditor"; + model_editor.def(py::init>(), py::arg("model_data")); + model_editor.def("resize_inputs", &ModelEditor::resize_inputs, py::arg("shapes")); } From 8fea4f242f47963c809b221e64ec1ec0d4fc010c Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 13 Mar 2025 15:58:00 +0100 Subject: [PATCH 17/39] revert debug change --- infra/nnfw/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/nnfw/CMakeLists.txt b/infra/nnfw/CMakeLists.txt index 8d67137e8f5..5209f6d4948 100644 --- a/infra/nnfw/CMakeLists.txt +++ b/infra/nnfw/CMakeLists.txt @@ -41,7 +41,7 @@ endmacro(nnas_include) # - this may drop warnings like "-- Could NOT find Eigen (missing: Eigen_DIR) # nnfw_find_package(Eigen QUIET): Load settings silently, without warnings # nnfw_find_package(Eigen REQUIRED): Load settings but stop with error when failed -macro(nnfw_find_package PREFIX) # +macro(nnfw_find_package PREFIX) find_package(${PREFIX} CONFIG NO_DEFAULT_PATH PATHS ${CMAKE_SOURCE_DIR}/cmake/packages ${ARGN} @@ -49,7 +49,7 @@ macro(nnfw_find_package PREFIX) # endmacro(nnfw_find_package) # Common 'find_package()' wrapper to find in infra/cmake/packages folder -macro(nnas_find_package PREFIX) # +macro(nnas_find_package PREFIX) find_package(${PREFIX} CONFIG NO_DEFAULT_PATH PATHS ${NNAS_PROJECT_SOURCE_DIR}/infra/cmake/packages ${ARGN} From c569016c10017a31912e24fb928b7d0987d4704b Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Fri, 14 Mar 2025 16:46:29 +0100 Subject: [PATCH 18/39] python api refactor --- compiler/circle-resizer/include/ModelData.h | 4 +- compiler/circle-resizer/include/ModelEditor.h | 2 +- compiler/circle-resizer/include/Shape.h | 1 + compiler/circle-resizer/include/ShapeParser.h | 2 +- .../circle_resizer/__init__.py | 5 +++ .../python/src/CircleResizerBindings.cpp | 39 +++++++++++++--- compiler/circle-resizer/src/ModelData.cpp | 9 ++-- compiler/circle-resizer/src/ModelEditor.cpp | 2 +- compiler/circle-resizer/src/ShapeParser.cpp | 4 +- .../circle-resizer/tests/ModelEditor.test.cpp | 45 +++++++++---------- .../circle-resizer/tests/ShapeParser.test.cpp | 32 ++++++------- 11 files changed, 85 insertions(+), 60 deletions(-) create mode 100644 compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py diff --git a/compiler/circle-resizer/include/ModelData.h b/compiler/circle-resizer/include/ModelData.h index ee180650f56..44e962ba7b5 100644 --- a/compiler/circle-resizer/include/ModelData.h +++ b/compiler/circle-resizer/include/ModelData.h @@ -45,8 +45,8 @@ class ModelData std::vector &buffer(); luci::Module *module(); - std::vector input_shapes(); - std::vector output_shapes(); + Shapes input_shapes(); + Shapes output_shapes(); void save(std::ostream &stream); void save(const std::string &output_path); diff --git a/compiler/circle-resizer/include/ModelEditor.h b/compiler/circle-resizer/include/ModelEditor.h index 07ec23af217..68c8cfd56dd 100644 --- a/compiler/circle-resizer/include/ModelEditor.h +++ b/compiler/circle-resizer/include/ModelEditor.h @@ -32,7 +32,7 @@ class ModelEditor explicit ModelEditor(std::shared_ptr model_data); public: - ModelEditor &resize_inputs(const std::vector &shapes); + ModelEditor &resize_inputs(const Shapes &shapes); private: std::shared_ptr _model_data; diff --git a/compiler/circle-resizer/include/Shape.h b/compiler/circle-resizer/include/Shape.h index 1938261176d..6ce1b609fd7 100644 --- a/compiler/circle-resizer/include/Shape.h +++ b/compiler/circle-resizer/include/Shape.h @@ -39,6 +39,7 @@ class Dim }; using Shape = std::vector; +using Shapes = std::vector; } // namespace circle_resizer #endif // __CIRCLE_RESIZER_SHAPE_H__ diff --git a/compiler/circle-resizer/include/ShapeParser.h b/compiler/circle-resizer/include/ShapeParser.h index 368ca9cdec9..202bc5bb7bd 100644 --- a/compiler/circle-resizer/include/ShapeParser.h +++ b/compiler/circle-resizer/include/ShapeParser.h @@ -24,7 +24,7 @@ namespace circle_resizer { -std::vector parse_shapes(std::string shapes_str); +Shapes parse_shapes(std::string shapes_str); } // namespace circle_resizer #endif // __CIRCLE_RESIZER_SHAPE_PARSER_H__ diff --git a/compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py b/compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py new file mode 100644 index 00000000000..f922f6c546d --- /dev/null +++ b/compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py @@ -0,0 +1,5 @@ +from circle_resizer.circle_resizer_python_api import Dim +from circle_resizer.circle_resizer_python_api import Shape +from circle_resizer.circle_resizer_python_api import Shapes +from circle_resizer.circle_resizer_python_api import ModelData +from circle_resizer.circle_resizer_python_api import ModelEditor diff --git a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp index cf007c44c1d..c9af6ba4d23 100644 --- a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp +++ b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp @@ -31,7 +31,27 @@ namespace py = pybind11; using namespace circle_resizer; +namespace +{ +std::string print_shape(const Shape &shape) +{ + if (shape.empty()) + { + return ""; + } + std::stringstream ss; + ss << "["; + for (int i = 0; i < shape.size() - 1; ++i) + { + ss << shape[i].value() << ", "; + } + ss << shape.back().value() << "]"; + return ss.str(); +} +} // namespace + PYBIND11_MAKE_OPAQUE(Shape); +PYBIND11_MAKE_OPAQUE(Shapes); PYBIND11_MODULE(circle_resizer_python_api, m) { @@ -61,18 +81,25 @@ PYBIND11_MODULE(circle_resizer_python_api, m) } return true; }); - shape.def("__str__", [](const Shape &shape) { + shape.def("__str__", [](const Shape &shape) -> std::string { return print_shape(shape); }); + + auto shapes = py::bind_vector(m, "Shapes"); + shapes.doc() = "circle_resizer::Shapes"; + shapes.def("__str__", [](const Shapes &shapes) -> std::string { + if (shapes.empty()) + { + return ""; + } std::stringstream ss; - ss << "["; - for (int i = 0; i < shape.size() - 1; ++i) + for (int i = 0; i < shapes.size() - 1; ++i) { - ss << shape[i].value() << ", "; + ss << print_shape(shapes[i]) << ", "; } - ss << shape.back().value() << "]"; + ss << print_shape(shapes.back()); return ss.str(); }); - py::class_ model_data(m, "ModelData"); + py::class_> model_data(m, "ModelData"); model_data.doc() = "circle_resizer::ModelData"; model_data.def(py::init &>(), py::arg("buffer")); model_data.def(py::init(), py::arg("model_path")); diff --git a/compiler/circle-resizer/src/ModelData.cpp b/compiler/circle-resizer/src/ModelData.cpp index 52024ef69b9..162f61a5da2 100644 --- a/compiler/circle-resizer/src/ModelData.cpp +++ b/compiler/circle-resizer/src/ModelData.cpp @@ -86,10 +86,9 @@ class BufferModelContract : public luci::CircleExporter::Contract std::unique_ptr> _buffer; }; -template -std::vector extract_shapes(const std::vector &nodes) +template Shapes extract_shapes(const std::vector &nodes) { - std::vector shapes; + Shapes shapes; for (const auto &loco_node : nodes) { shapes.push_back(Shape{}); @@ -158,12 +157,12 @@ void ModelData::save(const std::string &output_path) save(out_stream); } -std::vector ModelData::input_shapes() +Shapes ModelData::input_shapes() { return extract_shapes(loco::input_nodes(module()->graph())); } -std::vector ModelData::output_shapes() +Shapes ModelData::output_shapes() { return extract_shapes(loco::output_nodes(module()->graph())); } diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index cfcfca69556..a3a78cddcca 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -49,7 +49,7 @@ void replace_tensor_shape(::flatbuffers::Vector *tensor_shape, const Sh ModelEditor::ModelEditor(std::shared_ptr model_data) : _model_data{model_data} {} -ModelEditor &ModelEditor::resize_inputs(const std::vector &shapes) +ModelEditor &ModelEditor::resize_inputs(const Shapes &shapes) { auto model = circle::GetMutableModel(_model_data->buffer().data()); if (!model) diff --git a/compiler/circle-resizer/src/ShapeParser.cpp b/compiler/circle-resizer/src/ShapeParser.cpp index 7e917c83793..e669c8c83f4 100644 --- a/compiler/circle-resizer/src/ShapeParser.cpp +++ b/compiler/circle-resizer/src/ShapeParser.cpp @@ -54,9 +54,9 @@ Shape parse_shape(std::string shape_str) } } // namespace -std::vector circle_resizer::parse_shapes(std::string shapes_str) +Shapes circle_resizer::parse_shapes(std::string shapes_str) { - std::vector result_shapes; + Shapes result_shapes; std::stringstream shapes_stream(shapes_str); std::string token; size_t begin_pos = 0, end_pos = 0; diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp index c79b6db9aa7..8ddc320a3c8 100644 --- a/compiler/circle-resizer/tests/ModelEditor.test.cpp +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -50,34 +50,32 @@ TEST_F(ModelEditorTest, single_input_single_output) { auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); ModelEditor editor(model_data); - const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; + const auto new_input_shapes = Shapes{Shape{Dim{4}, Dim{6}}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); + EXPECT_EQ(model_data->output_shapes(), (Shapes{Shape{Dim{4}, Dim{1}, Dim{6}}})); } TEST_F(ModelEditorTest, single_input_two_outputs) { auto model_data = std::make_shared(_test_models_dir + "/CSE_Quantize_000.circle"); ModelEditor editor(model_data); - const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; + const auto new_input_shapes = Shapes{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(model_data->input_shapes(), new_input_shapes); EXPECT_EQ(model_data->output_shapes(), - (std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, - Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); + (Shapes{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); } TEST_F(ModelEditorTest, two_inputs_single_output) { auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); ModelEditor editor(model_data); - const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; + const auto new_input_shapes = + Shapes{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), - (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); + EXPECT_EQ(model_data->output_shapes(), (Shapes{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); } TEST_F(ModelEditorTest, two_inputs_two_outputs) @@ -85,13 +83,12 @@ TEST_F(ModelEditorTest, two_inputs_two_outputs) auto model_data = std::make_shared(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); ModelEditor editor(model_data); - const auto new_input_shapes = std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; + const auto new_input_shapes = + Shapes{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; editor.resize_inputs(new_input_shapes); EXPECT_EQ(model_data->input_shapes(), new_input_shapes); EXPECT_EQ(model_data->output_shapes(), - (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); + (Shapes{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); } TEST_F(ModelEditorTest, neg_not_all_input_shapes_provided) @@ -100,7 +97,7 @@ TEST_F(ModelEditorTest, neg_not_all_input_shapes_provided) ModelEditor editor(model_data); try { - editor.resize_inputs(std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}); + editor.resize_inputs(Shapes{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}); } catch (const std::runtime_error &err) { @@ -118,7 +115,7 @@ TEST_F(ModelEditorTest, neg_incorrect_rank_of_new_shape) ModelEditor editor(model_data); try { - editor.resize_inputs(std::vector{Shape{Dim{3}}}); + editor.resize_inputs(Shapes{Shape{Dim{3}}}); } catch (const std::runtime_error &err) { @@ -134,8 +131,8 @@ TEST_F(ModelEditorTest, neg_shape_inference_failed) { auto model_data = std::make_shared(_test_models_dir + "/DepthwiseConv2D_000.circle"); ModelEditor editor(model_data); - EXPECT_THROW(editor.resize_inputs(std::vector{Shape{Dim{1}, Dim{64}, Dim{64}, Dim{8}}, - Shape{Dim{1}, Dim{2}, Dim{2}, Dim{3}}}), + EXPECT_THROW(editor.resize_inputs(Shapes{Shape{Dim{1}, Dim{64}, Dim{64}, Dim{8}}, + Shape{Dim{1}, Dim{2}, Dim{2}, Dim{3}}}), oops::UserExn); } @@ -150,8 +147,8 @@ TEST_F(ModelEditorTest, save_without_change) model_buffer.insert(std::end(model_buffer), std::begin(model_buf_str), std::end(model_buf_str)); auto model_data_2 = std::make_shared(model_buffer); ModelEditor editor_2(model_data_2); - EXPECT_EQ(model_data_2->input_shapes(), (std::vector{Shape{Dim{3}, Dim{3}}})); - EXPECT_EQ(model_data_2->output_shapes(), (std::vector{Shape{Dim{3}, Dim{1}, Dim{3}}})); + EXPECT_EQ(model_data_2->input_shapes(), (Shapes{Shape{Dim{3}, Dim{3}}})); + EXPECT_EQ(model_data_2->output_shapes(), (Shapes{Shape{Dim{3}, Dim{1}, Dim{3}}})); } TEST_F(ModelEditorTest, save_after_resizing) @@ -159,7 +156,7 @@ TEST_F(ModelEditorTest, save_after_resizing) auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); ModelEditor editor(model_data); std::stringstream out_stream; - const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; + const auto new_input_shapes = Shapes{Shape{Dim{4}, Dim{6}}}; editor.resize_inputs(new_input_shapes); model_data->save(out_stream); const std::string &model_buf_str = out_stream.str(); @@ -168,15 +165,15 @@ TEST_F(ModelEditorTest, save_after_resizing) auto model_data_2 = std::make_shared(model_buffer); ModelEditor editor_2(model_data_2); EXPECT_EQ(model_data_2->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data_2->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); + EXPECT_EQ(model_data_2->output_shapes(), (Shapes{Shape{Dim{4}, Dim{1}, Dim{6}}})); } TEST_F(ModelEditorTest, single_input_single_output_double_resizing) { auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); ModelEditor editor(model_data); - const auto new_input_shapes = std::vector{Shape{Dim{4}, Dim{6}}}; - editor.resize_inputs(std::vector{Shape{Dim{6}, Dim{8}}}).resize_inputs(new_input_shapes); + const auto new_input_shapes = Shapes{Shape{Dim{4}, Dim{6}}}; + editor.resize_inputs(Shapes{Shape{Dim{6}, Dim{8}}}).resize_inputs(new_input_shapes); EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); + EXPECT_EQ(model_data->output_shapes(), (Shapes{Shape{Dim{4}, Dim{1}, Dim{6}}})); } diff --git a/compiler/circle-resizer/tests/ShapeParser.test.cpp b/compiler/circle-resizer/tests/ShapeParser.test.cpp index 1595acad486..cf9ac340f92 100644 --- a/compiler/circle-resizer/tests/ShapeParser.test.cpp +++ b/compiler/circle-resizer/tests/ShapeParser.test.cpp @@ -25,15 +25,14 @@ using namespace circle_resizer; -class ParseShapeTestFixture - : public ::testing::TestWithParam>> +class ParseShapeTestFixture : public ::testing::TestWithParam> { }; TEST_P(ParseShapeTestFixture, proper_shape_returned) { const auto [input_shape_str, expected_shapes] = GetParam(); - std::vector result_shapes; + Shapes result_shapes; EXPECT_NO_THROW(result_shapes = parse_shapes(input_shape_str)); ASSERT_EQ(result_shapes, expected_shapes); } @@ -42,23 +41,20 @@ INSTANTIATE_TEST_SUITE_P( ParseShapeTest, ParseShapeTestFixture, ::testing::Values( // single shape - std::make_tuple("[3,4]", std::vector{Shape{Dim{3}, Dim{4}}}), - std::make_tuple("[3]", std::vector{Shape{Dim{3}}}), - std::make_tuple("[-1]", std::vector{Shape{Dim{-1}}}), - std::make_tuple("[ 5, 6]", std::vector{Shape{Dim{5}, Dim{6}}}), - std::make_tuple("[3 , 4]", std::vector{Shape{Dim{3}, Dim{4}}}), - std::make_tuple("[-1 , 4]", std::vector{Shape{Dim{-1}, Dim{4}}}), + std::make_tuple("[3,4]", Shapes{Shape{Dim{3}, Dim{4}}}), + std::make_tuple("[3]", Shapes{Shape{Dim{3}}}), std::make_tuple("[-1]", Shapes{Shape{Dim{-1}}}), + std::make_tuple("[ 5, 6]", Shapes{Shape{Dim{5}, Dim{6}}}), + std::make_tuple("[3 , 4]", Shapes{Shape{Dim{3}, Dim{4}}}), + std::make_tuple("[-1 , 4]", Shapes{Shape{Dim{-1}, Dim{4}}}), // many shapes - std::make_tuple("[3,4],[5,6]", - std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), - std::make_tuple("[1],[2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), - std::make_tuple(" [3, 4] , [5,6]", - std::vector{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), - std::make_tuple(" [ 1 ] ,[ 2]", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), - std::make_tuple(" [ 1 ] ,[ 2] ", std::vector{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple("[3,4],[5,6]", Shapes{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), + std::make_tuple("[1],[2]", Shapes{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [3, 4] , [5,6]", Shapes{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}), + std::make_tuple(" [ 1 ] ,[ 2]", Shapes{Shape{Dim{1}}, Shape{Dim{2}}}), + std::make_tuple(" [ 1 ] ,[ 2] ", Shapes{Shape{Dim{1}}, Shape{Dim{2}}}), std::make_tuple(" [1,2],[3,4,5],[6,7,8,9]", - std::vector{Shape{Dim{1}, Dim{2}}, Shape{Dim{3}, Dim{4}, Dim{5}}, - Shape{Dim{6}, Dim{7}, Dim{8}, Dim{9}}}))); + Shapes{Shape{Dim{1}, Dim{2}}, Shape{Dim{3}, Dim{4}, Dim{5}}, + Shape{Dim{6}, Dim{7}, Dim{8}, Dim{9}}}))); class InvalidArgParseShapeTestFixture : public ::testing::TestWithParam { From 61ab3fdfa5f1406458ef3e766f3300bbaa493f3f Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 19 Mar 2025 12:16:21 +0100 Subject: [PATCH 19/39] shape extraction refactor --- compiler/circle-resizer/src/ModelData.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/circle-resizer/src/ModelData.cpp b/compiler/circle-resizer/src/ModelData.cpp index 162f61a5da2..0c204da8c1e 100644 --- a/compiler/circle-resizer/src/ModelData.cpp +++ b/compiler/circle-resizer/src/ModelData.cpp @@ -95,8 +95,15 @@ template Shapes extract_shapes(const std::vector(loco_node); for (uint32_t dim_idx = 0; dim_idx < circle_node->rank(); dim_idx++) { - const int32_t dim_val = circle_node->dim(dim_idx).value(); - shapes.back().push_back(Dim{dim_val}); + if (circle_node->dim(dim_idx).known()) + { + const int32_t dim_val = circle_node->dim(dim_idx).value(); + shapes.back().push_back(Dim{dim_val}); + } + else + { + shapes.back().push_back(Dim{-1}); + } } } return shapes; @@ -144,7 +151,8 @@ luci::Module *ModelData::module() void ModelData::save(std::ostream &stream) { - stream.write(reinterpret_cast(buffer().data()), buffer().size()); + auto &buff = buffer(); + stream.write(reinterpret_cast(buff.data()), buff.size()); if (!stream.good()) { throw std::runtime_error("Failed to write to output stream"); From f0b1b2766f9028e0e84385d41768e2b92b1ed2f8 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 2 Apr 2025 15:21:07 +0200 Subject: [PATCH 20/39] integrate circle-resizer with one-cmds --- .github/workflows/run-onecc-build.yml | 2 + compiler/one-cmds/CMakeLists.txt | 1 + compiler/one-cmds/one-build | 5 +- compiler/one-cmds/one-build.template.cfg | 1 + compiler/one-cmds/one-resize | 121 ++++++++++++++++++ compiler/one-cmds/onecc.template.cfg | 10 ++ compiler/one-cmds/onelib/CfgRunner.py | 4 +- compiler/one-cmds/onelib/OptionBuilder.py | 7 +- compiler/one-cmds/tests/one-build_001.test | 43 +++++++ compiler/one-cmds/tests/one-import_002.cfg | 1 + compiler/one-cmds/tests/one-import_003.cfg | 1 + compiler/one-cmds/tests/one-import_004.cfg | 1 + compiler/one-cmds/tests/one-import_005.cfg | 1 + compiler/one-cmds/tests/one-resize_001.cfg | 14 ++ compiler/one-cmds/tests/one-resize_001.test | 46 +++++++ compiler/one-cmds/tests/one-resize_002.test | 43 +++++++ .../one-cmds/tests/one-resize_neg_001.test | 49 +++++++ .../one-cmds/tests/one-resize_neg_002.test | 51 ++++++++ compiler/one-cmds/tests/onecc_070.cfg | 15 +++ compiler/one-cmds/tests/onecc_070.test | 43 +++++++ 20 files changed, 452 insertions(+), 7 deletions(-) create mode 100644 compiler/one-cmds/one-resize create mode 100644 compiler/one-cmds/tests/one-build_001.test create mode 100644 compiler/one-cmds/tests/one-resize_001.cfg create mode 100644 compiler/one-cmds/tests/one-resize_001.test create mode 100644 compiler/one-cmds/tests/one-resize_002.test create mode 100644 compiler/one-cmds/tests/one-resize_neg_001.test create mode 100644 compiler/one-cmds/tests/one-resize_neg_002.test create mode 100644 compiler/one-cmds/tests/onecc_070.cfg create mode 100644 compiler/one-cmds/tests/onecc_070.test diff --git a/.github/workflows/run-onecc-build.yml b/.github/workflows/run-onecc-build.yml index 4ab2c268622..f631e07d605 100644 --- a/.github/workflows/run-onecc-build.yml +++ b/.github/workflows/run-onecc-build.yml @@ -126,6 +126,8 @@ jobs: ## onecc workflows bash onecc_032.test bash onecc_041.test + ## one-resize + bash onecc_070.test # Upload log if failed - name: Upload log diff --git a/compiler/one-cmds/CMakeLists.txt b/compiler/one-cmds/CMakeLists.txt index 2e43fce4faa..99df414cd44 100644 --- a/compiler/one-cmds/CMakeLists.txt +++ b/compiler/one-cmds/CMakeLists.txt @@ -51,6 +51,7 @@ set(ONE_COMMAND_FILES one-import-tf one-import-tflite one-import-onnx + one-resize one-optimize one-quantize one-pack diff --git a/compiler/one-cmds/one-build b/compiler/one-cmds/one-build index 556a8f85eb9..984545a5b8d 100644 --- a/compiler/one-cmds/one-build +++ b/compiler/one-cmds/one-build @@ -79,6 +79,7 @@ def _get_driver_name(driver_name): 'one-import-tf': 'one-import-tf', 'one-import-tflite': 'one-import-tflite', 'one-import-onnx': 'one-import-onnx', + 'one-resize': 'one-resize', 'one-optimize': 'one-optimize', 'one-quantize': 'one-quantize', 'one-partition': 'one-partition', @@ -154,8 +155,8 @@ def main(): bin_dir = os.path.dirname(os.path.realpath(__file__)) import_drivers_dict = oneutils.detect_one_import_drivers(bin_dir) transform_drivers = [ - 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen', 'one-profile', - 'one-partition' + 'one-resize', 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen', + 'one-profile', 'one-partition' ] _verify_cfg(import_drivers_dict, config) diff --git a/compiler/one-cmds/one-build.template.cfg b/compiler/one-cmds/one-build.template.cfg index 42960811ef3..583c84e180f 100644 --- a/compiler/one-cmds/one-build.template.cfg +++ b/compiler/one-cmds/one-build.template.cfg @@ -3,6 +3,7 @@ one-import-tf=True one-import-tflite=False one-import-bcq=False one-import-onnx=False +one-resize=False one-optimize=True one-quantize=False one-parition=False diff --git a/compiler/one-cmds/one-resize b/compiler/one-cmds/one-resize new file mode 100644 index 00000000000..a59e9bdf61e --- /dev/null +++ b/compiler/one-cmds/one-resize @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +''''export SCRIPT_PATH="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" # ''' +''''export PY_PATH=${SCRIPT_PATH}/venv/bin/python # ''' +''''test -f ${PY_PATH} && exec ${PY_PATH} "$0" "$@" # ''' +''''echo "Error: Virtual environment not found. Please run 'one-prepare-venv' command." # ''' +''''exit 255 # ''' + +# 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. + +import argparse +import configparser +import os +import sys + +import onelib.utils as oneutils + +# TODO Find better way to suppress trackback on error +sys.tracebacklimit = 0 + + +def _get_parser(): + parser = argparse.ArgumentParser( + description='command line tool to change shape of model inputs') + + oneutils.add_default_arg(parser) + + parser.add_argument('--input_model', + type=str, + help='Input model filepath (.circle)') + + parser.add_argument('--output_model', + type=str, + help='Resized output model filepath (.circle)') + + parser.add_argument('--input_shapes', + type=str, + help='New inputs shapes in in comma separated format. An example for 2 inputs: [1,2],[3,4].') + + return parser + + +def _parse_arg(parser): + args = parser.parse_args() + # print version + if args.version: + oneutils.print_version_and_exit(__file__) + + return args + + +def _verify_arg(parser, args): + """verify given arguments""" + # check if required arguments is given + missing = [] + if not oneutils.is_valid_attr(args, 'input_model'): + missing.append('input_model') + if not oneutils.is_valid_attr(args, 'output_model'): + missing.append('output_model') + if not oneutils.is_valid_attr(args, 'input_shapes'): + missing.append('input_shapes') + if len(missing): + parser.error('the following arguments are required: ' + ' '.join(missing)) + return + + +def _resize(args): + # get file path to log + bin_path = os.path.dirname(os.path.realpath(__file__)) + cur_path = os.getcwd() + input_model = os.path.join(cur_path, args.input_model) + output_model = os.path.join(cur_path, args.output_model) + + log_file_path = os.path.join(cur_path, output_model) + '.log' + + with open(log_file_path, 'wb', buffering=0) as f: + # make a command to package circle model and metadata into nnpackage + circle_resizer_path = os.path.join(bin_path, 'circle-resizer') + + cmd = [os.path.expanduser(circle_resizer_path)] + + cmd.append('--input_model') + cmd.append(input_model) + cmd.append('--output_model') + cmd.append(output_model) + cmd.append('--input_shapes') + cmd.append(args.input_shapes) + + f.write((' '.join(cmd) + '\n').encode()) + + # run circle-resizer + oneutils.run(cmd, err_prefix='one-resize', logfile=f) + + +def main(): + # parse arguments + parser = _get_parser() + args = _parse_arg(parser) + + # parse configuration file + oneutils.parse_cfg(args.config, 'one-resize', args) + + # verify arguments + _verify_arg(parser, args) + + # resize the model + _resize(args) + +if __name__ == '__main__': + oneutils.safemain(main, __file__) diff --git a/compiler/one-cmds/onecc.template.cfg b/compiler/one-cmds/onecc.template.cfg index 8bc1e2a4ccb..fd80f658252 100644 --- a/compiler/one-cmds/onecc.template.cfg +++ b/compiler/one-cmds/onecc.template.cfg @@ -14,6 +14,7 @@ one-import-tf=False one-import-tflite=False one-import-bcq=False one-import-onnx=False +one-resize=False ; circle to circle with optimization one-optimize=False ; circle to circle with quantization @@ -90,6 +91,15 @@ unroll_rnn= ; True or False unroll_lstm= +[one-resize] +# mandatory +; path to the input model +input_model= +; path to the output model +output_model= +; the new shapes of inputs +input_shapes= + [one-optimize] # mandatory ; circle file diff --git a/compiler/one-cmds/onelib/CfgRunner.py b/compiler/one-cmds/onelib/CfgRunner.py index 13272525445..27a5f3240e5 100644 --- a/compiler/one-cmds/onelib/CfgRunner.py +++ b/compiler/one-cmds/onelib/CfgRunner.py @@ -27,8 +27,8 @@ def _simple_warning(message, category, filename, lineno, file=None, line=None): class CfgRunner: driver_sequence = [ - 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen', 'one-profile', - 'one-partition', 'one-infer' + 'one-optimize', 'one-resize', 'one-quantize', 'one-pack', 'one-codegen', + 'one-profile', 'one-partition', 'one-infer' ] def __init__(self, path): diff --git a/compiler/one-cmds/onelib/OptionBuilder.py b/compiler/one-cmds/onelib/OptionBuilder.py index 6a75783ada4..750b196467b 100644 --- a/compiler/one-cmds/onelib/OptionBuilder.py +++ b/compiler/one-cmds/onelib/OptionBuilder.py @@ -80,9 +80,10 @@ def _build_quantize(self, commands): return options def build(self, commands): - cmd_book = dict.fromkeys( - ['one-import-bcq', 'one-import-tflite', 'one-pack', 'one-partition'], - self._build_default) + cmd_book = dict.fromkeys([ + 'one-import-bcq', 'one-import-tflite', 'one-resize', 'one-pack', + 'one-partition' + ], self._build_default) cmd_book['one-codegen'] = self._build_with_unknown_command cmd_book['one-import-onnx'] = self._build_import cmd_book['one-import-pytorch'] = self._build_import diff --git a/compiler/one-cmds/tests/one-build_001.test b/compiler/one-cmds/tests/one-build_001.test new file mode 100644 index 00000000000..317e90a4a39 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_001.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# 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. + +# positive usage with overriding option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +config_file="one-resize_001.cfg" +output_file_cfg="inception_v3.resized.circle" + +rm -f ${filename}.log +rm -f ${output_file_cfg} + +# run test +one-build -C ${config_file} > ${filename}.log 2>&1 + +if [[ ! -s "${output_file_cfg}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-import_002.cfg b/compiler/one-cmds/tests/one-import_002.cfg index e7ede7bc2f4..079886cb565 100644 --- a/compiler/one-cmds/tests/one-import_002.cfg +++ b/compiler/one-cmds/tests/one-import_002.cfg @@ -2,6 +2,7 @@ one-import-tf=True one-import-tflite=False one-import-bcq=False +one-resize=True one-optimize=False one-quantize=False one-pack=False diff --git a/compiler/one-cmds/tests/one-import_003.cfg b/compiler/one-cmds/tests/one-import_003.cfg index b679ebdb3b6..e993d734cb9 100644 --- a/compiler/one-cmds/tests/one-import_003.cfg +++ b/compiler/one-cmds/tests/one-import_003.cfg @@ -2,6 +2,7 @@ one-import-tf=True one-import-tflite=False one-import-bcq=False +one-resize=False one-optimize=False one-quantize=False one-pack=False diff --git a/compiler/one-cmds/tests/one-import_004.cfg b/compiler/one-cmds/tests/one-import_004.cfg index d28c8dff642..2c9eae5a65a 100644 --- a/compiler/one-cmds/tests/one-import_004.cfg +++ b/compiler/one-cmds/tests/one-import_004.cfg @@ -2,6 +2,7 @@ one-import-tf=True one-import-tflite=False one-import-bcq=False +one-resize=False one-optimize=False one-quantize=False one-pack=False diff --git a/compiler/one-cmds/tests/one-import_005.cfg b/compiler/one-cmds/tests/one-import_005.cfg index abe4c7d7745..775a6980b2c 100644 --- a/compiler/one-cmds/tests/one-import_005.cfg +++ b/compiler/one-cmds/tests/one-import_005.cfg @@ -3,6 +3,7 @@ one-import-tf=False one-import-tflite=False one-import-bcq=False one-import-onnx=True +one-resize=False one-optimize=False one-quantize=False one-pack=False diff --git a/compiler/one-cmds/tests/one-resize_001.cfg b/compiler/one-cmds/tests/one-resize_001.cfg new file mode 100644 index 00000000000..4c205004f82 --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_001.cfg @@ -0,0 +1,14 @@ +[one-build] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-resize=True +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=False + +[one-resize] +input_model=inception_v3.circle +output_model=inception_v3.resized.circle +input_shapes=[2,299,299,3] diff --git a/compiler/one-cmds/tests/one-resize_001.test b/compiler/one-cmds/tests/one-resize_001.test new file mode 100644 index 00000000000..e4cef376f37 --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_001.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +test_model_name="Add_000" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +input_model="${test_model_name}.circle" +input_shapes="[1,3,3,1],[1,3,3,1]" +output_model="${test_model_name}.resize.circle" + +rm -f ${filename}.log +rm -f ${output_model} + +# run test +one-resize \ +--input_model ${input_model} \ +--input_shapes ${input_shapes} \ +--output_model ${output_model} > ${filename}.log 2>&1 + +if [[ ! -s "${output_model}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-resize_002.test b/compiler/one-cmds/tests/one-resize_002.test new file mode 100644 index 00000000000..1f8b69e5695 --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_002.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# 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. + +# positive usage with overriding option + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +config_file="one-resize_001.cfg" +output_file_cfg="inception_v3.resized.circle" + +rm -f ${filename}.log +rm -f ${output_file_cfg} + +# run test +one-resize -C ${config_file} > ${filename}.log 2>&1 + +if [[ ! -s "${output_file_cfg}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-resize_neg_001.test b/compiler/one-cmds/tests/one-resize_neg_001.test new file mode 100644 index 00000000000..239f4678058 --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_neg_001.test @@ -0,0 +1,49 @@ +#!/bin/bash + +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +test_model_name="Add_000" + +trap_err_onexit() +{ + if grep -q "the following arguments are required: input_shapes" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +input_model="${test_model_name}.circle" +output_model="${test_model_name}.resize.circle" + +rm -f ${filename}.log +rm -f ${output_model} + +# run test +one-resize \ +--input_model ${input_model} \ +--output_model ${output_model} > ${filename}.log 2>&1 + +if [[ ! -s "${output_model}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-resize_neg_002.test b/compiler/one-cmds/tests/one-resize_neg_002.test new file mode 100644 index 00000000000..509b6b93fc7 --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_neg_002.test @@ -0,0 +1,51 @@ +#!/bin/bash + +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +test_model_name="Add_000" + +trap_err_onexit() +{ + if grep -q "No shapes found in input string: abcd" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +input_model="${test_model_name}.circle" +input_shapes="abcd" +output_model="${test_model_name}.resize.circle" + +rm -f ${filename}.log +rm -f ${output_model} + +# run test +one-resize \ +--input_model ${input_model} \ +--input_shapes ${input_shapes} \ +--output_model ${output_model} > ${filename}.log 2>&1 + +if [[ ! -s "${output_model}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/onecc_070.cfg b/compiler/one-cmds/tests/onecc_070.cfg new file mode 100644 index 00000000000..bf974cbccc1 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_070.cfg @@ -0,0 +1,15 @@ +[onecc] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-import-onnx=False +one-resize=True +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=False + +[one-resize] +input_model=inception_v3.circle +output_model=inception_v3.resized.circle +input_shapes=[2,299,299,3] diff --git a/compiler/one-cmds/tests/onecc_070.test b/compiler/one-cmds/tests/onecc_070.test new file mode 100644 index 00000000000..ef30ff07a75 --- /dev/null +++ b/compiler/one-cmds/tests/onecc_070.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# 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. + +# one-import-tf -> one-quantize + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +config_file="onecc_070.cfg" +output_file="inception_v3.resized.circle" + +rm -f ${filename}.log +rm -rf ${output_file} + +# run test +onecc -C ${config_file} > ${filename}.log 2>&1 + +if [[ ! -s "${output_file}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" From 4896da55d3ce1c34cd6365c11131d531b60a2ab1 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 2 Apr 2025 15:52:48 +0200 Subject: [PATCH 21/39] input_model and output_model names refactor --- compiler/circle-resizer/app/Driver.cpp | 12 +++--- .../luci/service/src/Nodes/CircleOutput.cpp | 2 - compiler/one-cmds/one-resize | 42 ++++++++++--------- compiler/one-cmds/onecc.template.cfg | 6 +-- compiler/one-cmds/tests/one-resize_001.cfg | 4 +- compiler/one-cmds/tests/one-resize_001.test | 12 +++--- .../one-cmds/tests/one-resize_neg_001.test | 12 +++--- .../one-cmds/tests/one-resize_neg_002.test | 12 +++--- compiler/one-cmds/tests/onecc_070.cfg | 4 +- 9 files changed, 54 insertions(+), 52 deletions(-) diff --git a/compiler/circle-resizer/app/Driver.cpp b/compiler/circle-resizer/app/Driver.cpp index 82fa37abead..1654830a5eb 100644 --- a/compiler/circle-resizer/app/Driver.cpp +++ b/compiler/circle-resizer/app/Driver.cpp @@ -49,17 +49,17 @@ int entry(const int argc, char **argv) { arser::Arser arser("circle-resizer"); - arser.add_argument("--input_model") + arser.add_argument("--input_path") .nargs(1) .type(arser::DataType::STR) .required(true) - .help("Input model filepath (.circle)"); + .help("The path to the input model (.circle)"); - arser.add_argument("--output_model") + arser.add_argument("--output_path") .nargs(1) .type(arser::DataType::STR) .required(true) - .help("Resized output model filepath (.circle)"); + .help("The path to the resized model (.circle)"); arser.add_argument("--input_shapes") .nargs(1) @@ -71,7 +71,7 @@ int entry(const int argc, char **argv) { arser.parse(argc, argv); - const auto input_path = arser.get("--input_model"); + const auto input_path = arser.get("--input_path"); auto model_data = std::make_shared(input_path); ModelEditor resizer(model_data); @@ -89,7 +89,7 @@ int entry(const int argc, char **argv) std::cout << out_idx + 1 << ". " << to_string(output_shapes[out_idx]) << std::endl; } - const auto output_path = arser.get("--output_model"); + const auto output_path = arser.get("--output_path"); const auto new_input_shapes_str = arser.get("--input_shapes"); resizer.resize_inputs(parse_shapes(new_input_shapes_str)); diff --git a/compiler/luci/service/src/Nodes/CircleOutput.cpp b/compiler/luci/service/src/Nodes/CircleOutput.cpp index e45f78a909d..de20ca0f078 100644 --- a/compiler/luci/service/src/Nodes/CircleOutput.cpp +++ b/compiler/luci/service/src/Nodes/CircleOutput.cpp @@ -17,11 +17,9 @@ #include "luci/Service/CircleShapeInference.h" #include "CircleShapeInferenceHelper.h" -#include namespace luci { - namespace sinf { diff --git a/compiler/one-cmds/one-resize b/compiler/one-cmds/one-resize index a59e9bdf61e..e0a568f58ac 100644 --- a/compiler/one-cmds/one-resize +++ b/compiler/one-cmds/one-resize @@ -36,17 +36,20 @@ def _get_parser(): oneutils.add_default_arg(parser) - parser.add_argument('--input_model', + parser.add_argument('--input_path', type=str, - help='Input model filepath (.circle)') + help='The path to the input model (.circle)') - parser.add_argument('--output_model', + parser.add_argument('--output_path', type=str, - help='Resized output model filepath (.circle)') - - parser.add_argument('--input_shapes', - type=str, - help='New inputs shapes in in comma separated format. An example for 2 inputs: [1,2],[3,4].') + help='The path to the resized model (.circle)') + + parser.add_argument( + '--input_shapes', + type=str, + help= + 'New inputs shapes in in comma separated format. An example for 2 inputs: [1,2],[3,4].' + ) return parser @@ -64,10 +67,10 @@ def _verify_arg(parser, args): """verify given arguments""" # check if required arguments is given missing = [] - if not oneutils.is_valid_attr(args, 'input_model'): - missing.append('input_model') - if not oneutils.is_valid_attr(args, 'output_model'): - missing.append('output_model') + if not oneutils.is_valid_attr(args, 'input_path'): + missing.append('input_path') + if not oneutils.is_valid_attr(args, 'output_path'): + missing.append('output_path') if not oneutils.is_valid_attr(args, 'input_shapes'): missing.append('input_shapes') if len(missing): @@ -79,10 +82,10 @@ def _resize(args): # get file path to log bin_path = os.path.dirname(os.path.realpath(__file__)) cur_path = os.getcwd() - input_model = os.path.join(cur_path, args.input_model) - output_model = os.path.join(cur_path, args.output_model) + input_path = os.path.join(cur_path, args.input_path) + output_path = os.path.join(cur_path, args.output_path) - log_file_path = os.path.join(cur_path, output_model) + '.log' + log_file_path = os.path.join(cur_path, output_path) + '.log' with open(log_file_path, 'wb', buffering=0) as f: # make a command to package circle model and metadata into nnpackage @@ -90,10 +93,10 @@ def _resize(args): cmd = [os.path.expanduser(circle_resizer_path)] - cmd.append('--input_model') - cmd.append(input_model) - cmd.append('--output_model') - cmd.append(output_model) + cmd.append('--input_path') + cmd.append(input_path) + cmd.append('--output_path') + cmd.append(output_path) cmd.append('--input_shapes') cmd.append(args.input_shapes) @@ -117,5 +120,6 @@ def main(): # resize the model _resize(args) + if __name__ == '__main__': oneutils.safemain(main, __file__) diff --git a/compiler/one-cmds/onecc.template.cfg b/compiler/one-cmds/onecc.template.cfg index fd80f658252..a54110df2ac 100644 --- a/compiler/one-cmds/onecc.template.cfg +++ b/compiler/one-cmds/onecc.template.cfg @@ -94,9 +94,9 @@ unroll_lstm= [one-resize] # mandatory ; path to the input model -input_model= -; path to the output model -output_model= +input_path= +; path to the resized model +output_path= ; the new shapes of inputs input_shapes= diff --git a/compiler/one-cmds/tests/one-resize_001.cfg b/compiler/one-cmds/tests/one-resize_001.cfg index 4c205004f82..6964ad0eeb5 100644 --- a/compiler/one-cmds/tests/one-resize_001.cfg +++ b/compiler/one-cmds/tests/one-resize_001.cfg @@ -9,6 +9,6 @@ one-pack=False one-codegen=False [one-resize] -input_model=inception_v3.circle -output_model=inception_v3.resized.circle +input_path=inception_v3.circle +output_path=inception_v3.resized.circle input_shapes=[2,299,299,3] diff --git a/compiler/one-cmds/tests/one-resize_001.test b/compiler/one-cmds/tests/one-resize_001.test index e4cef376f37..68abb015579 100644 --- a/compiler/one-cmds/tests/one-resize_001.test +++ b/compiler/one-cmds/tests/one-resize_001.test @@ -26,20 +26,20 @@ trap_err_onexit() trap trap_err_onexit ERR -input_model="${test_model_name}.circle" +input_path="${test_model_name}.circle" input_shapes="[1,3,3,1],[1,3,3,1]" -output_model="${test_model_name}.resize.circle" +output_path="${test_model_name}.resize.circle" rm -f ${filename}.log -rm -f ${output_model} +rm -f ${output_path} # run test one-resize \ ---input_model ${input_model} \ +--input_path ${input_path} \ --input_shapes ${input_shapes} \ ---output_model ${output_model} > ${filename}.log 2>&1 +--output_path ${output_path} > ${filename}.log 2>&1 -if [[ ! -s "${output_model}" ]]; then +if [[ ! -s "${output_path}" ]]; then trap_err_onexit fi diff --git a/compiler/one-cmds/tests/one-resize_neg_001.test b/compiler/one-cmds/tests/one-resize_neg_001.test index 239f4678058..aaafdfa41aa 100644 --- a/compiler/one-cmds/tests/one-resize_neg_001.test +++ b/compiler/one-cmds/tests/one-resize_neg_001.test @@ -31,18 +31,18 @@ trap_err_onexit() trap trap_err_onexit ERR -input_model="${test_model_name}.circle" -output_model="${test_model_name}.resize.circle" +input_path="${test_model_name}.circle" +output_path="${test_model_name}.resize.circle" rm -f ${filename}.log -rm -f ${output_model} +rm -f ${output_path} # run test one-resize \ ---input_model ${input_model} \ ---output_model ${output_model} > ${filename}.log 2>&1 +--input_path ${input_path} \ +--output_path ${output_path} > ${filename}.log 2>&1 -if [[ ! -s "${output_model}" ]]; then +if [[ ! -s "${output_path}" ]]; then trap_err_onexit fi diff --git a/compiler/one-cmds/tests/one-resize_neg_002.test b/compiler/one-cmds/tests/one-resize_neg_002.test index 509b6b93fc7..f2e3b600561 100644 --- a/compiler/one-cmds/tests/one-resize_neg_002.test +++ b/compiler/one-cmds/tests/one-resize_neg_002.test @@ -31,20 +31,20 @@ trap_err_onexit() trap trap_err_onexit ERR -input_model="${test_model_name}.circle" +input_path="${test_model_name}.circle" input_shapes="abcd" -output_model="${test_model_name}.resize.circle" +output_path="${test_model_name}.resize.circle" rm -f ${filename}.log -rm -f ${output_model} +rm -f ${output_path} # run test one-resize \ ---input_model ${input_model} \ +--input_path ${input_path} \ --input_shapes ${input_shapes} \ ---output_model ${output_model} > ${filename}.log 2>&1 +--output_path ${output_path} > ${filename}.log 2>&1 -if [[ ! -s "${output_model}" ]]; then +if [[ ! -s "${output_path}" ]]; then trap_err_onexit fi diff --git a/compiler/one-cmds/tests/onecc_070.cfg b/compiler/one-cmds/tests/onecc_070.cfg index bf974cbccc1..481e8459f49 100644 --- a/compiler/one-cmds/tests/onecc_070.cfg +++ b/compiler/one-cmds/tests/onecc_070.cfg @@ -10,6 +10,6 @@ one-pack=False one-codegen=False [one-resize] -input_model=inception_v3.circle -output_model=inception_v3.resized.circle +input_path=inception_v3.circle +output_path=inception_v3.resized.circle input_shapes=[2,299,299,3] From a7afdf2df6028e7c6a635f7988b1a473a6490e7a Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 2 Apr 2025 15:54:52 +0200 Subject: [PATCH 22/39] remove build python file --- .../lib.linux-x86_64-cpython-38/circle_resizer/__init__.py | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py diff --git a/compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py b/compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py deleted file mode 100644 index f922f6c546d..00000000000 --- a/compiler/circle-resizer/python/build/lib.linux-x86_64-cpython-38/circle_resizer/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from circle_resizer.circle_resizer_python_api import Dim -from circle_resizer.circle_resizer_python_api import Shape -from circle_resizer.circle_resizer_python_api import Shapes -from circle_resizer.circle_resizer_python_api import ModelData -from circle_resizer.circle_resizer_python_api import ModelEditor From e4af358acb80a75ca83b56dbf6405366a8877ee4 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 2 Apr 2025 17:06:36 +0200 Subject: [PATCH 23/39] debian config --- compiler/one-cmds/one-resize | 20 +++++---- infra/debian/compiler/docs/one-resize.1 | 45 +++++++++++++++++++++ infra/debian/compiler/one-compiler.install | 1 + infra/debian/compiler/one-compiler.manpages | 1 + 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 infra/debian/compiler/docs/one-resize.1 diff --git a/compiler/one-cmds/one-resize b/compiler/one-cmds/one-resize index e0a568f58ac..f86c69692db 100644 --- a/compiler/one-cmds/one-resize +++ b/compiler/one-cmds/one-resize @@ -36,15 +36,21 @@ def _get_parser(): oneutils.add_default_arg(parser) - parser.add_argument('--input_path', - type=str, - help='The path to the input model (.circle)') + ## model2nnpkg arguments + model2nnpkg_group = parser.add_argument_group('arguments for packaging') - parser.add_argument('--output_path', - type=str, - help='The path to the resized model (.circle)') + model2nnpkg_group.add_argument('-i', + '--input_path', + type=str, + help='The path to the input model (.circle)') - parser.add_argument( + model2nnpkg_group.add_argument('-o', + '--output_path', + type=str, + help='The path to the resized model (.circle)') + + model2nnpkg_group.add_argument( + '-s', '--input_shapes', type=str, help= diff --git a/infra/debian/compiler/docs/one-resize.1 b/infra/debian/compiler/docs/one-resize.1 new file mode 100644 index 00000000000..a75ca2bee11 --- /dev/null +++ b/infra/debian/compiler/docs/one-resize.1 @@ -0,0 +1,45 @@ +.TH ONE-RESIZE "1" "July 2025" "one-resize version 1.28.0" "User Commands" +.SH NAME +one-resize \- resize input shapes of the model +.SH DESCRIPTION +usage: one\-resize [\-h] [\-v] [\-V] [\-C CONFIG] [\-i INPUT_PATH] [\-s INPUT_SHAPES] [\-o OUTPUT_PATH] +.PP +\fBone\-resize\fR is a command line tool to change input shapes of Circle models. +.SH OPTIONS +.TP +\fB\-h\fR, \fB\-\-help\fR +show this help message and exit +.TP +\fB\-v\fR, \fB\-\-version\fR +show program's version number and exit +.TP +\fB\-V\fR, \fB\-\-verbose\fR +output additional information to stdout or stderr +.TP +\fB\-C\fR CONFIG, \fB\-\-config\fR CONFIG +run with configuration file +.TP +\fB\-i\fR INPUT_PATH, \fB\-\-input_path\fR INPUT_PATH +The path to the input model (.circle) +.TP +\fB\-i\fR INPUT_SHAPES, \fB\-\-input_shapes\fR INPUT_SHAPES +New inputs shapes in in comma separated format. An example for 2 inputs: [1,2],[3,4] +.TP +\fB\-o\fR OUTPUT_PATH, \fB\-\-output_path\fR OUTPUT_PATH +The path to the resized model (.circle) +.SH COPYRIGHT +Copyright \(co 2020\-2025 Samsung Electronics Co., Ltd. All Rights Reserved +Licensed under the Apache License, Version 2.0 +https://github.com/Samsung/ONE +.SH "SEE ALSO" +The full documentation for +.B one-resize +is maintained as a Texinfo manual. If the +.B info +and +.B one-resize +programs are properly installed at your site, the command +.IP +.B info one-resize +.PP +should give you access to the complete manual. diff --git a/infra/debian/compiler/one-compiler.install b/infra/debian/compiler/one-compiler.install index 5b169677ec1..4330c5ab793 100644 --- a/infra/debian/compiler/one-compiler.install +++ b/infra/debian/compiler/one-compiler.install @@ -22,6 +22,7 @@ usr/bin/one-import-bcq usr/share/one/bin/ usr/bin/one-import-onnx usr/share/one/bin/ usr/bin/one-import-tf usr/share/one/bin/ usr/bin/one-import-tflite usr/share/one/bin/ +usr/bin/one-resize usr/share/one/bin/ usr/bin/one-infer usr/share/one/bin/ usr/bin/one-optimize usr/share/one/bin/ usr/bin/one-pack usr/share/one/bin/ diff --git a/infra/debian/compiler/one-compiler.manpages b/infra/debian/compiler/one-compiler.manpages index e0284ae4e4a..c4326421090 100644 --- a/infra/debian/compiler/one-compiler.manpages +++ b/infra/debian/compiler/one-compiler.manpages @@ -6,6 +6,7 @@ debian/docs/one-import-bcq.1 debian/docs/one-import-onnx.1 debian/docs/one-import-tf.1 debian/docs/one-import-tflite.1 +debian/docs/one-resize.1 debian/docs/one-optimize.1 debian/docs/one-pack.1 debian/docs/one-partition.1 From 4149cda66a98b8bdd30f346acc898036da6e28ed Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 2 Apr 2025 17:28:52 +0200 Subject: [PATCH 24/39] test for one-resize with shortened flags --- compiler/one-cmds/tests/one-resize_003.test | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 compiler/one-cmds/tests/one-resize_003.test diff --git a/compiler/one-cmds/tests/one-resize_003.test b/compiler/one-cmds/tests/one-resize_003.test new file mode 100644 index 00000000000..3b246413a30 --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_003.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +test_model_name="Net_InstanceNorm_003" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +input_path="${test_model_name}.circle" +input_shapes="[2,8,6,12]" +output_path="${test_model_name}.resize.circle" + +rm -f ${filename}.log +rm -f ${output_path} + +# run test +one-resize \ +-i ${input_path} \ +-s ${input_shapes} \ +-o ${output_path} > ${filename}.log 2>&1 + +if [[ ! -s "${output_path}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" From 89d790f57ae9bcfb0ed47fb652a0fc0f19c94cbe Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 3 Apr 2025 10:41:37 +0200 Subject: [PATCH 25/39] driver refactor --- compiler/circle-resizer/README | 5 +++ compiler/circle-resizer/app/CMakeLists.txt | 3 +- .../app/{Driver.cpp => CircleResizer.cpp} | 41 +++++++++++++------ compiler/circle-resizer/requires.cmake | 1 + 4 files changed, 36 insertions(+), 14 deletions(-) rename compiler/circle-resizer/app/{Driver.cpp => CircleResizer.cpp} (80%) diff --git a/compiler/circle-resizer/README b/compiler/circle-resizer/README index bca8079e2cc..2235b6c6b25 100644 --- a/compiler/circle-resizer/README +++ b/compiler/circle-resizer/README @@ -1,3 +1,8 @@ +# circle-resizer + +_circle-resizer provides capabilities to change the shapes of models inputs. + +## How to build Python API ```py CMAKE_INSTALL_PREFIX=~/workspace/one_install_nncc python setup.py bdist_wheel ``` diff --git a/compiler/circle-resizer/app/CMakeLists.txt b/compiler/circle-resizer/app/CMakeLists.txt index 15943439228..3fd67c9c974 100644 --- a/compiler/circle-resizer/app/CMakeLists.txt +++ b/compiler/circle-resizer/app/CMakeLists.txt @@ -1,7 +1,8 @@ -add_executable(circle_resizer Driver.cpp) +add_executable(circle_resizer CircleResizer.cpp) target_link_libraries(circle_resizer PRIVATE circle_resizer_core) target_link_libraries(circle_resizer PRIVATE arser) target_link_libraries(circle_resizer PRIVATE safemain) +target_link_libraries(circle_resizer PRIVATE vconone) set_target_properties(circle_resizer PROPERTIES OUTPUT_NAME "circle-resizer" diff --git a/compiler/circle-resizer/app/Driver.cpp b/compiler/circle-resizer/app/CircleResizer.cpp similarity index 80% rename from compiler/circle-resizer/app/Driver.cpp rename to compiler/circle-resizer/app/CircleResizer.cpp index 1654830a5eb..9685de3ac59 100644 --- a/compiler/circle-resizer/app/Driver.cpp +++ b/compiler/circle-resizer/app/CircleResizer.cpp @@ -1,4 +1,3 @@ - /* * Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved * @@ -19,6 +18,8 @@ #include "ShapeParser.h" #include +#include + #include #include #include @@ -30,24 +31,31 @@ namespace { std::string to_string(const Shape &shape) { - std::stringstream stream; - stream << "["; - for (size_t pos = 0; pos < shape.size(); ++pos) + if (shape.empty()) { - stream << shape[pos].value(); - if (pos < shape.size() - 1) - { - stream << ","; - } + return ""; + } + std::stringstream ss; + ss << "["; + for (int i = 0; i < shape.size() - 1; ++i) + { + ss << shape[i].value() << ", "; } - stream << "]"; - return stream.str(); + ss << shape.back().value() << "]"; + return ss.str(); } + +void print_version() +{ + std::cout << "circle-resizer version " << vconone::get_string() << std::endl; + std::cout << vconone::get_copyright() << std::endl; +} + } // namespace int entry(const int argc, char **argv) { - arser::Arser arser("circle-resizer"); + arser::Arser arser("circle-resizer provides capabilities to change inputs of the models"); arser.add_argument("--input_path") .nargs(1) @@ -67,6 +75,13 @@ int entry(const int argc, char **argv) .required(true) .help("New inputs shapes in in comma separated format. An example for 2 inputs: [1,2],[3,4]."); + arser.add_argument("--version") + .nargs(0) + .required(false) + .default_value(false) + .help("Show version information and exit") + .exit_with(print_version); + try { arser.parse(argc, argv); @@ -106,7 +121,7 @@ int entry(const int argc, char **argv) } catch (const std::runtime_error &err) { - std::cout << err.what() << std::endl; + std::cerr << err.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/compiler/circle-resizer/requires.cmake b/compiler/circle-resizer/requires.cmake index a07a9fe361d..452b08913b9 100644 --- a/compiler/circle-resizer/requires.cmake +++ b/compiler/circle-resizer/requires.cmake @@ -5,3 +5,4 @@ require("logo-core") require("luci") require("logo") require("common-artifacts") +require("vconone") From a2c5ed682fd91f49deb726c0ee86fcec2ea98c7d Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 3 Apr 2025 13:19:16 +0200 Subject: [PATCH 26/39] test cut llama2 fragments --- .github/workflows/run-onecc-build.yml | 2 - compiler/one-cmds/tests/one-resize_004.test | 46 +++++++++++++++++++ compiler/one-cmds/tests/one-resize_005.test | 46 +++++++++++++++++++ .../one-cmds/tests/prepare_test_materials.sh | 15 ++++++ 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 compiler/one-cmds/tests/one-resize_004.test create mode 100644 compiler/one-cmds/tests/one-resize_005.test diff --git a/.github/workflows/run-onecc-build.yml b/.github/workflows/run-onecc-build.yml index 1e183b14213..e6f58e09e74 100644 --- a/.github/workflows/run-onecc-build.yml +++ b/.github/workflows/run-onecc-build.yml @@ -129,8 +129,6 @@ jobs: ## onecc workflows bash onecc_032.test bash onecc_041.test - ## one-resize - bash onecc_070.test # Upload log if failed - name: Upload log diff --git a/compiler/one-cmds/tests/one-resize_004.test b/compiler/one-cmds/tests/one-resize_004.test new file mode 100644 index 00000000000..16dd6274ca1 --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_004.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +test_model_name="dyn_llama2_query" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +input_path="${test_model_name}.circle" +input_shapes="[2,16]" +output_path="${test_model_name}.resize.circle" + +rm -f ${filename}.log +rm -f ${output_path} + +# run test +one-resize \ +-i ${input_path} \ +-s ${input_shapes} \ +-o ${output_path} > ${filename}.log 2>&1 + +if [[ ! -s "${output_path}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-resize_005.test b/compiler/one-cmds/tests/one-resize_005.test new file mode 100644 index 00000000000..b5fc9c5c95d --- /dev/null +++ b/compiler/one-cmds/tests/one-resize_005.test @@ -0,0 +1,46 @@ +#!/bin/bash + +# 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. + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" +test_model_name="dyn_llama2_norm" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +input_path="${test_model_name}.circle" +input_shapes="[2,16]" +output_path="${test_model_name}.resize.circle" + +rm -f ${filename}.log +rm -f ${output_path} + +# run test +one-resize \ +-i ${input_path} \ +-s ${input_shapes} \ +-o ${output_path} > ${filename}.log 2>&1 + +if [[ ! -s "${output_path}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/prepare_test_materials.sh b/compiler/one-cmds/tests/prepare_test_materials.sh index 065c2dcc5dd..067eb744c61 100644 --- a/compiler/one-cmds/tests/prepare_test_materials.sh +++ b/compiler/one-cmds/tests/prepare_test_materials.sh @@ -135,6 +135,21 @@ if [[ ! -s "onnx_conv2d_conv2d_split.onnx" ]]; then # https://github.com/Samsung/ONE/issues/11280#issuecomment-1732852295 fi +# prepare models to test circle-resizer +if [[ ! -s "dyn_llama2_query.circle" ]]; then + rm -rf dyn_llama2_query.zip + wget -nv https://github.com/user-attachments/files/19584809/dyn_llama2_query.zip + unzip dyn_llama2_query.zip + # https://github.com/Samsung/ONE/issues/14791#issue-2902255581 +fi + +if [[ ! -s "dyn_llama2_norm.circle" ]]; then + rm -rf dyn_llama2_norm.zip + wget -nv https://github.com/user-attachments/files/19584810/dyn_llama2_norm.zip + unzip dyn_llama2_norm.zip + # https://github.com/Samsung/ONE/issues/14791#issue-2902255581 +fi + if [[ ! -s "Add_000.inputs.txt" ]]; then rm -rf Add_000.inputs.txt echo "Add_000.circle.input0 Add_000.circle.input1" >> Add_000.inputs.txt From 7a6fcdae17a7fbef5fbafd908c8f88e0357b4b5b Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 16 Apr 2025 22:32:33 +0200 Subject: [PATCH 27/39] model data doc + more tests --- compiler/circle-resizer/include/ModelData.h | 70 ++++++++++- compiler/circle-resizer/tests/CMakeLists.txt | 2 + .../circle-resizer/tests/ModelData.test.cpp | 117 +++++++++++++++++- 3 files changed, 180 insertions(+), 9 deletions(-) diff --git a/compiler/circle-resizer/include/ModelData.h b/compiler/circle-resizer/include/ModelData.h index ee180650f56..8e900b0969c 100644 --- a/compiler/circle-resizer/include/ModelData.h +++ b/compiler/circle-resizer/include/ModelData.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef __CIRCLE_RESIZER_MODEL_H__ -#define __CIRCLE_RESIZER_MODEL_H__ +#ifndef __CIRCLE_RESIZER_MODEL_DATA_H__ +#define __CIRCLE_RESIZER_MODEL_DATA_H__ #include "Shape.h" @@ -30,25 +30,84 @@ class Module; namespace circle_resizer { -// DESIGN NOTE: The purpose of the class is to keep buffer and module synchronized + +/** + * The representation of Circle Model. + * The purpose of the class is to keep the buffer and the module representation of the model + * synchronized. + */ class ModelData { public: + /** + * @brief Initialize the model with buffer representation. + * + * Exceptions: + * - std::runtime_error if interpretation of provided buffer as a circle model failed. + */ explicit ModelData(const std::vector &buffer); + + /** + * @brief Initialize the model with buffer representation. + * + * Exceptions: + * - std::runtime_error if reading a model from provided path failed. + */ explicit ModelData(const std::string &model_path); - // to satisfy forward declaration + unique_ptr + + /** + * @brief Dtor of ModelData. Note that explicit declaration is needed to satisfy forward + * declaration + unique_ptr. + */ ~ModelData(); + /** + * @brief Notify that the buffer representation of the model has been modified so the module is no + * more valid. + */ void invalidate_module(); + + /** + * @brief Notify that the module representation of the model has been modified so the buffer is no + * more valid. + */ void invalidate_buffer(); + /** + * @brief Get the loaded model as the buffer. + */ std::vector &buffer(); + + /** + * @brief Get the loaded model as the module. + */ luci::Module *module(); + /** + * @brief Get input shapes of the loaded model. + */ std::vector input_shapes(); + + /** + * @brief Get output shapes of the loaded model. + * + */ std::vector output_shapes(); + /** + * @brief Save the loaded model to the stream. + * + * Exceptions: + * - std::runtime_error if saving the model the given stream failed. + */ void save(std::ostream &stream); + + /** + * @brief Save the loaded model to the location indicated by output_path. + * + * Exceptions: + * - std::runtime_error if saving the model the given path failed. + */ void save(const std::string &output_path); private: @@ -56,6 +115,7 @@ class ModelData std::vector _buffer; std::unique_ptr _module; }; + } // namespace circle_resizer -#endif // __CIRCLE_RESIZER_H__ +#endif // __CIRCLE_RESIZER_MODEL_DATA_H__ diff --git a/compiler/circle-resizer/tests/CMakeLists.txt b/compiler/circle-resizer/tests/CMakeLists.txt index 2588df0f448..93f2161fe76 100644 --- a/compiler/circle-resizer/tests/CMakeLists.txt +++ b/compiler/circle-resizer/tests/CMakeLists.txt @@ -11,6 +11,8 @@ nnas_find_package(GTest REQUIRED) GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES}) target_link_libraries(circle_resizer_unit_test circle_resizer_core) target_link_libraries(circle_resizer_unit_test oops) +target_link_libraries(circle_resizer_unit_test mio_circle08) +target_link_libraries(circle_resizer_unit_test luci_lang) get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) set_tests_properties(circle_resizer_unit_test diff --git a/compiler/circle-resizer/tests/ModelData.test.cpp b/compiler/circle-resizer/tests/ModelData.test.cpp index 78980de9d03..1392ab99af4 100644 --- a/compiler/circle-resizer/tests/ModelData.test.cpp +++ b/compiler/circle-resizer/tests/ModelData.test.cpp @@ -16,14 +16,82 @@ #include "ModelData.h" -#include +#include "luci/IR/Module.h" +#include "loco/IR/Graph.h" + #include +#include + +#include #include using namespace circle_resizer; using ::testing::HasSubstr; +namespace +{ + +bool compare_shapes(const std::vector ¤t, const std::vector &expected) +{ + if (current.size() != expected.size()) + { + return false; + } + for (size_t i = 0; i < current.size(); ++i) + { + if (!(current[i] == expected[i])) + { + return false; + } + } + return true; +} + +std::string extract_subgraph_name(const std::vector &buffer) +{ + auto model = circle::GetModel(buffer.data()); + if (model) + { + auto subgraphs = model->subgraphs(); + if (subgraphs->size() > 0) + { + auto subgraph = subgraphs->Get(0); + if (subgraph->name()->c_str()) + { + return subgraph->name()->c_str(); + } + } + } + return ""; +} + +// change the first subgraph name using buffer as an input +bool change_subgraph_name(std::vector &buffer, const std::string &name) +{ + auto model = circle::GetMutableModel(buffer.data()); + if (!model) + { + return false; + } + auto subgraphs = model->mutable_subgraphs(); + auto subgraph = subgraphs->GetMutableObject(0); + if (subgraph->name()->size() != name.size()) + { + return false; + } + for (size_t i = 0; i < name.size(); ++i) + { + subgraph->mutable_name()->Mutate(i, name[i]); + } + return true; +} + +// change the first subgraph name using loco::Graph as an input +void change_subgraph_name(loco::Graph *graph, const std::string &name) { graph->name(name); } + +} // namespace + class ModelDataTest : public ::testing::Test { protected: @@ -42,7 +110,48 @@ class ModelDataTest : public ::testing::Test std::string _test_models_dir; }; -TEST_F(ModelDataTest, neg_model_file_not_exist) +TEST_F(ModelDataTest, proper_input_output_shapes) +{ + ModelData model_data(_test_models_dir + "/Add_000.circle"); + EXPECT_TRUE(compare_shapes(model_data.input_shapes(), + std::vector{Shape{1, 4, 4, 3}, Shape{1, 4, 4, 3}})); + EXPECT_TRUE(compare_shapes(model_data.output_shapes(), std::vector{Shape{1, 4, 4, 3}})); +} + +TEST_F(ModelDataTest, proper_output_stream) +{ + ModelData model_data(_test_models_dir + "/Add_000.circle"); + std::stringstream out_stream; + model_data.save(out_stream); + out_stream.seekg(0, std::ios::end); + EXPECT_TRUE(out_stream.tellg() > 0); +} + +TEST_F(ModelDataTest, invalidate_module) +{ + ModelData model_data(_test_models_dir + "/Add_000.circle"); + const auto module_before_name_change = model_data.module(); + const std::string new_subgraph_name = "abcd"; + ASSERT_TRUE(change_subgraph_name(model_data.buffer(), new_subgraph_name)); + model_data.invalidate_module(); // after buffer representation change the module is outdated + const auto module_after_name_change = model_data.module(); + EXPECT_EQ(module_after_name_change->graph()->name(), + new_subgraph_name); // check if buffer update applied to the module +} + +TEST_F(ModelDataTest, invalidate_buffer) +{ + ModelData model_data(_test_models_dir + "/Add_000.circle"); + const auto buffer_before_name_change = model_data.buffer(); + const std::string new_subgraph_name = "abcd"; + change_subgraph_name(model_data.module()->graph(), new_subgraph_name); + model_data.invalidate_buffer(); // after module representation change the buffer is outdated + const auto buffer_after_name_change = model_data.buffer(); + EXPECT_EQ(extract_subgraph_name(buffer_after_name_change), + new_subgraph_name); // check if module update applied to the buffer +} + +TEST_F(ModelDataTest, model_file_not_exist_NEG) { auto file_name = "/not_existed.circle"; try @@ -61,7 +170,7 @@ TEST_F(ModelDataTest, neg_model_file_not_exist) } } -TEST_F(ModelDataTest, neg_invalid_model) +TEST_F(ModelDataTest, invalid_model_NEG) { try { @@ -78,7 +187,7 @@ TEST_F(ModelDataTest, neg_invalid_model) } } -TEST_F(ModelDataTest, neg_incorrect_output_stream) +TEST_F(ModelDataTest, incorrect_output_stream_NEG) { auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); std::ofstream out_stream; From fd387adbfa3304cb4967ee097e7232d326144b43 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 17 Apr 2025 16:35:22 +0200 Subject: [PATCH 28/39] change model data --- compiler/circle-resizer/app/CircleResizer.cpp | 12 +- .../include/{ModelData.h => CircleModel.h} | 47 ++-- compiler/circle-resizer/include/ModelEditor.h | 8 +- .../python/circle_resizer/__init__.py | 2 +- .../python/src/CircleResizerBindings.cpp | 21 +- compiler/circle-resizer/src/CMakeLists.txt | 2 +- .../src/{ModelData.cpp => CircleModel.cpp} | 61 ++---- compiler/circle-resizer/src/ModelEditor.cpp | 74 +++---- compiler/circle-resizer/tests/CMakeLists.txt | 4 +- .../circle-resizer/tests/CircleModel.test.cpp | 135 ++++++++++++ .../circle-resizer/tests/ModelData.test.cpp | 206 ------------------ .../circle-resizer/tests/ModelEditor.test.cpp | 87 ++++---- 12 files changed, 260 insertions(+), 399 deletions(-) rename compiler/circle-resizer/include/{ModelData.h => CircleModel.h} (59%) rename compiler/circle-resizer/src/{ModelData.cpp => CircleModel.cpp} (70%) create mode 100644 compiler/circle-resizer/tests/CircleModel.test.cpp delete mode 100644 compiler/circle-resizer/tests/ModelData.test.cpp diff --git a/compiler/circle-resizer/app/CircleResizer.cpp b/compiler/circle-resizer/app/CircleResizer.cpp index b428b9c8ae3..855f288eb81 100644 --- a/compiler/circle-resizer/app/CircleResizer.cpp +++ b/compiler/circle-resizer/app/CircleResizer.cpp @@ -73,16 +73,16 @@ int entry(const int argc, char **argv) const auto input_path = arser.get("--input_path"); - auto model_data = std::make_shared(input_path); - ModelEditor resizer(model_data); - const auto input_shapes = model_data->input_shapes(); + auto circle_model = std::make_shared(input_path); + ModelEditor resizer(circle_model); + const auto input_shapes = circle_model->input_shapes(); std::cout << "Input shapes before resizing:" << std::endl; for (size_t in_idx = 0; in_idx < input_shapes.size(); ++in_idx) { std::cout << in_idx + 1 << ". " << input_shapes[in_idx] << std::endl; } - auto output_shapes = model_data->output_shapes(); + auto output_shapes = circle_model->output_shapes(); std::cout << "Output shapes before resizing:" << std::endl; for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) { @@ -94,14 +94,14 @@ int entry(const int argc, char **argv) resizer.resize_inputs(parse_shapes(new_input_shapes_str)); - output_shapes = model_data->output_shapes(); + output_shapes = circle_model->output_shapes(); std::cout << "Output shapes after resizing:" << std::endl; for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) { std::cout << out_idx + 1 << ". " << output_shapes[out_idx] << std::endl; } - model_data->save(output_path); + circle_model->save(output_path); std::cout << "Resizing complete, the model saved to: " << output_path << std::endl; } catch (const std::runtime_error &err) diff --git a/compiler/circle-resizer/include/ModelData.h b/compiler/circle-resizer/include/CircleModel.h similarity index 59% rename from compiler/circle-resizer/include/ModelData.h rename to compiler/circle-resizer/include/CircleModel.h index 8e900b0969c..20ed294e79a 100644 --- a/compiler/circle-resizer/include/ModelData.h +++ b/compiler/circle-resizer/include/CircleModel.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef __CIRCLE_RESIZER_MODEL_DATA_H__ -#define __CIRCLE_RESIZER_MODEL_DATA_H__ +#ifndef __CIRCLE_RESIZER_CIRCLE_MODEL_H__ +#define __CIRCLE_RESIZER_CIRCLE_MODEL_H__ #include "Shape.h" @@ -33,10 +33,8 @@ namespace circle_resizer /** * The representation of Circle Model. - * The purpose of the class is to keep the buffer and the module representation of the model - * synchronized. */ -class ModelData +class CircleModel { public: /** @@ -45,7 +43,7 @@ class ModelData * Exceptions: * - std::runtime_error if interpretation of provided buffer as a circle model failed. */ - explicit ModelData(const std::vector &buffer); + explicit CircleModel(const std::vector &buffer); /** * @brief Initialize the model with buffer representation. @@ -53,49 +51,32 @@ class ModelData * Exceptions: * - std::runtime_error if reading a model from provided path failed. */ - explicit ModelData(const std::string &model_path); + explicit CircleModel(const std::string &model_path); /** - * @brief Dtor of ModelData. Note that explicit declaration is needed to satisfy forward + * @brief Dtor of CircleModel. Note that explicit declaration is needed to satisfy forward * declaration + unique_ptr. */ - ~ModelData(); + ~CircleModel(); /** - * @brief Notify that the buffer representation of the model has been modified so the module is no - * more valid. - */ - void invalidate_module(); - - /** - * @brief Notify that the module representation of the model has been modified so the buffer is no - * more valid. - */ - void invalidate_buffer(); - - /** - * @brief Get the loaded model as the buffer. - */ - std::vector &buffer(); - - /** - * @brief Get the loaded model as the module. + * @brief Get the loaded model in luci::Module representation. */ luci::Module *module(); /** * @brief Get input shapes of the loaded model. */ - std::vector input_shapes(); + std::vector input_shapes() const; /** * @brief Get output shapes of the loaded model. * */ - std::vector output_shapes(); + std::vector output_shapes() const; /** - * @brief Save the loaded model to the stream. + * @brief Save the model to the output stream. * * Exceptions: * - std::runtime_error if saving the model the given stream failed. @@ -103,7 +84,7 @@ class ModelData void save(std::ostream &stream); /** - * @brief Save the loaded model to the location indicated by output_path. + * @brief Save the model to the location indicated by output_path. * * Exceptions: * - std::runtime_error if saving the model the given path failed. @@ -111,11 +92,9 @@ class ModelData void save(const std::string &output_path); private: - bool _module_invalidated = false, _buffer_invalidated = false; - std::vector _buffer; std::unique_ptr _module; }; } // namespace circle_resizer -#endif // __CIRCLE_RESIZER_MODEL_DATA_H__ +#endif // __CIRCLE_RESIZER_CIRCLE_MODEL_H__ diff --git a/compiler/circle-resizer/include/ModelEditor.h b/compiler/circle-resizer/include/ModelEditor.h index 07ec23af217..fbfb5e1e66f 100644 --- a/compiler/circle-resizer/include/ModelEditor.h +++ b/compiler/circle-resizer/include/ModelEditor.h @@ -18,7 +18,7 @@ #define __CIRCLE_RESIZER_H__ #include "Shape.h" -#include "ModelData.h" +#include "CircleModel.h" #include #include @@ -29,13 +29,13 @@ namespace circle_resizer class ModelEditor { public: - explicit ModelEditor(std::shared_ptr model_data); + explicit ModelEditor(std::shared_ptr circle_model); public: - ModelEditor &resize_inputs(const std::vector &shapes); + ModelEditor &resize_inputs(const std::vector &new_inputs_shapes); private: - std::shared_ptr _model_data; + std::shared_ptr _circle_model; }; } // namespace circle_resizer diff --git a/compiler/circle-resizer/python/circle_resizer/__init__.py b/compiler/circle-resizer/python/circle_resizer/__init__.py index 4aeafd1d7b9..1ca0e4ab5ff 100644 --- a/compiler/circle-resizer/python/circle_resizer/__init__.py +++ b/compiler/circle-resizer/python/circle_resizer/__init__.py @@ -1,4 +1,4 @@ from circle_resizer.circle_resizer_python_api import Dim from circle_resizer.circle_resizer_python_api import Shape -from circle_resizer.circle_resizer_python_api import ModelData +from circle_resizer.circle_resizer_python_api import CircleModel from circle_resizer.circle_resizer_python_api import ModelEditor diff --git a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp index 35ca659c267..332b3eed7fd 100644 --- a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp +++ b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp @@ -16,7 +16,7 @@ */ #include "Shape.h" -#include "ModelData.h" +#include "CircleModel.h" #include "ModelEditor.h" #include @@ -74,19 +74,18 @@ PYBIND11_MODULE(circle_resizer_python_api, m) return ss.str(); }); - py::class_> model_data(m, "ModelData"); - model_data.doc() = "circle_resizer::ModelData"; - model_data.def(py::init &>(), py::arg("buffer")); - model_data.def(py::init(), py::arg("model_path")); - model_data.def("buffer", &ModelData::buffer); - model_data.def("input_shapes", &ModelData::input_shapes); - model_data.def("output_shapes", &ModelData::output_shapes); - model_data.def("save", py::overload_cast(&ModelData::save), py::arg("stream")); - model_data.def("save", py::overload_cast(&ModelData::save), + py::class_> circle_model(m, "CircleModel"); + circle_model.doc() = "circle_resizer::CircleModel"; + circle_model.def(py::init &>(), py::arg("buffer")); + circle_model.def(py::init(), py::arg("model_path")); + circle_model.def("input_shapes", &CircleModel::input_shapes); + circle_model.def("output_shapes", &CircleModel::output_shapes); + circle_model.def("save", py::overload_cast(&CircleModel::save), py::arg("stream")); + circle_model.def("save", py::overload_cast(&CircleModel::save), py::arg("output_path")); py::class_ model_editor(m, "ModelEditor"); model_editor.doc() = "circle_resizer::ModelEditor"; - model_editor.def(py::init>(), py::arg("model_data")); + model_editor.def(py::init>(), py::arg("circle_model")); model_editor.def("resize_inputs", &ModelEditor::resize_inputs, py::arg("shapes")); } diff --git a/compiler/circle-resizer/src/CMakeLists.txt b/compiler/circle-resizer/src/CMakeLists.txt index 1e006b9e200..a9c0c068410 100644 --- a/compiler/circle-resizer/src/CMakeLists.txt +++ b/compiler/circle-resizer/src/CMakeLists.txt @@ -1,7 +1,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 ModelData.cpp) +list(APPEND CIRCLE_RESIZER_SOURCES CircleModel.cpp) list(APPEND CIRCLE_RESIZER_SOURCES ModelEditor.cpp) add_library(circle_resizer_core SHARED "${CIRCLE_RESIZER_SOURCES}") diff --git a/compiler/circle-resizer/src/ModelData.cpp b/compiler/circle-resizer/src/CircleModel.cpp similarity index 70% rename from compiler/circle-resizer/src/ModelData.cpp rename to compiler/circle-resizer/src/CircleModel.cpp index ad9c7690415..73826d93e73 100644 --- a/compiler/circle-resizer/src/ModelData.cpp +++ b/compiler/circle-resizer/src/CircleModel.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ModelData.h" +#include "CircleModel.h" #include @@ -23,12 +23,12 @@ #include #include -#include using namespace circle_resizer; namespace { + std::vector read_model(const std::string &model_path) { std::ifstream file_stream(model_path, std::ios::in | std::ios::binary | std::ifstream::ate); @@ -83,7 +83,7 @@ class BufferModelContract : public luci::CircleExporter::Contract private: luci::Module *_module; - std::unique_ptr> _buffer; + std::unique_ptr> _buffer; // note that the store method has to be const }; template @@ -113,68 +113,49 @@ std::vector extract_shapes(const std::vector &nodes) } // namespace -ModelData::ModelData(const std::vector &buffer) - : _buffer{buffer}, _module{load_module(buffer)} +CircleModel::CircleModel(const std::vector &buffer) + : _module{load_module(buffer)} { } -ModelData::ModelData(const std::string &model_path) : ModelData(read_model(model_path)) {} - -void ModelData::invalidate_module() { _module_invalidated = true; } - -void ModelData::invalidate_buffer() { _buffer_invalidated = true; } +CircleModel::CircleModel(const std::string &model_path) : CircleModel(read_model(model_path)) {} -std::vector &ModelData::buffer() +luci::Module *CircleModel::module() { - if (_buffer_invalidated) - { - luci::CircleExporter exporter; - BufferModelContract contract(module()); - - if (!exporter.invoke(&contract)) - { - throw std::runtime_error("Exporting buffer from the model failed"); - } - _buffer = contract.get_buffer(); - _buffer_invalidated = false; - } - return _buffer; + return _module.get(); } -luci::Module *ModelData::module() +void CircleModel::save(std::ostream &stream) { - if (_module_invalidated) + BufferModelContract contract(module()); + luci::CircleExporter exporter; + if (!exporter.invoke(&contract)) { - _module = load_module(_buffer); - _module_invalidated = false; + throw std::runtime_error("Exporting buffer from the model failed"); } - return _module.get(); -} -void ModelData::save(std::ostream &stream) -{ - auto &buff = buffer(); - stream.write(reinterpret_cast(buff.data()), buff.size()); + auto model_buffer = contract.get_buffer(); + stream.write(reinterpret_cast(model_buffer.data()), model_buffer.size()); if (!stream.good()) { throw std::runtime_error("Failed to write to output stream"); } } -void ModelData::save(const std::string &output_path) +void CircleModel::save(const std::string &output_path) { std::ofstream out_stream(output_path, std::ios::out | std::ios::binary); save(out_stream); } -std::vector ModelData::input_shapes() +std::vector CircleModel::input_shapes() const { - return extract_shapes(loco::input_nodes(module()->graph())); + return extract_shapes(loco::input_nodes(_module->graph())); } -std::vector ModelData::output_shapes() +std::vector CircleModel::output_shapes() const { - return extract_shapes(loco::output_nodes(module()->graph())); + return extract_shapes(loco::output_nodes(_module->graph())); } -ModelData::~ModelData() = default; +CircleModel::~CircleModel() = default; diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index 912f1865a66..973eb01ba13 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -18,78 +18,62 @@ #include +#include #include +#include +#include #include #include -#include -#include - -#include -#include using namespace circle_resizer; namespace { -void replace_tensor_shape(::flatbuffers::Vector *tensor_shape, const Shape &new_shape) + +void change_single_input_shape(luci::CircleInput* circle_input, const Shape &new_shape) { - const auto shape_size = tensor_shape->size(); - if (shape_size != new_shape.rank()) + 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())); + } + } +} + +bool 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("Provided shape rank: " + std::to_string(new_shape.rank()) + - " is different from expected: " + std::to_string(shape_size)); + return false; } - for (uint32_t dim_idx = 0; dim_idx < shape_size; ++dim_idx) + for(size_t in_idx=0; in_idxMutate(dim_idx, new_shape[dim_idx].value()); + auto circle_input = loco::must_cast(graph_inputs[in_idx]); + change_single_input_shape(circle_input, new_inputs_shapes[in_idx]); } + return true; } } // namespace -ModelEditor::ModelEditor(std::shared_ptr model_data) : _model_data{model_data} {} +ModelEditor::ModelEditor(std::shared_ptr circle_model) : _circle_model{circle_model} {} -ModelEditor &ModelEditor::resize_inputs(const std::vector &shapes) +ModelEditor &ModelEditor::resize_inputs(const std::vector &new_inputs_shapes) { - auto model = circle::GetMutableModel(_model_data->buffer().data()); - if (!model) - { - throw std::runtime_error("Incorrect model format"); - } - auto subgraphs = model->mutable_subgraphs(); - if (!subgraphs || subgraphs->size() != 1) - { - throw std::runtime_error("Many subgraphs are not supported"); - } - auto subgraph = subgraphs->GetMutableObject(0); - const auto inputs_number = subgraph->inputs()->size(); - if (inputs_number != shapes.size()) - { - throw std::runtime_error("Expected input shapes: " + std::to_string(inputs_number) + - " while provided: " + std::to_string(shapes.size())); - } - for (int in_idx = 0; in_idx < inputs_number; ++in_idx) - { - const auto in_tensor_idx = subgraph->inputs()->Get(in_idx); - auto input_shape = - subgraph->mutable_tensors()->GetMutableObject(in_tensor_idx)->mutable_shape(); - replace_tensor_shape(input_shape, shapes[in_idx]); - } - - // invalidate after changing input shape - _model_data->invalidate_module(); + 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()); - auto graph = _model_data->module()->graph(); logo::PhaseRunner phase_runner{graph}; phase_runner.run(phase); - // invalidate after shape inference - _model_data->invalidate_buffer(); - return *this; } diff --git a/compiler/circle-resizer/tests/CMakeLists.txt b/compiler/circle-resizer/tests/CMakeLists.txt index 93f2161fe76..aa4cb60442d 100644 --- a/compiler/circle-resizer/tests/CMakeLists.txt +++ b/compiler/circle-resizer/tests/CMakeLists.txt @@ -4,15 +4,13 @@ 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 ModelData.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}) target_link_libraries(circle_resizer_unit_test circle_resizer_core) target_link_libraries(circle_resizer_unit_test oops) -target_link_libraries(circle_resizer_unit_test mio_circle08) -target_link_libraries(circle_resizer_unit_test luci_lang) get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) set_tests_properties(circle_resizer_unit_test diff --git a/compiler/circle-resizer/tests/CircleModel.test.cpp b/compiler/circle-resizer/tests/CircleModel.test.cpp new file mode 100644 index 00000000000..e803de31fb9 --- /dev/null +++ b/compiler/circle-resizer/tests/CircleModel.test.cpp @@ -0,0 +1,135 @@ +/* + * 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 "CircleModel.h" + +#include +#include + +#include + +using namespace circle_resizer; +using ::testing::HasSubstr; + +namespace +{ + +bool compare_shapes(const std::vector ¤t, const std::vector &expected) +{ + if (current.size() != expected.size()) + { + return false; + } + for (size_t i = 0; i < current.size(); ++i) + { + if (!(current[i] == expected[i])) + { + return false; + } + } + return true; +} + +} // namespace + +class CircleModelTest : 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(CircleModelTest, proper_input_output_shapes) +{ + CircleModel circle_model(_test_models_dir + "/Add_000.circle"); + EXPECT_TRUE(compare_shapes(circle_model.input_shapes(), + std::vector{Shape{1, 4, 4, 3}, Shape{1, 4, 4, 3}})); + EXPECT_TRUE(compare_shapes(circle_model.output_shapes(), std::vector{Shape{1, 4, 4, 3}})); +} + +TEST_F(CircleModelTest, proper_output_stream) +{ + CircleModel circle_model(_test_models_dir + "/Add_000.circle"); + std::stringstream out_stream; + circle_model.save(out_stream); + out_stream.seekg(0, std::ios::end); + EXPECT_TRUE(out_stream.tellg() > 0); +} + +TEST_F(CircleModelTest, model_file_not_exist_NEG) +{ + auto file_name = "/not_existed.circle"; + try + { + CircleModel circle_model(file_name); + FAIL() << "Expected std::runtime_error"; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Failed to open file")); + EXPECT_THAT(err.what(), HasSubstr(file_name)); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(CircleModelTest, invalid_model_NEG) +{ + try + { + CircleModel(std::vector{1, 2, 3, 4, 5}); + FAIL() << "Expected std::runtime_error"; + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Verification of the model failed")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} + +TEST_F(CircleModelTest, incorrect_output_stream_NEG) +{ + auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); + std::ofstream out_stream; + try + { + circle_model->save(out_stream); + } + catch (const std::runtime_error &err) + { + EXPECT_THAT(err.what(), HasSubstr("Failed to write to output stream")); + } + catch (...) + { + FAIL() << "Expected std::runtime_error, other exception thrown"; + } +} diff --git a/compiler/circle-resizer/tests/ModelData.test.cpp b/compiler/circle-resizer/tests/ModelData.test.cpp deleted file mode 100644 index 1392ab99af4..00000000000 --- a/compiler/circle-resizer/tests/ModelData.test.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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 "ModelData.h" - -#include "luci/IR/Module.h" -#include "loco/IR/Graph.h" - -#include -#include - -#include - -#include - -using namespace circle_resizer; -using ::testing::HasSubstr; - -namespace -{ - -bool compare_shapes(const std::vector ¤t, const std::vector &expected) -{ - if (current.size() != expected.size()) - { - return false; - } - for (size_t i = 0; i < current.size(); ++i) - { - if (!(current[i] == expected[i])) - { - return false; - } - } - return true; -} - -std::string extract_subgraph_name(const std::vector &buffer) -{ - auto model = circle::GetModel(buffer.data()); - if (model) - { - auto subgraphs = model->subgraphs(); - if (subgraphs->size() > 0) - { - auto subgraph = subgraphs->Get(0); - if (subgraph->name()->c_str()) - { - return subgraph->name()->c_str(); - } - } - } - return ""; -} - -// change the first subgraph name using buffer as an input -bool change_subgraph_name(std::vector &buffer, const std::string &name) -{ - auto model = circle::GetMutableModel(buffer.data()); - if (!model) - { - return false; - } - auto subgraphs = model->mutable_subgraphs(); - auto subgraph = subgraphs->GetMutableObject(0); - if (subgraph->name()->size() != name.size()) - { - return false; - } - for (size_t i = 0; i < name.size(); ++i) - { - subgraph->mutable_name()->Mutate(i, name[i]); - } - return true; -} - -// change the first subgraph name using loco::Graph as an input -void change_subgraph_name(loco::Graph *graph, const std::string &name) { graph->name(name); } - -} // namespace - -class ModelDataTest : 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(ModelDataTest, proper_input_output_shapes) -{ - ModelData model_data(_test_models_dir + "/Add_000.circle"); - EXPECT_TRUE(compare_shapes(model_data.input_shapes(), - std::vector{Shape{1, 4, 4, 3}, Shape{1, 4, 4, 3}})); - EXPECT_TRUE(compare_shapes(model_data.output_shapes(), std::vector{Shape{1, 4, 4, 3}})); -} - -TEST_F(ModelDataTest, proper_output_stream) -{ - ModelData model_data(_test_models_dir + "/Add_000.circle"); - std::stringstream out_stream; - model_data.save(out_stream); - out_stream.seekg(0, std::ios::end); - EXPECT_TRUE(out_stream.tellg() > 0); -} - -TEST_F(ModelDataTest, invalidate_module) -{ - ModelData model_data(_test_models_dir + "/Add_000.circle"); - const auto module_before_name_change = model_data.module(); - const std::string new_subgraph_name = "abcd"; - ASSERT_TRUE(change_subgraph_name(model_data.buffer(), new_subgraph_name)); - model_data.invalidate_module(); // after buffer representation change the module is outdated - const auto module_after_name_change = model_data.module(); - EXPECT_EQ(module_after_name_change->graph()->name(), - new_subgraph_name); // check if buffer update applied to the module -} - -TEST_F(ModelDataTest, invalidate_buffer) -{ - ModelData model_data(_test_models_dir + "/Add_000.circle"); - const auto buffer_before_name_change = model_data.buffer(); - const std::string new_subgraph_name = "abcd"; - change_subgraph_name(model_data.module()->graph(), new_subgraph_name); - model_data.invalidate_buffer(); // after module representation change the buffer is outdated - const auto buffer_after_name_change = model_data.buffer(); - EXPECT_EQ(extract_subgraph_name(buffer_after_name_change), - new_subgraph_name); // check if module update applied to the buffer -} - -TEST_F(ModelDataTest, model_file_not_exist_NEG) -{ - auto file_name = "/not_existed.circle"; - try - { - ModelData model_data(file_name); - FAIL() << "Expected std::runtime_error"; - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Failed to open file")); - EXPECT_THAT(err.what(), HasSubstr(file_name)); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} - -TEST_F(ModelDataTest, invalid_model_NEG) -{ - try - { - ModelData(std::vector{1, 2, 3, 4, 5}); - FAIL() << "Expected std::runtime_error"; - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Verification of the model failed")); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} - -TEST_F(ModelDataTest, incorrect_output_stream_NEG) -{ - auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); - std::ofstream out_stream; - try - { - model_data->save(out_stream); - } - catch (const std::runtime_error &err) - { - EXPECT_THAT(err.what(), HasSubstr("Failed to write to output stream")); - } - catch (...) - { - FAIL() << "Expected std::runtime_error, other exception thrown"; - } -} diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp index c79b6db9aa7..3d89b440be0 100644 --- a/compiler/circle-resizer/tests/ModelEditor.test.cpp +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -48,56 +48,56 @@ class ModelEditorTest : public ::testing::Test TEST_F(ModelEditorTest, single_input_single_output) { - auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); - ModelEditor editor(model_data); + 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{6}}}; editor.resize_inputs(new_input_shapes); - EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); } TEST_F(ModelEditorTest, single_input_two_outputs) { - auto model_data = std::make_shared(_test_models_dir + "/CSE_Quantize_000.circle"); - ModelEditor editor(model_data); + 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{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; editor.resize_inputs(new_input_shapes); - EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); } TEST_F(ModelEditorTest, two_inputs_single_output) { - auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); - ModelEditor editor(model_data); + 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{1}, Dim{5}, Dim{5}, Dim{3}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; editor.resize_inputs(new_input_shapes); - EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); } TEST_F(ModelEditorTest, two_inputs_two_outputs) { - auto model_data = - std::make_shared(_test_models_dir + "/Part_Add_Sqrt_Rsqrt_000.circle"); - ModelEditor editor(model_data); + 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{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; editor.resize_inputs(new_input_shapes); - EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); } TEST_F(ModelEditorTest, neg_not_all_input_shapes_provided) { - auto model_data = std::make_shared(_test_models_dir + "/Add_000.circle"); - ModelEditor editor(model_data); + auto circle_model = std::make_shared(_test_models_dir + "/Add_000.circle"); + ModelEditor editor(circle_model); try { editor.resize_inputs(std::vector{Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}); @@ -114,8 +114,8 @@ TEST_F(ModelEditorTest, neg_not_all_input_shapes_provided) TEST_F(ModelEditorTest, neg_incorrect_rank_of_new_shape) { - auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); - ModelEditor editor(model_data); + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(circle_model); try { editor.resize_inputs(std::vector{Shape{Dim{3}}}); @@ -130,53 +130,44 @@ TEST_F(ModelEditorTest, neg_incorrect_rank_of_new_shape) } } -TEST_F(ModelEditorTest, neg_shape_inference_failed) -{ - auto model_data = std::make_shared(_test_models_dir + "/DepthwiseConv2D_000.circle"); - ModelEditor editor(model_data); - EXPECT_THROW(editor.resize_inputs(std::vector{Shape{Dim{1}, Dim{64}, Dim{64}, Dim{8}}, - Shape{Dim{1}, Dim{2}, Dim{2}, Dim{3}}}), - oops::UserExn); -} - TEST_F(ModelEditorTest, save_without_change) { - auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); - ModelEditor editor(model_data); + auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); + ModelEditor editor(circle_model); std::stringstream out_stream; - model_data->save(out_stream); + 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 model_data_2 = std::make_shared(model_buffer); - ModelEditor editor_2(model_data_2); - EXPECT_EQ(model_data_2->input_shapes(), (std::vector{Shape{Dim{3}, Dim{3}}})); - EXPECT_EQ(model_data_2->output_shapes(), (std::vector{Shape{Dim{3}, Dim{1}, Dim{3}}})); + auto circle_model_2 = std::make_shared(model_buffer); + ModelEditor editor_2(circle_model_2); + EXPECT_EQ(circle_model_2->input_shapes(), (std::vector{Shape{Dim{3}, Dim{3}}})); + EXPECT_EQ(circle_model_2->output_shapes(), (std::vector{Shape{Dim{3}, Dim{1}, Dim{3}}})); } TEST_F(ModelEditorTest, save_after_resizing) { - auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); - ModelEditor editor(model_data); + 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{Dim{4}, Dim{6}}}; editor.resize_inputs(new_input_shapes); - model_data->save(out_stream); + 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 model_data_2 = std::make_shared(model_buffer); - ModelEditor editor_2(model_data_2); - EXPECT_EQ(model_data_2->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data_2->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); + auto circle_model_2 = std::make_shared(model_buffer); + ModelEditor editor_2(circle_model_2); + EXPECT_EQ(circle_model_2->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model_2->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); } TEST_F(ModelEditorTest, single_input_single_output_double_resizing) { - auto model_data = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); - ModelEditor editor(model_data); + 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{6}}}; editor.resize_inputs(std::vector{Shape{Dim{6}, Dim{8}}}).resize_inputs(new_input_shapes); - EXPECT_EQ(model_data->input_shapes(), new_input_shapes); - EXPECT_EQ(model_data->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); + EXPECT_EQ(circle_model->input_shapes(), new_input_shapes); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{6}}})); } From 01263bb5b572e8267a8f31fdb74e7d981c8bfb06 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 17 Apr 2025 22:24:33 +0200 Subject: [PATCH 29/39] styles applied --- .../python/src/CircleResizerBindings.cpp | 5 +++-- compiler/circle-resizer/src/CircleModel.cpp | 10 ++-------- compiler/circle-resizer/src/ModelEditor.cpp | 17 ++++++++++------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp index 332b3eed7fd..119b5c79b30 100644 --- a/compiler/circle-resizer/python/src/CircleResizerBindings.cpp +++ b/compiler/circle-resizer/python/src/CircleResizerBindings.cpp @@ -80,9 +80,10 @@ PYBIND11_MODULE(circle_resizer_python_api, m) circle_model.def(py::init(), py::arg("model_path")); circle_model.def("input_shapes", &CircleModel::input_shapes); circle_model.def("output_shapes", &CircleModel::output_shapes); - circle_model.def("save", py::overload_cast(&CircleModel::save), py::arg("stream")); + circle_model.def("save", py::overload_cast(&CircleModel::save), + py::arg("stream")); circle_model.def("save", py::overload_cast(&CircleModel::save), - py::arg("output_path")); + py::arg("output_path")); py::class_ model_editor(m, "ModelEditor"); model_editor.doc() = "circle_resizer::ModelEditor"; diff --git a/compiler/circle-resizer/src/CircleModel.cpp b/compiler/circle-resizer/src/CircleModel.cpp index 73826d93e73..f964a025730 100644 --- a/compiler/circle-resizer/src/CircleModel.cpp +++ b/compiler/circle-resizer/src/CircleModel.cpp @@ -113,17 +113,11 @@ std::vector extract_shapes(const std::vector &nodes) } // namespace -CircleModel::CircleModel(const std::vector &buffer) - : _module{load_module(buffer)} -{ -} +CircleModel::CircleModel(const std::vector &buffer) : _module{load_module(buffer)} {} CircleModel::CircleModel(const std::string &model_path) : CircleModel(read_model(model_path)) {} -luci::Module *CircleModel::module() -{ - return _module.get(); -} +luci::Module *CircleModel::module() { return _module.get(); } void CircleModel::save(std::ostream &stream) { diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index 973eb01ba13..ca5c7430c65 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -30,14 +30,17 @@ using namespace circle_resizer; namespace { -void change_single_input_shape(luci::CircleInput* circle_input, const Shape &new_shape) +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()) + 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 { + } + else + { circle_input->dim(i) = loco::Dimension(static_cast(new_shape[i].value())); } } @@ -46,13 +49,13 @@ void change_single_input_shape(luci::CircleInput* circle_input, const Shape &new bool 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()) + if (graph_inputs.size() != new_inputs_shapes.size()) { return false; } - for(size_t in_idx=0; in_idx(graph_inputs[in_idx]); + auto circle_input = loco::must_cast(graph_inputs[in_idx]); change_single_input_shape(circle_input, new_inputs_shapes[in_idx]); } return true; From f12471bd7fdd4b37c720e31901307cfc7d8e0bf4 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 23 Apr 2025 13:14:10 +0200 Subject: [PATCH 30/39] add circle-resizer-dredd-recipe-test --- .../CMakeLists.txt | 117 ++++++++++++++++++ .../README.md | 31 +++++ .../requires.cmake | 5 + .../circle-resizer-dredd-recipe-test/test.lst | 19 +++ .../testall.sh | 99 +++++++++++++++ .../GreaterEqual_000/test.rule | 7 ++ .../Inf_StridedSlice_002/test.recipe | 47 +++++++ .../Inf_StridedSlice_002/test.rule | 6 + .../Net_FullyConnected_Gelu_000/test.rule | 6 + res/TensorFlowLiteRecipes/PRelu_000/test.rule | 7 ++ .../ReduceAny_dynamic_000/test.rule | 6 + res/TensorFlowLiteRecipes/Split_000/test.rule | 7 ++ 12 files changed, 357 insertions(+) create mode 100644 compiler/circle-resizer-dredd-recipe-test/CMakeLists.txt create mode 100644 compiler/circle-resizer-dredd-recipe-test/README.md create mode 100644 compiler/circle-resizer-dredd-recipe-test/requires.cmake create mode 100644 compiler/circle-resizer-dredd-recipe-test/test.lst create mode 100755 compiler/circle-resizer-dredd-recipe-test/testall.sh create mode 100644 res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule create mode 100644 res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/PRelu_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule create mode 100644 res/TensorFlowLiteRecipes/Split_000/test.rule diff --git a/compiler/circle-resizer-dredd-recipe-test/CMakeLists.txt b/compiler/circle-resizer-dredd-recipe-test/CMakeLists.txt new file mode 100644 index 00000000000..cd80070cc13 --- /dev/null +++ b/compiler/circle-resizer-dredd-recipe-test/CMakeLists.txt @@ -0,0 +1,117 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_include(TargetRequire) + +unset(REQUIRED_TARGETS) +list(APPEND REQUIRED_TARGETS circle-inspect) +list(APPEND REQUIRED_TARGETS circle-verify) +list(APPEND REQUIRED_TARGETS circle_resizer) +list(APPEND REQUIRED_TARGETS dredd_rule_lib) +TargetRequire_Return(${REQUIRED_TARGETS}) + +unset(TEST_DEPS) +unset(TEST_NAMES) + +get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) + +set(oneValueArgs NEW_INPUTS_SIZES) +set(multiValueArgs "") + +macro(Add RECIPE) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(NEW_INPUTS_SIZES "") + if(ARG_NEW_INPUTS_SIZES) + set(NEW_INPUTS_SIZES "--input_shapes" "${ARG_NEW_INPUTS_SIZES}") + endif() + + set(CIRCLE_PATH "${ARTIFACTS_BIN_PATH}/${RECIPE}.circle") + set(RESIZED_CIRCLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE}.resized.circle") + + # Generate resized .circle + add_custom_command(OUTPUT ${RESIZED_CIRCLE_PATH} + COMMAND $ --input_path ${CIRCLE_PATH} --output_path ${RESIZED_CIRCLE_PATH} ${NEW_INPUTS_SIZES} + DEPENDS + circle_resizer + ${CIRCLE_PATH} + COMMENT "Generate ${RECIPE}.resized.circle" + ) + + list(APPEND TEST_DEPS ${RESIZED_CIRCLE_PATH}) + list(APPEND TEST_NAMES ${RECIPE}) +endmacro(Add) + + +# Read "test.lst" +include("test.lst") + +## +## Copy testall +## +set(TEST_RUNNER "${CMAKE_CURRENT_BINARY_DIR}/testall.sh") +set(TEST_RUNNER_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/testall.sh") + +add_custom_command( + OUTPUT ${TEST_RUNNER} + COMMAND ${CMAKE_COMMAND} -E copy "${TEST_RUNNER_SOURCE}" "${TEST_RUNNER}" + DEPENDS ${TEST_RUNNER_SOURCE} + COMMENT "Generate test runner" +) + +list(APPEND TEST_DEPS "${TEST_RUNNER}") + +### +### Generate test.config +### +set(TEST_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/test.config") + +add_custom_command( + OUTPUT ${TEST_CONFIG} + COMMAND ${CMAKE_COMMAND} -E remove -f ${TEST_CONFIG} + COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_INSPECT_PATH=\"$\"' >> ${TEST_CONFIG} + COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_VERIFY_PATH=\"$\"' >> ${TEST_CONFIG} + COMMAND ${CMAKE_COMMAND} -E echo 'CIRCLE_RESIZER_PATH=\"$\"' >> ${TEST_CONFIG} + DEPENDS + circle-inspect + circle-verify + circle_resizer + COMMENT "Generate test configuration" +) + +list(APPEND TEST_DEPS "${TEST_CONFIG}") + +# +# copy rule-lib.sh (a library of shell script functions) +# + +# getting path for rule-lib.sh in dredd-rule-lib +get_target_property(DREDD_RULE_LIB_DIR dredd_rule_lib BINARY_DIR) + +set(RULE_LIB_SOURCE_PATH "${DREDD_RULE_LIB_DIR}/rule-lib.sh") +set(RULE_LIB_BINARY_PATH "${CMAKE_CURRENT_BINARY_DIR}/rule-lib.sh") + +add_custom_command( + OUTPUT ${RULE_LIB_BINARY_PATH} + COMMAND ${CMAKE_COMMAND} -E copy "${RULE_LIB_SOURCE_PATH}" "${RULE_LIB_BINARY_PATH}" + DEPENDS ${RULE_LIB_SOURCE_PATH} + COMMENT "Generate rule lib" +) + +list(APPEND TEST_DEPS "${RULE_LIB_BINARY_PATH}") + +# Generate dependencies +add_custom_target(circle-resizer_dredd_recipe_test ALL DEPENDS ${TEST_DEPS}) +add_dependencies(circle-resizer_dredd_recipe_test common_artifacts_deps) + +get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) + +# Run tests +add_test( + NAME resizer_dredd_recipe_test + COMMAND "${TEST_RUNNER}" + "${TEST_CONFIG}" + "${ARTIFACTS_BIN_PATH}" + ${TEST_NAMES} +) diff --git a/compiler/circle-resizer-dredd-recipe-test/README.md b/compiler/circle-resizer-dredd-recipe-test/README.md new file mode 100644 index 00000000000..ea65d7319ee --- /dev/null +++ b/compiler/circle-resizer-dredd-recipe-test/README.md @@ -0,0 +1,31 @@ +# circle-resizer-dredd-recipe-test + +It tests non-functional conditions of a circle model resized by circle-resizer. + +## How to add a test? + +1. Create a directory under `res/TensorFlowLiteRecipes/` or `res/CircleRecipes/`. + +2. Make a recipe (`test.recipe`) for a model under the directory. + +3. Make a rule (`test.rule`) you want to test under the directory. Note, that you can find more information about dredd-test-rules in _dredd-rule-lib_ module. + +4. Add a test to `test.lst` in this module using `Add` macro. + ``` + Add(RECIPE_DIR NEW_INPUTS_SIZES) + ``` + - `NEW_INPUTS_SIZES`: New shapes of Circle model inputs in comma-separated format like `[1,2,3],[4,5]` for a model with 2 inputs. + +## Example + +``` +# TensorFlowLiteRecipes +res/TensorFlowLiteRecipes/PRelu_000 +├── test.recipe # What you want to test +└── test.rule # Non-functional conditions to be satisfied + +# test.lst +... +Add(PRelu_000 NEW_INPUTS_SIZES [1,4,4,5],[1,1,5]) +... +``` diff --git a/compiler/circle-resizer-dredd-recipe-test/requires.cmake b/compiler/circle-resizer-dredd-recipe-test/requires.cmake new file mode 100644 index 00000000000..d5e55f70dc6 --- /dev/null +++ b/compiler/circle-resizer-dredd-recipe-test/requires.cmake @@ -0,0 +1,5 @@ +require("circle-inspect") +require("circle-resizer") +require("circle-verify") +require("common-artifacts") +require("dredd-rule-lib") diff --git a/compiler/circle-resizer-dredd-recipe-test/test.lst b/compiler/circle-resizer-dredd-recipe-test/test.lst new file mode 100644 index 00000000000..b18f91cd296 --- /dev/null +++ b/compiler/circle-resizer-dredd-recipe-test/test.lst @@ -0,0 +1,19 @@ +## EXAMPLE +# +# Add(RECIPE_DIR NEW_INPUTS_SIZES [1,2,3]) +# + +## TFLITE RECIPE + +# two inputs, one output +Add(PRelu_000 NEW_INPUTS_SIZES [1,4,4,5],[1,1,5]) +# scalar output +Add(ReduceAny_dynamic_000 NEW_INPUTS_SIZES [4,5,6]) +# change rank +Add(GreaterEqual_000 NEW_INPUTS_SIZES [1,2,3],[1,2,3]) +# one inputs, two outputs +Add(Split_000 NEW_INPUTS_SIZES [8,1,2]) +# bigger graph +Add(Net_FullyConnected_Gelu_000 NEW_INPUTS_SIZES [2,16]) +# from dynamic to static +Add(Inf_StridedSlice_002 NEW_INPUTS_SIZES [1,10,10,5]) diff --git a/compiler/circle-resizer-dredd-recipe-test/testall.sh b/compiler/circle-resizer-dredd-recipe-test/testall.sh new file mode 100755 index 00000000000..8883895fd04 --- /dev/null +++ b/compiler/circle-resizer-dredd-recipe-test/testall.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +# Need at least 2 arguments +if [[ $# -lt 2 ]]; then + echo "USAGE: $0 ..." + echo + echo "ARGUMENTS:" + echo " [test.config path]" + echo " [WORKDIR]" + echo " [Prefix1]" + echo " [Prefix2]" + echo " ..." + exit 255 +fi + +WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +CONFIG_PATH="$1"; shift +RESOURCE_DIR="$1"; shift + +source "${CONFIG_PATH}" + +echo "-- Found circle-resizer: ${CIRCLE_RESIZER_PATH}" +echo "-- Found circle-inspect: ${CIRCLE_INSPECT_PATH}" +echo "-- Found circle-verify: ${CIRCLE_VERIFY_PATH}" +echo "-- Found common-artifacts: ${RESOURCE_DIR}" + +TESTED=() +PASSED=() +FAILED=() + +pushd ${WORKDIR} +while [[ $# -ne 0 ]]; do + PREFIX="$1"; shift + + TESTED+=("${PREFIX}") + + PASSED_TAG="${PREFIX}.passed" + + rm -f "${PASSED_TAG}" + + cat > "${PREFIX}.log" <( + exec 2>&1 + + echo "-- Found circle: ${PREFIX}.resized.circle" + + # Exit immediately if any command fails + set -e + # Show commands + set -x + + # + # Check if rule is satisfied + # + + # Note: turn off 'command printing'. Otherwise printing will be so messy + set +x + + # (RESIZED_FILE, INSPECT_PROG_PATH, VERIFY_PROG_PATH, ERROR_LOG) must be set for rule-lib.sh + RESIZED_FILE="${PREFIX}.resized.circle" + INSPECT_PROG_PATH=${CIRCLE_INSPECT_PATH} + VERIFY_PROG_PATH=${CIRCLE_VERIFY_PATH} + ERROR_LOG="${PREFIX}.error" + + rm -f "${ERROR_LOG}" + + # in case error while running rule-lib.sh, prints error msg + trap 'echo "** ERROR **" ; cat "${ERROR_LOG}"' ERR + + source rule-lib.sh + source "${RESOURCE_DIR}/${PREFIX}.rule" + + # unset + trap - ERR + set -x + + # At this point, the exit code of all commands is 0 + # If not 0, execution of this script ends because of "set -e" + touch "${PASSED_TAG}" + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("$PREFIX") + else + FAILED+=("$PREFIX") + fi +done +popd + +if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then + echo "FAILED" + for TEST in "${FAILED[@]}" + do + echo "- ${TEST}" + done + exit 255 +fi + +echo "PASSED" +exit 0 diff --git a/res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule b/res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule new file mode 100644 index 00000000000..6e11af8f7cc --- /dev/null +++ b/res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule @@ -0,0 +1,7 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM1_SHAPE" $(tensor_shape ifm1) '=' [1,2,3] +RULE "IFM2_SHAPE" $(tensor_shape ifm2) '=' [1,2,3] +RULE "GREATER_EQUAL_SHAPE" $(tensor_shape ofm) '=' [1,2,3] diff --git a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe new file mode 100644 index 00000000000..9db0593c066 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe @@ -0,0 +1,47 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 0 dim: 0 dim: 5 } + shape_signature { dim: 1 dim: -1 dim: -1 dim: 5 } +} +operand { + name: "begin" + type: INT32 + shape { dim: 4 } + filler { tag: "explicit" arg: "0" arg: "0" arg: "0" arg: "0" } +} +operand { + name: "end" + type: INT32 + shape { dim: 4 } + filler { tag: "explicit" arg: "1" arg: "8" arg: "3" arg: "5" } +} +operand { + name: "strides" + type: INT32 + shape { dim: 4 } + filler { tag: "explicit" arg: "1" arg: "1" arg: "1" arg: "1" } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 0 dim: 0 dim: 5 } + shape_signature { dim: 1 dim: -1 dim: -1 dim: 5 } +} +operation { + type: "StridedSlice" + input: "ifm" + input: "begin" + input: "end" + input: "strides" + output: "ofm" + strided_slice_options { + begin_mask: 0 + end_mask: 0 + ellipsis_mask: 0 + new_axis_mask: 0 + shrink_axis_mask: 0 + } +} +input: "ifm" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule new file mode 100644 index 00000000000..eca45dc33f0 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule @@ -0,0 +1,6 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [1,10,10,5] +RULE "STRIDED_SLICE_SHAPE" $(tensor_shape ofm) '=' [1,8,3,5] diff --git a/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule new file mode 100644 index 00000000000..05ecff0e7c1 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule @@ -0,0 +1,6 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IN_SHAPE" $(tensor_shape in) '=' [2,16] +RULE "OUT_2_SHAPE" $(tensor_shape out_2) '=' [2,4] diff --git a/res/TensorFlowLiteRecipes/PRelu_000/test.rule b/res/TensorFlowLiteRecipes/PRelu_000/test.rule new file mode 100644 index 00000000000..39eacbd0a36 --- /dev/null +++ b/res/TensorFlowLiteRecipes/PRelu_000/test.rule @@ -0,0 +1,7 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM1_SHAPE" $(tensor_shape ifm1) '=' [1,4,4,5] +RULE "IFM2_SHAPE" $(tensor_shape ifm2) '=' [1,1,5] +RULE "PRELU_SHAPE" $(tensor_shape ofm) '=' [1,4,4,5] diff --git a/res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule new file mode 100644 index 00000000000..052a5727e75 --- /dev/null +++ b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule @@ -0,0 +1,6 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [4,5,6] +RULE "REDUCE_ANY_SHAPE" $(tensor_shape ofm) '=' [] diff --git a/res/TensorFlowLiteRecipes/Split_000/test.rule b/res/TensorFlowLiteRecipes/Split_000/test.rule new file mode 100644 index 00000000000..2f62e5e387c --- /dev/null +++ b/res/TensorFlowLiteRecipes/Split_000/test.rule @@ -0,0 +1,7 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [8,1,2] +RULE "OFM1_SHAPE" $(tensor_shape ofm1) '=' [4,1,2] +RULE "OFM2_SHAPE" $(tensor_shape ofm2) '=' [4,1,2] From a020acae1b2d336233c1870f61f0e5048a276875 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Wed, 23 Apr 2025 16:36:08 +0200 Subject: [PATCH 31/39] missing change in testall.sh (align to rule-lib.sh names) --- compiler/circle-resizer-dredd-recipe-test/testall.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/circle-resizer-dredd-recipe-test/testall.sh b/compiler/circle-resizer-dredd-recipe-test/testall.sh index 8883895fd04..18f47137c13 100755 --- a/compiler/circle-resizer-dredd-recipe-test/testall.sh +++ b/compiler/circle-resizer-dredd-recipe-test/testall.sh @@ -55,8 +55,8 @@ while [[ $# -ne 0 ]]; do # Note: turn off 'command printing'. Otherwise printing will be so messy set +x - # (RESIZED_FILE, INSPECT_PROG_PATH, VERIFY_PROG_PATH, ERROR_LOG) must be set for rule-lib.sh - RESIZED_FILE="${PREFIX}.resized.circle" + # (COMPILED_FILE, INSPECT_PROG_PATH, VERIFY_PROG_PATH, ERROR_LOG) must be set for rule-lib.sh + COMPILED_FILE="${PREFIX}.resized.circle" INSPECT_PROG_PATH=${CIRCLE_INSPECT_PATH} VERIFY_PROG_PATH=${CIRCLE_VERIFY_PATH} ERROR_LOG="${PREFIX}.error" From d469df45be22ffdddf4184ca5dd44a4e72657ff5 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 24 Apr 2025 12:47:12 +0200 Subject: [PATCH 32/39] added more tests --- compiler/circle-resizer/src/ModelEditor.cpp | 14 +- compiler/circle-resizer/tests/CMakeLists.txt | 1 - .../circle-resizer/tests/CircleModel.test.cpp | 1 + .../circle-resizer/tests/ModelEditor.test.cpp | 132 ++++++++++-------- 4 files changed, 81 insertions(+), 67 deletions(-) diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index ca5c7430c65..4127cea381f 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -46,19 +46,18 @@ void change_single_input_shape(luci::CircleInput *circle_input, const Shape &new } } -bool change_inputs_shapes(loco::Graph *graph, const std::vector &new_inputs_shapes) +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()) { - return false; + 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]); } - return true; } } // namespace @@ -76,7 +75,14 @@ ModelEditor &ModelEditor::resize_inputs(const std::vector &new_inputs_sha phase.emplace_back(std::make_unique()); logo::PhaseRunner phase_runner{graph}; - phase_runner.run(phase); + 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 aa4cb60442d..3337718cc34 100644 --- a/compiler/circle-resizer/tests/CMakeLists.txt +++ b/compiler/circle-resizer/tests/CMakeLists.txt @@ -10,7 +10,6 @@ list(APPEND CIRCLE_RESIZER_TEST_SOURCES ModelEditor.test.cpp) nnas_find_package(GTest REQUIRED) GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES}) target_link_libraries(circle_resizer_unit_test circle_resizer_core) -target_link_libraries(circle_resizer_unit_test oops) get_target_property(ARTIFACTS_PATH testDataGenerator BINARY_DIR) set_tests_properties(circle_resizer_unit_test diff --git a/compiler/circle-resizer/tests/CircleModel.test.cpp b/compiler/circle-resizer/tests/CircleModel.test.cpp index e803de31fb9..b48cdc4a163 100644 --- a/compiler/circle-resizer/tests/CircleModel.test.cpp +++ b/compiler/circle-resizer/tests/CircleModel.test.cpp @@ -123,6 +123,7 @@ TEST_F(CircleModelTest, incorrect_output_stream_NEG) try { circle_model->save(out_stream); + FAIL() << "Expected exception."; } catch (const std::runtime_error &err) { diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp index 3d89b440be0..ae47843f6db 100644 --- a/compiler/circle-resizer/tests/ModelEditor.test.cpp +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -15,7 +15,6 @@ */ #include "ModelEditor.h" -#include "oops/UserExn.h" #include #include @@ -50,34 +49,34 @@ 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{Dim{4}, Dim{6}}}; + 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{Dim{4}, Dim{1}, Dim{6}}})); + 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{Dim{1}, Dim{6}, Dim{6}, Dim{4}}}; + 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{Dim{1}, Dim{6}, Dim{6}, Dim{4}}, - Shape{Dim{1}, Dim{6}, Dim{6}, Dim{4}}})); + (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{Dim{1}, Dim{5}, Dim{5}, Dim{3}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}; + 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{Dim{1}, Dim{5}, Dim{5}, Dim{3}}})); + (std::vector{Shape{1, 5, 5, 3}})); } TEST_F(ModelEditorTest, two_inputs_two_outputs) @@ -85,26 +84,75 @@ 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{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}}; + 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{Dim{1}, Dim{5}, Dim{5}, Dim{2}}, - Shape{Dim{1}, Dim{5}, Dim{5}, Dim{2}}})); + (std::vector{Shape{1, 5, 5, 2}, + Shape{1, 5, 5, 2}})); } -TEST_F(ModelEditorTest, neg_not_all_input_shapes_provided) +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{Dim{1}, Dim{5}, Dim{5}, Dim{3}}}); + 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 input shapes: 2 while provided: 1")); + EXPECT_THAT(err.what(), HasSubstr("Expected 2 shapes but provided only 1")); } catch (...) { @@ -112,62 +160,22 @@ TEST_F(ModelEditorTest, neg_not_all_input_shapes_provided) } } -TEST_F(ModelEditorTest, neg_incorrect_rank_of_new_shape) +TEST_F(ModelEditorTest, exception_during_shape_inference_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); try { - editor.resize_inputs(std::vector{Shape{Dim{3}}}); + 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("Provided shape rank: 1 is different from expected: 2")); + EXPECT_THAT(err.what(), HasSubstr("Exception during shape inference with message:")); } catch (...) { FAIL() << "Expected std::runtime_error, other exception thrown"; } } - -TEST_F(ModelEditorTest, save_without_change) -{ - auto circle_model = std::make_shared(_test_models_dir + "/ExpandDims_000.circle"); - ModelEditor editor(circle_model); - std::stringstream out_stream; - 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_2 = std::make_shared(model_buffer); - ModelEditor editor_2(circle_model_2); - EXPECT_EQ(circle_model_2->input_shapes(), (std::vector{Shape{Dim{3}, Dim{3}}})); - EXPECT_EQ(circle_model_2->output_shapes(), (std::vector{Shape{Dim{3}, Dim{1}, Dim{3}}})); -} - -TEST_F(ModelEditorTest, save_after_resizing) -{ - 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{Dim{4}, Dim{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_2 = std::make_shared(model_buffer); - ModelEditor editor_2(circle_model_2); - EXPECT_EQ(circle_model_2->input_shapes(), new_input_shapes); - EXPECT_EQ(circle_model_2->output_shapes(), (std::vector{Shape{Dim{4}, Dim{1}, Dim{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{Dim{4}, Dim{6}}}; - editor.resize_inputs(std::vector{Shape{Dim{6}, Dim{8}}}).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{6}}})); -} From c9229a584044b99ed6c3f9ce907f643d18898a4b Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 24 Apr 2025 13:22:05 +0200 Subject: [PATCH 33/39] added model editor doc --- compiler/circle-resizer/include/ModelEditor.h | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/compiler/circle-resizer/include/ModelEditor.h b/compiler/circle-resizer/include/ModelEditor.h index fbfb5e1e66f..6dff1196639 100644 --- a/compiler/circle-resizer/include/ModelEditor.h +++ b/compiler/circle-resizer/include/ModelEditor.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef __CIRCLE_RESIZER_H__ -#define __CIRCLE_RESIZER_H__ +#ifndef __CIRCLE_RESIZER_MODEL_EDITOR_H__ +#define __CIRCLE_RESIZER_MODEL_EDITOR_H__ #include "Shape.h" #include "CircleModel.h" @@ -26,12 +26,29 @@ 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: @@ -39,4 +56,4 @@ class ModelEditor }; } // namespace circle_resizer -#endif // __CIRCLE_RESIZER_H__ +#endif // __CIRCLE_RESIZER_MODEL_EDITOR_H__ From 3d43630c93314ca650d172cff351be4a68ad48c3 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 24 Apr 2025 13:22:41 +0200 Subject: [PATCH 34/39] styles applied --- compiler/circle-resizer/include/ModelEditor.h | 4 ++-- compiler/circle-resizer/src/ModelEditor.cpp | 9 +++++--- .../circle-resizer/tests/ModelEditor.test.cpp | 21 +++++++------------ 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/circle-resizer/include/ModelEditor.h b/compiler/circle-resizer/include/ModelEditor.h index 6dff1196639..684f75d7132 100644 --- a/compiler/circle-resizer/include/ModelEditor.h +++ b/compiler/circle-resizer/include/ModelEditor.h @@ -39,9 +39,8 @@ class ModelEditor explicit ModelEditor(std::shared_ptr circle_model); public: - /** - * @brief Resize the model. In means changing shape of the inputs + * @brief Resize the model. In means changing shape of the inputs * and propagating changes through the graph. * * Exceptions: @@ -54,6 +53,7 @@ class ModelEditor private: std::shared_ptr _circle_model; }; + } // namespace circle_resizer #endif // __CIRCLE_RESIZER_MODEL_EDITOR_H__ diff --git a/compiler/circle-resizer/src/ModelEditor.cpp b/compiler/circle-resizer/src/ModelEditor.cpp index 4127cea381f..f632e85213e 100644 --- a/compiler/circle-resizer/src/ModelEditor.cpp +++ b/compiler/circle-resizer/src/ModelEditor.cpp @@ -51,7 +51,9 @@ void change_inputs_shapes(loco::Graph *graph, const std::vector &new_inpu 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())); + 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) { @@ -79,9 +81,10 @@ ModelEditor &ModelEditor::resize_inputs(const std::vector &new_inputs_sha { phase_runner.run(phase); } - catch(const std::exception& e) + catch (const std::exception &e) { - throw std::runtime_error("Exception during shape inference with message: " + std::string{e.what()}); + throw std::runtime_error("Exception during shape inference with message: " + + std::string{e.what()}); } return *this; diff --git a/compiler/circle-resizer/tests/ModelEditor.test.cpp b/compiler/circle-resizer/tests/ModelEditor.test.cpp index ae47843f6db..303682c791f 100644 --- a/compiler/circle-resizer/tests/ModelEditor.test.cpp +++ b/compiler/circle-resizer/tests/ModelEditor.test.cpp @@ -63,20 +63,17 @@ TEST_F(ModelEditorTest, single_input_two_outputs) 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}})); + (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}}; + 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}})); + EXPECT_EQ(circle_model->output_shapes(), (std::vector{Shape{1, 5, 5, 3}})); } TEST_F(ModelEditorTest, two_inputs_two_outputs) @@ -84,13 +81,11 @@ 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}}; + 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}})); + (std::vector{Shape{1, 5, 5, 2}, Shape{1, 5, 5, 2}})); } TEST_F(ModelEditorTest, resize_applied_after_save) @@ -138,7 +133,8 @@ TEST_F(ModelEditorTest, resize_to_dynamic) 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()}})); + EXPECT_EQ(circle_model->output_shapes(), + (std::vector{Shape{Dim{4}, Dim{1}, Dim::dynamic()}})); } TEST_F(ModelEditorTest, not_all_input_shapes_provided_NEG) @@ -166,8 +162,7 @@ TEST_F(ModelEditorTest, exception_during_shape_inference_NEG) ModelEditor editor(circle_model); try { - editor.resize_inputs(std::vector{Shape{1, 2, 3}, - Shape{4, 5, 6}}); + 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) From 9b0437bbcbc222fab6cf70ab8b271fcf08708e90 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Mon, 28 Apr 2025 10:04:59 +0200 Subject: [PATCH 35/39] resizer cmd app refactoring --- compiler/circle-resizer/app/CircleResizer.cpp | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/compiler/circle-resizer/app/CircleResizer.cpp b/compiler/circle-resizer/app/CircleResizer.cpp index 855f288eb81..62a69bb0585 100644 --- a/compiler/circle-resizer/app/CircleResizer.cpp +++ b/compiler/circle-resizer/app/CircleResizer.cpp @@ -21,9 +21,8 @@ #include #include -#include -#include #include +#include using namespace circle_resizer; @@ -36,6 +35,14 @@ void print_version() std::cout << vconone::get_copyright() << std::endl; } +void list_shapes(const std::vector &shapes) +{ + for (size_t idx = 0; idx < shapes.size(); ++idx) + { + std::cout << idx << " -> " << shapes[idx] << std::endl; + } +} + } // namespace int entry(const int argc, char **argv) @@ -46,19 +53,19 @@ int entry(const int argc, char **argv) .nargs(1) .type(arser::DataType::STR) .required(true) - .help("The path to the input model (.circle)"); + .help("Path to the input model (.circle)"); arser.add_argument("--output_path") .nargs(1) .type(arser::DataType::STR) .required(true) - .help("The path to the resized model (.circle)"); + .help("Path to the resized model (.circle)"); arser.add_argument("--input_shapes") .nargs(1) .type(arser::DataType::STR) .required(true) - .help("New inputs shapes in in comma separated format. An example for 2 inputs: [1,2],[3,4]."); + .help("New inputs shapes in the comma separated format. An example for 2 inputs: [1,2],[3,4]."); arser.add_argument("--version") .nargs(0) @@ -75,38 +82,29 @@ int entry(const int argc, char **argv) auto circle_model = std::make_shared(input_path); ModelEditor resizer(circle_model); - const auto input_shapes = circle_model->input_shapes(); - std::cout << "Input shapes before resizing:" << std::endl; - for (size_t in_idx = 0; in_idx < input_shapes.size(); ++in_idx) - { - std::cout << in_idx + 1 << ". " << input_shapes[in_idx] << std::endl; - } - - auto output_shapes = circle_model->output_shapes(); - std::cout << "Output shapes before resizing:" << std::endl; - for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) - { - std::cout << out_idx + 1 << ". " << output_shapes[out_idx] << std::endl; - } + + std::cout << "Shapes of inputs before resizing:" << std::endl; + list_shapes(circle_model->input_shapes()); + + std::cout << "Shapes of outputs before resizing:" << std::endl; + list_shapes(circle_model->output_shapes()); const auto output_path = arser.get("--output_path"); const auto new_input_shapes_str = arser.get("--input_shapes"); - resizer.resize_inputs(parse_shapes(new_input_shapes_str)); - output_shapes = circle_model->output_shapes(); - std::cout << "Output shapes after resizing:" << std::endl; - for (size_t out_idx = 0; out_idx < output_shapes.size(); ++out_idx) - { - std::cout << out_idx + 1 << ". " << output_shapes[out_idx] << std::endl; - } + std::cout << "Shapes of inputs after resizing:" << std::endl; + list_shapes(circle_model->input_shapes()); + + std::cout << "Shapes of outputs after resizing:" << std::endl; + list_shapes(circle_model->output_shapes()); circle_model->save(output_path); std::cout << "Resizing complete, the model saved to: " << output_path << std::endl; } catch (const std::runtime_error &err) { - std::cerr << err.what() << std::endl; + std::cerr << "Exception during resizing: " << err.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; From b1b9811e34db996306a567afcc8369a0a41c9f20 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Mon, 28 Apr 2025 11:22:09 +0200 Subject: [PATCH 36/39] readme update --- compiler/circle-resizer/README | 8 -------- compiler/circle-resizer/README.md | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 9 deletions(-) delete mode 100644 compiler/circle-resizer/README diff --git a/compiler/circle-resizer/README b/compiler/circle-resizer/README deleted file mode 100644 index 2235b6c6b25..00000000000 --- a/compiler/circle-resizer/README +++ /dev/null @@ -1,8 +0,0 @@ -# circle-resizer - -_circle-resizer provides capabilities to change the shapes of models inputs. - -## How to build Python API -```py -CMAKE_INSTALL_PREFIX=~/workspace/one_install_nncc python setup.py bdist_wheel -``` diff --git a/compiler/circle-resizer/README.md b/compiler/circle-resizer/README.md index b762b97b948..67b8fac4a73 100644 --- a/compiler/circle-resizer/README.md +++ b/compiler/circle-resizer/README.md @@ -1,3 +1,19 @@ # circle-resizer -_circle-resizer_ provides capabilities to change the shapes of models inputs. +_circle-resizer_ is a command-line tool designed to change the shapes of models inputs in `.circle` format. + +The basic syntax of `circle-resizer` is: +```bash +./circle-resizer --input_path --output_path --input_shapes +``` + +## Arguments: +- `--input_path`: Path to the input .circle model file (required). +- `--output_path`: Path to save the resized .circle model (required). +- `--input_shapes`: Comma-separated list of new input shapes in the format [dim1,dim2,...]. Example for two inputs: [1,2,3],[4,5] (required). +- `--version`: Display version information and exit. + +## Example Command +```bash +./circle-resizer --input_path model.circle --output_path resized_model.circle --input_shapes [1,3,224,224],[10] +``` From 8a455331a8d800deead0aa68b0c8964eb5a0dc31 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 8 May 2025 11:43:47 +0200 Subject: [PATCH 37/39] remove previous recipes --- .../GreaterEqual_000/test.rule | 7 --- .../Inf_StridedSlice_002/test.recipe | 47 ------------------- .../Inf_StridedSlice_002/test.rule | 6 --- .../Net_FullyConnected_Gelu_000/test.rule | 6 --- res/TensorFlowLiteRecipes/PRelu_000/test.rule | 7 --- .../ReduceAny_dynamic_000/test.rule | 6 --- res/TensorFlowLiteRecipes/Split_000/test.rule | 7 --- 7 files changed, 86 deletions(-) delete mode 100644 res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule delete mode 100644 res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe delete mode 100644 res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule delete mode 100644 res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule delete mode 100644 res/TensorFlowLiteRecipes/PRelu_000/test.rule delete mode 100644 res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule delete mode 100644 res/TensorFlowLiteRecipes/Split_000/test.rule diff --git a/res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule b/res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule deleted file mode 100644 index 6e11af8f7cc..00000000000 --- a/res/TensorFlowLiteRecipes/GreaterEqual_000/test.rule +++ /dev/null @@ -1,7 +0,0 @@ -# To check if the model is resized properly - -RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 - -RULE "IFM1_SHAPE" $(tensor_shape ifm1) '=' [1,2,3] -RULE "IFM2_SHAPE" $(tensor_shape ifm2) '=' [1,2,3] -RULE "GREATER_EQUAL_SHAPE" $(tensor_shape ofm) '=' [1,2,3] diff --git a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe deleted file mode 100644 index 9db0593c066..00000000000 --- a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe +++ /dev/null @@ -1,47 +0,0 @@ -operand { - name: "ifm" - type: FLOAT32 - shape { dim: 1 dim: 0 dim: 0 dim: 5 } - shape_signature { dim: 1 dim: -1 dim: -1 dim: 5 } -} -operand { - name: "begin" - type: INT32 - shape { dim: 4 } - filler { tag: "explicit" arg: "0" arg: "0" arg: "0" arg: "0" } -} -operand { - name: "end" - type: INT32 - shape { dim: 4 } - filler { tag: "explicit" arg: "1" arg: "8" arg: "3" arg: "5" } -} -operand { - name: "strides" - type: INT32 - shape { dim: 4 } - filler { tag: "explicit" arg: "1" arg: "1" arg: "1" arg: "1" } -} -operand { - name: "ofm" - type: FLOAT32 - shape { dim: 1 dim: 0 dim: 0 dim: 5 } - shape_signature { dim: 1 dim: -1 dim: -1 dim: 5 } -} -operation { - type: "StridedSlice" - input: "ifm" - input: "begin" - input: "end" - input: "strides" - output: "ofm" - strided_slice_options { - begin_mask: 0 - end_mask: 0 - ellipsis_mask: 0 - new_axis_mask: 0 - shrink_axis_mask: 0 - } -} -input: "ifm" -output: "ofm" diff --git a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule deleted file mode 100644 index eca45dc33f0..00000000000 --- a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule +++ /dev/null @@ -1,6 +0,0 @@ -# To check if the model is resized properly - -RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 - -RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [1,10,10,5] -RULE "STRIDED_SLICE_SHAPE" $(tensor_shape ofm) '=' [1,8,3,5] diff --git a/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule deleted file mode 100644 index 05ecff0e7c1..00000000000 --- a/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_000/test.rule +++ /dev/null @@ -1,6 +0,0 @@ -# To check if the model is resized properly - -RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 - -RULE "IN_SHAPE" $(tensor_shape in) '=' [2,16] -RULE "OUT_2_SHAPE" $(tensor_shape out_2) '=' [2,4] diff --git a/res/TensorFlowLiteRecipes/PRelu_000/test.rule b/res/TensorFlowLiteRecipes/PRelu_000/test.rule deleted file mode 100644 index 39eacbd0a36..00000000000 --- a/res/TensorFlowLiteRecipes/PRelu_000/test.rule +++ /dev/null @@ -1,7 +0,0 @@ -# To check if the model is resized properly - -RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 - -RULE "IFM1_SHAPE" $(tensor_shape ifm1) '=' [1,4,4,5] -RULE "IFM2_SHAPE" $(tensor_shape ifm2) '=' [1,1,5] -RULE "PRELU_SHAPE" $(tensor_shape ofm) '=' [1,4,4,5] diff --git a/res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule deleted file mode 100644 index 052a5727e75..00000000000 --- a/res/TensorFlowLiteRecipes/ReduceAny_dynamic_000/test.rule +++ /dev/null @@ -1,6 +0,0 @@ -# To check if the model is resized properly - -RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 - -RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [4,5,6] -RULE "REDUCE_ANY_SHAPE" $(tensor_shape ofm) '=' [] diff --git a/res/TensorFlowLiteRecipes/Split_000/test.rule b/res/TensorFlowLiteRecipes/Split_000/test.rule deleted file mode 100644 index 2f62e5e387c..00000000000 --- a/res/TensorFlowLiteRecipes/Split_000/test.rule +++ /dev/null @@ -1,7 +0,0 @@ -# To check if the model is resized properly - -RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 - -RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [8,1,2] -RULE "OFM1_SHAPE" $(tensor_shape ofm1) '=' [4,1,2] -RULE "OFM2_SHAPE" $(tensor_shape ofm2) '=' [4,1,2] From 82a859a72331992fa5b48b49be37376a14422f81 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 8 May 2025 11:44:56 +0200 Subject: [PATCH 38/39] added new recipes --- compiler/common-artifacts/exclude.lst | 1 + .../GreaterEqual_001/test.recipe | 26 ++++++ .../GreaterEqual_001/test.reverse | 0 .../GreaterEqual_001/test.rule | 7 ++ .../Inf_StridedSlice_002/test.recipe | 47 ++++++++++ .../Inf_StridedSlice_002/test.rule | 6 ++ .../Net_FullyConnected_Gelu_001/test.recipe | 90 +++++++++++++++++++ .../Net_FullyConnected_Gelu_001/test.reverse | 0 .../Net_FullyConnected_Gelu_001/test.rule | 6 ++ .../PRelu_002/test.recipe | 24 +++++ .../PRelu_002/test.reverse | 0 res/TensorFlowLiteRecipes/PRelu_002/test.rule | 7 ++ .../ReduceAny_dynamic_004/test.recipe | 31 +++++++ .../ReduceAny_dynamic_004/test.reverse | 0 .../ReduceAny_dynamic_004/test.rule | 6 ++ .../Split_001/test.recipe | 34 +++++++ .../Split_001/test.reverse | 0 res/TensorFlowLiteRecipes/Split_001/test.rule | 7 ++ 18 files changed, 292 insertions(+) create mode 100644 res/TensorFlowLiteRecipes/GreaterEqual_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/GreaterEqual_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/GreaterEqual_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule create mode 100644 res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.rule create mode 100644 res/TensorFlowLiteRecipes/PRelu_002/test.recipe create mode 100644 res/TensorFlowLiteRecipes/PRelu_002/test.reverse create mode 100644 res/TensorFlowLiteRecipes/PRelu_002/test.rule create mode 100644 res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.recipe create mode 100644 res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.reverse create mode 100644 res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.rule create mode 100644 res/TensorFlowLiteRecipes/Split_001/test.recipe create mode 100644 res/TensorFlowLiteRecipes/Split_001/test.reverse create mode 100644 res/TensorFlowLiteRecipes/Split_001/test.rule diff --git a/compiler/common-artifacts/exclude.lst b/compiler/common-artifacts/exclude.lst index 4f1611bd01a..845e0b09422 100644 --- a/compiler/common-artifacts/exclude.lst +++ b/compiler/common-artifacts/exclude.lst @@ -110,6 +110,7 @@ tcgenerate(ReduceAny_dynamic_000) # TestDataGenerator does not support unknown d tcgenerate(ReduceAny_dynamic_001) # TestDataGenerator does not support unknown dimension tcgenerate(ReduceAny_dynamic_002) # TestDataGenerator does not support unknown dimension tcgenerate(ReduceAny_dynamic_003) # TestDataGenerator does not support unknown dimension +tcgenerate(ReduceAny_dynamic_004) # TestDataGenerator does not support unknown dimension tcgenerate(ReduceMax_000) tcgenerate(ReduceMax_dynamic_000) # TestDataGenerator does not support unknown dimension tcgenerate(ReduceMin_000) diff --git a/res/TensorFlowLiteRecipes/GreaterEqual_001/test.recipe b/res/TensorFlowLiteRecipes/GreaterEqual_001/test.recipe new file mode 100644 index 00000000000..bae45f8b71c --- /dev/null +++ b/res/TensorFlowLiteRecipes/GreaterEqual_001/test.recipe @@ -0,0 +1,26 @@ +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operand { + name: "ofm" + type: BOOL + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operation { + type: "GreaterEqual" + greaterequal_options { + } + input: "ifm1" + input: "ifm2" + output: "ofm" +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/GreaterEqual_001/test.reverse b/res/TensorFlowLiteRecipes/GreaterEqual_001/test.reverse new file mode 100644 index 00000000000..e69de29bb2d diff --git a/res/TensorFlowLiteRecipes/GreaterEqual_001/test.rule b/res/TensorFlowLiteRecipes/GreaterEqual_001/test.rule new file mode 100644 index 00000000000..6e11af8f7cc --- /dev/null +++ b/res/TensorFlowLiteRecipes/GreaterEqual_001/test.rule @@ -0,0 +1,7 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM1_SHAPE" $(tensor_shape ifm1) '=' [1,2,3] +RULE "IFM2_SHAPE" $(tensor_shape ifm2) '=' [1,2,3] +RULE "GREATER_EQUAL_SHAPE" $(tensor_shape ofm) '=' [1,2,3] diff --git a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe new file mode 100644 index 00000000000..9db0593c066 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.recipe @@ -0,0 +1,47 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 0 dim: 0 dim: 5 } + shape_signature { dim: 1 dim: -1 dim: -1 dim: 5 } +} +operand { + name: "begin" + type: INT32 + shape { dim: 4 } + filler { tag: "explicit" arg: "0" arg: "0" arg: "0" arg: "0" } +} +operand { + name: "end" + type: INT32 + shape { dim: 4 } + filler { tag: "explicit" arg: "1" arg: "8" arg: "3" arg: "5" } +} +operand { + name: "strides" + type: INT32 + shape { dim: 4 } + filler { tag: "explicit" arg: "1" arg: "1" arg: "1" arg: "1" } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 0 dim: 0 dim: 5 } + shape_signature { dim: 1 dim: -1 dim: -1 dim: 5 } +} +operation { + type: "StridedSlice" + input: "ifm" + input: "begin" + input: "end" + input: "strides" + output: "ofm" + strided_slice_options { + begin_mask: 0 + end_mask: 0 + ellipsis_mask: 0 + new_axis_mask: 0 + shrink_axis_mask: 0 + } +} +input: "ifm" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule new file mode 100644 index 00000000000..eca45dc33f0 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Inf_StridedSlice_002/test.rule @@ -0,0 +1,6 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [1,10,10,5] +RULE "STRIDED_SLICE_SHAPE" $(tensor_shape ofm) '=' [1,8,3,5] diff --git a/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.recipe b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.recipe new file mode 100644 index 00000000000..0e47be64b46 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.recipe @@ -0,0 +1,90 @@ +operand { + name: "in" + type: FLOAT32 + shape { dim: 1 dim: 16 } +} +operand { + name: "weight" + type: FLOAT32 + shape { dim: 16 dim: 16 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 16 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "out" + type: FLOAT32 + shape { dim: 1 dim: 16 } +} +operation { + type: "FullyConnected" + fullyconnected_options { + activation: RELU + } + input: "in" + input: "weight" + input: "bias" + output: "out" +} +operand { + name: "gelu_out" + type: FLOAT32 + shape { dim: 1 dim: 16 } +} +operation { + type: "Gelu" + gelu_options { + approximate: false + } + input: "out" + output: "gelu_out" +} +operand { + name: "weight_2" + type: FLOAT32 + shape { dim: 4 dim: 16 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias_2" + type: FLOAT32 + shape { dim: 4 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "out_2" + type: FLOAT32 + shape { dim: 1 dim: 4 } +} +operation { + type: "FullyConnected" + fullyconnected_options { + activation: RELU + } + input: "gelu_out" + input: "weight_2" + input: "bias_2" + output: "out_2" +} +input: "in" +output: "out_2" diff --git a/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.reverse b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.reverse new file mode 100644 index 00000000000..e69de29bb2d diff --git a/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.rule b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.rule new file mode 100644 index 00000000000..05ecff0e7c1 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Net_FullyConnected_Gelu_001/test.rule @@ -0,0 +1,6 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IN_SHAPE" $(tensor_shape in) '=' [2,16] +RULE "OUT_2_SHAPE" $(tensor_shape out_2) '=' [2,4] diff --git a/res/TensorFlowLiteRecipes/PRelu_002/test.recipe b/res/TensorFlowLiteRecipes/PRelu_002/test.recipe new file mode 100644 index 00000000000..35f88c16c6c --- /dev/null +++ b/res/TensorFlowLiteRecipes/PRelu_002/test.recipe @@ -0,0 +1,24 @@ +operand { + name: "ifm1" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operand { + name: "ifm2" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 3 } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operation { + type: "PRelu" + input: "ifm1" + input: "ifm2" + output: "ofm" +} +input: "ifm1" +input: "ifm2" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/PRelu_002/test.reverse b/res/TensorFlowLiteRecipes/PRelu_002/test.reverse new file mode 100644 index 00000000000..e69de29bb2d diff --git a/res/TensorFlowLiteRecipes/PRelu_002/test.rule b/res/TensorFlowLiteRecipes/PRelu_002/test.rule new file mode 100644 index 00000000000..39eacbd0a36 --- /dev/null +++ b/res/TensorFlowLiteRecipes/PRelu_002/test.rule @@ -0,0 +1,7 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM1_SHAPE" $(tensor_shape ifm1) '=' [1,4,4,5] +RULE "IFM2_SHAPE" $(tensor_shape ifm2) '=' [1,1,5] +RULE "PRELU_SHAPE" $(tensor_shape ofm) '=' [1,4,4,5] diff --git a/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.recipe b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.recipe new file mode 100644 index 00000000000..427bd05f17b --- /dev/null +++ b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.recipe @@ -0,0 +1,31 @@ +operand { + name: "ifm" + type: BOOL + shape { dim: 1 dim: 3 dim: 4 } + shape_signature { dim: -1 dim: 3 dim: 4 } +} +operand { + name: "reduction_indices" + type: INT32 + shape { dim: 3 } + filler { + tag: "explicit" + arg: "0" arg: "1" arg: "2" + } +} +operand { + name: "ofm" + type: BOOL + shape { } +} +operation { + type: "ReduceAny" + reduce_any_options { + keep_dims: false + } + input: "ifm" + input: "reduction_indices" + output: "ofm" +} +input: "ifm" +output: "ofm" diff --git a/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.reverse b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.reverse new file mode 100644 index 00000000000..e69de29bb2d diff --git a/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.rule b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.rule new file mode 100644 index 00000000000..052a5727e75 --- /dev/null +++ b/res/TensorFlowLiteRecipes/ReduceAny_dynamic_004/test.rule @@ -0,0 +1,6 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [4,5,6] +RULE "REDUCE_ANY_SHAPE" $(tensor_shape ofm) '=' [] diff --git a/res/TensorFlowLiteRecipes/Split_001/test.recipe b/res/TensorFlowLiteRecipes/Split_001/test.recipe new file mode 100644 index 00000000000..e4bbe7620e1 --- /dev/null +++ b/res/TensorFlowLiteRecipes/Split_001/test.recipe @@ -0,0 +1,34 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 6 dim: 1 dim: 2 } +} +operand { + name: "split_dim" + type: INT32 + shape { } + filler { tag: "explicit" arg: "0" } +} +operand { + name: "ofm1" + type: FLOAT32 + shape { dim: 3 dim: 1 dim: 2 } +} +operand { + name: "ofm2" + type: FLOAT32 + shape { dim: 3 dim: 1 dim: 2 } +} +operation { + type: "Split" + split_options { + num_splits: 2 + } + input: "split_dim" + input: "ifm" + output: "ofm1" + output: "ofm2" +} +input: "ifm" +output: "ofm1" +output: "ofm2" diff --git a/res/TensorFlowLiteRecipes/Split_001/test.reverse b/res/TensorFlowLiteRecipes/Split_001/test.reverse new file mode 100644 index 00000000000..e69de29bb2d diff --git a/res/TensorFlowLiteRecipes/Split_001/test.rule b/res/TensorFlowLiteRecipes/Split_001/test.rule new file mode 100644 index 00000000000..2f62e5e387c --- /dev/null +++ b/res/TensorFlowLiteRecipes/Split_001/test.rule @@ -0,0 +1,7 @@ +# To check if the model is resized properly + +RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1 + +RULE "IFM_SHAPE" $(tensor_shape ifm) '=' [8,1,2] +RULE "OFM1_SHAPE" $(tensor_shape ofm1) '=' [4,1,2] +RULE "OFM2_SHAPE" $(tensor_shape ofm2) '=' [4,1,2] From b3781dfa13905220ae22d7eb0c09327d07c10e18 Mon Sep 17 00:00:00 2001 From: Mateusz Bencer Date: Thu, 8 May 2025 12:41:35 +0200 Subject: [PATCH 39/39] update test.lst --- compiler/circle-resizer-dredd-recipe-test/test.lst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/circle-resizer-dredd-recipe-test/test.lst b/compiler/circle-resizer-dredd-recipe-test/test.lst index b18f91cd296..3326ce0993c 100644 --- a/compiler/circle-resizer-dredd-recipe-test/test.lst +++ b/compiler/circle-resizer-dredd-recipe-test/test.lst @@ -6,14 +6,14 @@ ## TFLITE RECIPE # two inputs, one output -Add(PRelu_000 NEW_INPUTS_SIZES [1,4,4,5],[1,1,5]) +Add(PRelu_002 NEW_INPUTS_SIZES [1,4,4,5],[1,1,5]) # scalar output -Add(ReduceAny_dynamic_000 NEW_INPUTS_SIZES [4,5,6]) +Add(ReduceAny_dynamic_004 NEW_INPUTS_SIZES [4,5,6]) # change rank -Add(GreaterEqual_000 NEW_INPUTS_SIZES [1,2,3],[1,2,3]) +Add(GreaterEqual_001 NEW_INPUTS_SIZES [1,2,3],[1,2,3]) # one inputs, two outputs -Add(Split_000 NEW_INPUTS_SIZES [8,1,2]) +Add(Split_001 NEW_INPUTS_SIZES [8,1,2]) # bigger graph -Add(Net_FullyConnected_Gelu_000 NEW_INPUTS_SIZES [2,16]) +Add(Net_FullyConnected_Gelu_001 NEW_INPUTS_SIZES [2,16]) # from dynamic to static Add(Inf_StridedSlice_002 NEW_INPUTS_SIZES [1,10,10,5])