diff options
Diffstat (limited to 'compiler')
1670 files changed, 42525 insertions, 9752 deletions
diff --git a/compiler/.ahub/tcchecker-tca/config.yaml b/compiler/.ahub/tcchecker-tca/config.yaml index ef681de1a..9d3e6b8eb 100644 --- a/compiler/.ahub/tcchecker-tca/config.yaml +++ b/compiler/.ahub/tcchecker-tca/config.yaml @@ -8,6 +8,7 @@ test: - ./arser - ./circle2circle - ./circle-quantizer + - ./crew - ./cwrap - ./foder - ./hermes @@ -19,6 +20,8 @@ test: - ./logo-core - ./luci - ./luci-interpreter + - ./luci-eval-driver + - ./luci-pass-value-test - ./luci-value-test - ./mio-circle - ./mio-tflite @@ -30,7 +33,6 @@ test: - ./record-minmax - ./safemain - ./souschef - - ./stdex - ./tflite2circle testFile: diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Overlay.h b/compiler/angkor/include/nncc/core/ADT/feature/Overlay.h index 93d86f56b..0af13c56a 100644 --- a/compiler/angkor/include/nncc/core/ADT/feature/Overlay.h +++ b/compiler/angkor/include/nncc/core/ADT/feature/Overlay.h @@ -34,7 +34,7 @@ template <typename T> class Overlay final : public View<T> { public: explicit Overlay(const Shape &shape, const Layout &layout, T *base) - : View<T>{shape, layout}, _base{base} + : View<T>{shape, layout}, _base{base} { // DO NOTHING } diff --git a/compiler/angkor/include/nncc/core/ADT/feature/Shape.h b/compiler/angkor/include/nncc/core/ADT/feature/Shape.h index 319326308..7d086b9b7 100644 --- a/compiler/angkor/include/nncc/core/ADT/feature/Shape.h +++ b/compiler/angkor/include/nncc/core/ADT/feature/Shape.h @@ -35,7 +35,7 @@ class Shape { public: Shape(uint32_t depth, uint32_t height, uint32_t width) - : _depth{depth}, _height{height}, _width{width} + : _depth{depth}, _height{height}, _width{width} { // DO NOTHING } diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h b/compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h index e348a8769..0684277fa 100644 --- a/compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Overlay.h @@ -35,7 +35,7 @@ template <typename T, typename InputIt> class Overlay final : public View<T> { public: explicit Overlay(const Shape &shape, const Layout &layout, InputIt it) - : _impl{shape, layout}, _it{it} + : _impl{shape, layout}, _it{it} { // DO NOTHING } diff --git a/compiler/angkor/include/nncc/core/ADT/kernel/Shape.h b/compiler/angkor/include/nncc/core/ADT/kernel/Shape.h index d485d526b..92f90970a 100644 --- a/compiler/angkor/include/nncc/core/ADT/kernel/Shape.h +++ b/compiler/angkor/include/nncc/core/ADT/kernel/Shape.h @@ -35,7 +35,7 @@ class Shape { public: Shape(uint32_t count, uint32_t depth, uint32_t height, uint32_t width) - : _count{count}, _depth{depth}, _height{height}, _width{width} + : _count{count}, _depth{depth}, _height{height}, _width{width} { // DO NOTHING } diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h b/compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h index 11ee5350c..5fa36bbc9 100644 --- a/compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h +++ b/compiler/angkor/include/nncc/core/ADT/tensor/Overlay.h @@ -32,7 +32,7 @@ template <typename T> class Overlay final : public View<T> { public: explicit Overlay(const Shape &shape, const Layout &layout, T *base) - : View<T>{shape, layout}, _base{base} + : View<T>{shape, layout}, _base{base} { // DO NOTHING } diff --git a/compiler/angkor/include/nncc/core/ADT/tensor/View.h b/compiler/angkor/include/nncc/core/ADT/tensor/View.h index 4c9a91539..8407df3be 100644 --- a/compiler/angkor/include/nncc/core/ADT/tensor/View.h +++ b/compiler/angkor/include/nncc/core/ADT/tensor/View.h @@ -36,7 +36,7 @@ template <typename T> class View : public Reader<T>, public Accessor<T> { public: explicit View(const Shape &shape, const Layout &layout) - : _shape{shape}, _layout{std::move(layout)} + : _shape{shape}, _layout{std::move(layout)} { // DO NOTHING } diff --git a/compiler/angkor/src/ADT/feature/Overlay.test.cpp b/compiler/angkor/src/ADT/feature/Overlay.test.cpp index 8ba28bf5a..1ac62f856 100644 --- a/compiler/angkor/src/ADT/feature/Overlay.test.cpp +++ b/compiler/angkor/src/ADT/feature/Overlay.test.cpp @@ -30,7 +30,7 @@ TEST(ADT_FEATURE_OVERLAY, ctor) const Shape shape{4, 6, 3}; int data[4 * 6 * 3] = { - 0, + 0, }; auto overlay = make_overlay<int, CHWLayout>(shape, data); @@ -44,7 +44,7 @@ TEST(ADT_FEATURE_OVERLAY, read) const Shape shape{4, 6, 3}; int data[4 * 6 * 3] = { - 0, + 0, }; const auto overlay = make_overlay<int, CHWLayout>(shape, data); @@ -60,7 +60,7 @@ TEST(ADT_FEATURE_OVERLAY, access) const Shape shape{4, 6, 3}; int data[4 * 6 * 3] = { - 0, + 0, }; auto overlay = make_overlay<int, CHWLayout>(shape, data); diff --git a/compiler/angkor/src/ADT/kernel/Overlay.test.cpp b/compiler/angkor/src/ADT/kernel/Overlay.test.cpp index 4e9bd8dbd..7129fe242 100644 --- a/compiler/angkor/src/ADT/kernel/Overlay.test.cpp +++ b/compiler/angkor/src/ADT/kernel/Overlay.test.cpp @@ -30,7 +30,7 @@ TEST(ADT_KERNEL_OVERLAY, ctor) const Shape shape{2, 4, 6, 3}; int data[2 * 4 * 6 * 3] = { - 0, + 0, }; auto overlay = make_overlay<int, NCHWLayout>(shape, data); @@ -45,7 +45,7 @@ TEST(ADT_KERNEL_OVERLAY, read) const Shape shape{2, 4, 6, 3}; int data[2 * 4 * 6 * 3] = { - 0, + 0, }; const auto overlay = make_overlay<int, NCHWLayout>(shape, data); @@ -61,7 +61,7 @@ TEST(ADT_KERNEL_OVERLAY, access) const Shape shape{2, 4, 6, 3}; int data[2 * 4 * 6 * 3] = { - 0, + 0, }; auto overlay = make_overlay<int, NCHWLayout>(shape, data); diff --git a/compiler/angkor/src/ADT/tensor/Overlay.test.cpp b/compiler/angkor/src/ADT/tensor/Overlay.test.cpp index 57cd1e6f9..d5369dffc 100644 --- a/compiler/angkor/src/ADT/tensor/Overlay.test.cpp +++ b/compiler/angkor/src/ADT/tensor/Overlay.test.cpp @@ -31,7 +31,7 @@ TEST(ADT_TENSOR_OVERLAY, ctor) const Shape shape{2, 3}; int data[2 * 3] = { - 0, + 0, }; auto view = make_overlay<int, LexicalLayout>(shape, data); @@ -43,7 +43,7 @@ TEST(ADT_TENSOR_OVERLAY, read) const Shape shape{2, 3}; int data[2 * 3] = { - 0, + 0, }; const auto view = make_overlay<int, LexicalLayout>(shape, data); @@ -61,7 +61,7 @@ TEST(ADT_TENSOR_OVERLAY, access) const Shape shape{2, 3}; int data[2 * 3] = { - 0, + 0, }; auto view = make_overlay<int, LexicalLayout>(shape, data); diff --git a/compiler/arser/CMakeLists.txt b/compiler/arser/CMakeLists.txt index 63d19f538..7eda21564 100644 --- a/compiler/arser/CMakeLists.txt +++ b/compiler/arser/CMakeLists.txt @@ -4,12 +4,14 @@ add_library(arser INTERFACE) # It means that a developer who want to link arser just need to add one line. # target_link_library(another-users-target arser) target_include_directories(arser INTERFACE include/) +target_link_libraries(arser INTERFACE nncc_coverage) if(NOT ENABLE_TEST) return() endif(NOT ENABLE_TEST) nnas_find_package(GTest REQUIRED) -set(TESTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/arser.test.cpp") +set(TESTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/arser.test.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/HelpMessage.test.cpp") GTest_AddTest(arser_test ${TESTS}) -target_include_directories(arser_test PRIVATE include) +target_link_libraries(arser_test arser) diff --git a/compiler/arser/include/arser/arser.h b/compiler/arser/include/arser/arser.h index 64bb557c4..f2a7a2b85 100644 --- a/compiler/arser/include/arser/arser.h +++ b/compiler/arser/include/arser/arser.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef __ARSER_H__ +#define __ARSER_H__ + #include <iostream> #include <sstream> @@ -29,7 +32,11 @@ #include <cstring> -namespace +#include <cassert> + +namespace arser +{ +namespace internal { template <typename T> T lexical_cast(const std::string &str) @@ -41,7 +48,7 @@ template <typename T> T lexical_cast(const std::string &str) return data; } -template <> bool lexical_cast(const std::string &str) +template <> inline bool lexical_cast(const std::string &str) { bool data = true; if (str == "false" || str == "False" || str == "FALSE" || str == "0") @@ -55,7 +62,33 @@ template <> inline std::string to_string(const char *value) { return std::string template <> inline std::string to_string(const bool value) { return value ? "true" : "false"; } -} // namespace +/** + * @brief Returns the string with the leading dash removed. + * + * If there is no dash, it returns as it is. + */ +inline std::string remove_dash(const std::string &str) +{ + std::string ret{str}; + auto pos = ret.find_first_not_of('-'); + if (pos == std::string::npos) + return ret; + return ret.substr(pos); +} + +/** + * @brief Returns the string that created by concatenating the elements of a vector with commas. + */ +inline std::string make_comma_concatenated(const std::vector<std::string> &vec) +{ + std::ostringstream oss; + std::copy(vec.begin(), std::prev(vec.end()), std::ostream_iterator<std::string>(oss, ", ")); + oss << vec.back(); + return oss.str(); +} + +} // namespace internal +} // namespace arser namespace arser { @@ -116,10 +149,41 @@ enum class DataType class Arser; +/** + * Argument + * ├── positional argument + * └── optioanl argument [ dash at the beginning of the string ] + * ├── long option [ two or more dashes ] + * └── short option [ one dash ] + * + * Argument has two types - positional argument, optional argument. + * + * The way to distinguish the two types is whether there is a dash('-') at the beginning of the + * string. + * + * And, optional argument has two types as well - long option, short option, which is distinguished + * by the number of dash. + */ class Argument { public: - explicit Argument(const std::string &arg_name) : _name{arg_name} {} + explicit Argument(const std::string &arg_name) : _long_name{arg_name}, _names{arg_name} {} + explicit Argument(const std::string &short_name, const std::string &long_name) + : _short_name{short_name}, _long_name{long_name}, _names{short_name, long_name} + { + } + explicit Argument(const std::string &short_name, const std::string &long_name, + const std::vector<std::string> &names) + : _short_name{short_name}, _long_name{long_name}, _names{names} + { + // 'names' must have 'short_name' and 'long_name'. + auto it = std::find(names.begin(), names.end(), short_name); + assert(it != names.end()); + it = std::find(names.begin(), names.end(), long_name); + assert(it != names.end()); + // for avoiding unused warning. + (void)it; + } Argument &nargs(uint32_t num) { @@ -190,7 +254,7 @@ public: { if ((_nargs <= 1 && TypeName<T>::Get() == _type) || (_nargs > 1 && TypeName<std::vector<T>>::Get() == _type)) - _values.emplace_back(::to_string(value)); + _values.emplace_back(internal::to_string(value)); else { throw std::runtime_error("Type mismatch. " @@ -207,7 +271,7 @@ public: if ((_nargs <= 1 && TypeName<T>::Get() == _type) || (_nargs > 1 && TypeName<std::vector<T>>::Get() == _type)) { - _values.emplace_back(::to_string(value)); + _values.emplace_back(internal::to_string(value)); default_value(values...); } else @@ -222,7 +286,11 @@ public: } private: - std::string _name; + // The '_names' vector contains all of the options specified by the user. + // And among them, '_long_name' and '_short_name' are selected. + std::string _long_name; + std::string _short_name; + std::vector<std::string> _names; std::string _type; std::string _help_message; std::function<void(void)> _func; @@ -238,33 +306,113 @@ class Arser { public: explicit Arser(const std::string &program_description = {}) - : _program_description{program_description} + : _program_description{program_description} { - add_argument("--help").help("Show help message and exit").nargs(0); + add_argument("-h", "--help").help("Show help message and exit").nargs(0); } Argument &add_argument(const std::string &arg_name) { - if (arg_name.at(0) != '-') + if (arg_name.at(0) != '-') /* positional */ { _positional_arg_vec.emplace_back(arg_name); _arg_map[arg_name] = &_positional_arg_vec.back(); } - else + else /* optional */ { + // The length of optional argument name must be 2 or more. + // And it shouldn't be hard to recognize. e.g. '-', '--' + if (arg_name.size() < 2) + { + throw std::runtime_error("Too short name. The length of argument name must be 2 or more."); + } + if (arg_name == "--") + { + throw std::runtime_error( + "Too short name. Option name must contain at least one character other than dash."); + } _optional_arg_vec.emplace_back(arg_name); + _optional_arg_vec.back()._short_name = arg_name; _arg_map[arg_name] = &_optional_arg_vec.back(); } return *_arg_map[arg_name]; } + Argument &add_argument(const std::vector<std::string> &arg_name_vec) + { + assert(arg_name_vec.size() >= 2); + std::string long_opt, short_opt; + // find long and short option + for (const auto &arg_name : arg_name_vec) + { + if (arg_name.at(0) != '-') + { + throw std::runtime_error("Invalid argument. " + "Positional argument cannot have short option."); + } + assert(arg_name.size() >= 2); + if (long_opt.empty() && arg_name.at(0) == '-' && arg_name.at(1) == '-') + { + long_opt = arg_name; + } + if (short_opt.empty() && arg_name.at(0) == '-' && arg_name.at(1) != '-') + { + short_opt = arg_name; + } + } + // If one of the two is empty, fill it with the non-empty one for pretty printing. + if (long_opt.empty()) + { + assert(not short_opt.empty()); + long_opt = short_opt; + } + if (short_opt.empty()) + { + assert(not long_opt.empty()); + short_opt = long_opt; + } + + _optional_arg_vec.emplace_back(short_opt, long_opt, arg_name_vec); + for (const auto &arg_name : arg_name_vec) + { + _arg_map[arg_name] = &_optional_arg_vec.back(); + } + return _optional_arg_vec.back(); + } + + template <typename... Ts> Argument &add_argument(const std::string &arg_name, Ts... arg_names) + { + if (sizeof...(arg_names) == 0) + { + return add_argument(arg_name); + } + // sizeof...(arg_names) > 0 + else + { + return add_argument(std::vector<std::string>{arg_name, arg_names...}); + } + } + + void validate_arguments(void) + { + // positional argument is always required. + for (const auto &arg : _positional_arg_vec) + { + if (arg._is_required) + { + throw std::runtime_error("Invalid arguments. Positional argument must always be required."); + } + } + } + void parse(int argc, char **argv) { + validate_arguments(); _program_name = argv[0]; _program_name.erase(0, _program_name.find_last_of("/\\") + 1); if (argc >= 2) { - if (!std::strcmp(argv[1], "--help")) + if (!std::strcmp(argv[1], "--help") || !std::strcmp(argv[1], "-h")) { std::cout << *this; std::exit(0); @@ -274,7 +422,7 @@ public: for (const auto &arg : _arg_map) { const auto &func = arg.second->_func; - if (func && !std::strcmp(argv[1], arg.second->_name.c_str())) + if (func && !std::strcmp(argv[1], arg.first.c_str())) { func(); std::exit(0); @@ -354,14 +502,111 @@ public: template <typename T> T get(const std::string &arg_name); + friend std::ostream &operator<<(std::ostream &stream, const Arser &parser) + { + // print description + if (!parser._program_description.empty()) + { + stream << "What " << parser._program_name << " does: " << parser._program_description + << "\n\n"; + } + /* + ** print usage + */ + stream << "Usage: ./" << parser._program_name << " "; + // required optional argument + for (const auto &arg : parser._optional_arg_vec) + { + if (!arg._is_required) + continue; + stream << arg._short_name << " "; + std::string arg_name = arser::internal::remove_dash(arg._long_name); + std::for_each(arg_name.begin(), arg_name.end(), + [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); }); + stream << " "; + } + // rest of the optional argument + for (const auto &arg : parser._optional_arg_vec) + { + if (arg._is_required) + continue; + stream << "[" << arg._short_name; + if (arg._nargs) + { + stream << " "; + std::string arg_name = arser::internal::remove_dash(arg._long_name); + std::for_each(arg_name.begin(), arg_name.end(), + [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); }); + } + stream << "]" + << " "; + } + // positional arguement + for (const auto &arg : parser._positional_arg_vec) + { + stream << arg._long_name << " "; + } + stream << "\n\n"; + /* + ** print argument list and its help message + */ + // get the length of the longest argument + size_t length_of_longest_arg = 0; + for (const auto &arg : parser._positional_arg_vec) + { + length_of_longest_arg = std::max(length_of_longest_arg, + arser::internal::make_comma_concatenated(arg._names).size()); + } + for (const auto &arg : parser._optional_arg_vec) + { + length_of_longest_arg = std::max(length_of_longest_arg, + arser::internal::make_comma_concatenated(arg._names).size()); + } + + const size_t message_width = 60; + // positional argument + if (!parser._positional_arg_vec.empty()) + { + stream << "[Positional argument]" << std::endl; + for (const auto &arg : parser._positional_arg_vec) + { + stream.width(length_of_longest_arg); + stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t"; + for (size_t i = 0; i < arg._help_message.length(); i += message_width) + { + if (i) + stream << std::string(length_of_longest_arg, ' ') << "\t"; + stream << arg._help_message.substr(i, message_width) << std::endl; + } + } + std::cout << std::endl; + } + // optional argument + if (!parser._optional_arg_vec.empty()) + { + stream << "[Optional argument]" << std::endl; + for (const auto &arg : parser._optional_arg_vec) + { + stream.width(length_of_longest_arg); + stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t"; + for (size_t i = 0; i < arg._help_message.length(); i += message_width) + { + if (i) + stream << std::string(length_of_longest_arg, ' ') << "\t"; + stream << arg._help_message.substr(i, message_width) << std::endl; + } + } + } + + return stream; + } + private: std::string _program_name; std::string _program_description; std::list<Argument> _positional_arg_vec; std::list<Argument> _optional_arg_vec; std::map<std::string, Argument *> _arg_map; - - friend std::ostream &operator<<(std::ostream &, const Arser &); }; template <typename T> T Arser::get_impl(const std::string &arg_name, T *) @@ -369,7 +614,8 @@ template <typename T> T Arser::get_impl(const std::string &arg_name, T *) auto arg = _arg_map.find(arg_name); if (arg == _arg_map.end()) throw std::runtime_error("Invalid argument. " - "There is no argument you are looking for."); + "There is no argument you are looking for: " + + arg_name); if (arg->second->_type != TypeName<T>::Get()) throw std::runtime_error("Type mismatch. " @@ -383,7 +629,7 @@ template <typename T> T Arser::get_impl(const std::string &arg_name, T *) "You must make sure that the argument is given before accessing it. " "You can do it by calling arser[\"argument\"]."); - return ::lexical_cast<T>(arg->second->_values[0]); + return internal::lexical_cast<T>(arg->second->_values[0]); } template <typename T> std::vector<T> Arser::get_impl(const std::string &arg_name, std::vector<T> *) @@ -391,7 +637,8 @@ template <typename T> std::vector<T> Arser::get_impl(const std::string &arg_name auto arg = _arg_map.find(arg_name); if (arg == _arg_map.end()) throw std::runtime_error("Invalid argument. " - "There is no argument you are looking for."); + "There is no argument you are looking for: " + + arg_name); if (arg->second->_type != TypeName<std::vector<T>>::Get()) throw std::runtime_error("Type mismatch. " @@ -399,7 +646,7 @@ template <typename T> std::vector<T> Arser::get_impl(const std::string &arg_name std::vector<T> data; std::transform(arg->second->_values.begin(), arg->second->_values.end(), std::back_inserter(data), - [](std::string str) -> T { return ::lexical_cast<T>(str); }); + [](std::string str) -> T { return internal::lexical_cast<T>(str); }); return data; } @@ -408,100 +655,6 @@ template <typename T> T Arser::get(const std::string &arg_name) return get_impl(arg_name, static_cast<T *>(nullptr)); } -std::ostream &operator<<(std::ostream &stream, const Arser &parser) -{ - // print description - if (!parser._program_description.empty()) - { - stream << "What " << parser._program_name << " does: " << parser._program_description << "\n\n"; - } - /* - ** print usage - */ - stream << "Usage: ./" << parser._program_name << " "; - // required optional argument - for (const auto &arg : parser._optional_arg_vec) - { - if (!arg._is_required) - continue; - stream << arg._name << " "; - std::string arg_name = arg._name.substr(2); - std::for_each(arg_name.begin(), arg_name.end(), - [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); }); - stream << " "; - } - // rest of the optional argument - for (const auto &arg : parser._optional_arg_vec) - { - if (arg._is_required) - continue; - stream << "[" << arg._name; - if (arg._nargs) - { - stream << " "; - std::string arg_name = arg._name.substr(2); - std::for_each(arg_name.begin(), arg_name.end(), - [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); }); - } - stream << "]" - << " "; - } - // positional arguement - for (const auto &arg : parser._positional_arg_vec) - { - stream << arg._name << " "; - } - stream << "\n\n"; - /* - ** print argument list and its help message - */ - // get the length of the longest argument - size_t length_of_longest_arg = 0; - for (const auto &arg : parser._positional_arg_vec) - { - length_of_longest_arg = std::max(length_of_longest_arg, arg._name.length()); - } - for (const auto &arg : parser._optional_arg_vec) - { - length_of_longest_arg = std::max(length_of_longest_arg, arg._name.length()); - } - - const size_t message_width = 60; - // positional argument - if (!parser._positional_arg_vec.empty()) - { - stream << "[Positional argument]" << std::endl; - for (const auto &arg : parser._positional_arg_vec) - { - stream.width(length_of_longest_arg); - stream << std::left << arg._name << "\t"; - for (size_t i = 0; i < arg._help_message.length(); i += message_width) - { - if (i) - stream << std::string(length_of_longest_arg, ' ') << "\t"; - stream << arg._help_message.substr(i, message_width) << std::endl; - } - } - std::cout << std::endl; - } - // optional argument - if (!parser._optional_arg_vec.empty()) - { - stream << "[Optional argument]" << std::endl; - for (const auto &arg : parser._optional_arg_vec) - { - stream.width(length_of_longest_arg); - stream << std::left << arg._name << "\t"; - for (size_t i = 0; i < arg._help_message.length(); i += message_width) - { - if (i) - stream << std::string(length_of_longest_arg, ' ') << "\t"; - stream << arg._help_message.substr(i, message_width) << std::endl; - } - } - } - - return stream; -} - } // namespace arser + +#endif // __ARSER_H__ diff --git a/compiler/arser/tests/HelpMessage.test.cpp b/compiler/arser/tests/HelpMessage.test.cpp new file mode 100644 index 000000000..45cf840e6 --- /dev/null +++ b/compiler/arser/tests/HelpMessage.test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> + +#include "arser/arser.h" + +#include "Prompt.h" + +using namespace arser; + +/** + * [WARNING] DO NOT GIVE THE ARSER '-h' or '--help' OPTION IN BELOW TESTS. + * + * arser exits with code 0 when '-h' option is given, which forces googletest to pass. + */ + +TEST(HelpMessageTest, Default) +{ + /* arrange */ + Arser arser; + + arser.add_argument("--dummy").nargs(0).help("Dummy optional argument"); + + std::ostringstream oss; + std::string expected_out = "Usage: ./arser [-h] [--dummy] \n" + "\n" + "[Optional argument]\n" + "-h, --help Show help message and exit\n" + "--dummy \tDummy optional argument\n"; + + test::Prompt prompt("./arser --dummy"); + /* act */ + arser.parse(prompt.argc(), prompt.argv()); + oss << arser; + + /* assert */ + EXPECT_EQ(expected_out, oss.str()); +} + +TEST(HelpMessageTest, ShortOption) +{ + /* arrange */ + Arser arser; + + arser.add_argument("-v", "--verbose").nargs(0).help("Provides additional details"); + + std::ostringstream oss; + std::string expected_out = "Usage: ./arser [-h] [-v] \n" + "\n" + "[Optional argument]\n" + "-h, --help \tShow help message and exit\n" + "-v, --verbose\tProvides additional details\n"; + + test::Prompt prompt("./arser -v"); + /* act */ + arser.parse(prompt.argc(), prompt.argv()); + oss << arser; + + /* assert */ + EXPECT_EQ(expected_out, oss.str()); +} diff --git a/compiler/arser/tests/Prompt.h b/compiler/arser/tests/Prompt.h new file mode 100644 index 000000000..d816f199c --- /dev/null +++ b/compiler/arser/tests/Prompt.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 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 __ARSER_PROMPT_H__ +#define __ARSER_PROMPT_H__ + +#include <iterator> +#include <sstream> +#include <string> +#include <vector> + +namespace arser +{ +namespace test +{ + +class Prompt +{ +public: + Prompt(const std::string &command) + { + std::istringstream iss(command); + std::vector<std::string> token(std::istream_iterator<std::string>{iss}, + std::istream_iterator<std::string>()); + _arg = std::move(token); + _argv.reserve(_arg.size()); + for (const auto &t : _arg) + { + _argv.push_back(const_cast<char *>(t.data())); + } + } + int argc(void) const { return _argv.size(); } + char **argv(void) { return _argv.data(); } + +private: + std::vector<char *> _argv; + std::vector<std::string> _arg; +}; + +} // namespace test +} // namespace arser + +#endif // __ARSER_PROMPT_H__ diff --git a/compiler/arser/tests/arser.test.cpp b/compiler/arser/tests/arser.test.cpp index 28bee4238..b37d0dec3 100644 --- a/compiler/arser/tests/arser.test.cpp +++ b/compiler/arser/tests/arser.test.cpp @@ -54,8 +54,8 @@ TEST(BasicTest, option) Arser arser; arser.add_argument("--verbose") - .nargs(0) - .help("It provides additional details as to what the executable is doing"); + .nargs(0) + .help("It provides additional details as to what the executable is doing"); Prompt prompt("./executable --verbose"); /* act */ @@ -71,13 +71,13 @@ TEST(BasicTest, OptionalArgument) Arser arser; arser.add_argument("--volume") - .nargs(1) - .type(arser::DataType::INT32) - .help("Set a volume as you provided."); + .nargs(1) + .type(arser::DataType::INT32) + .help("Set a volume as you provided."); arser.add_argument("--frequency") - .nargs(1) - .type(arser::DataType::FLOAT) - .help("Set a frequency as you provided."); + .nargs(1) + .type(arser::DataType::FLOAT) + .help("Set a frequency as you provided."); Prompt prompt("./radio --volume 5 --frequency 128.5"); /* act */ @@ -99,9 +99,9 @@ TEST(BasicTest, NonRequiredOptionalArgument) Arser arser; arser.add_argument("--weight") - .nargs(1) - .type(arser::DataType::INT32) - .help("Set a volume as you provided."); + .nargs(1) + .type(arser::DataType::INT32) + .help("Set a volume as you provided."); Prompt prompt("./radio"); // empty argument /* act */ @@ -117,10 +117,10 @@ TEST(BasicTest, RequiredOptionalArgument) Arser arser; arser.add_argument("--volume") - .nargs(1) - .type(arser::DataType::INT32) - .required() - .help("Set a volume as you provided."); + .nargs(1) + .type(arser::DataType::INT32) + .required() + .help("Set a volume as you provided."); Prompt prompt("./radio"); /* act */ /* assert */ @@ -152,20 +152,20 @@ TEST(BasicTest, MultipleOptionalArgument) Arser arser; arser.add_argument("--input_path") - .nargs(1) - .type(arser::DataType::STR) - .help("input path of this program.") - .required(); + .nargs(1) + .type(arser::DataType::STR) + .help("input path of this program.") + .required(); arser.add_argument("--output_path") - .nargs(1) - .type(arser::DataType::STR) - .help("output path of this program.") - .required(true); + .nargs(1) + .type(arser::DataType::STR) + .help("output path of this program.") + .required(true); arser.add_argument("--training_data") - .nargs(5) - .type(arser::DataType::INT32_VEC) - .help("give traning data to this program.") - .required(); + .nargs(5) + .type(arser::DataType::INT32_VEC) + .help("give traning data to this program.") + .required(); Prompt prompt("./ml --input_path /I/am/in.put --output_path I/am/out.put " "--training_data 2 43 234 3 334"); @@ -191,9 +191,9 @@ TEST(BasicTest, MultipleFloatValue) Arser arser; arser.add_argument("--add_float") - .nargs(2) - .type(arser::DataType::FLOAT_VEC) - .help("Add two float numbers."); + .nargs(2) + .type(arser::DataType::FLOAT_VEC) + .help("Add two float numbers."); Prompt prompt("./calculator --add_float 3.2 5.4"); /* act */ @@ -213,9 +213,9 @@ TEST(BasicTest, MultipleStringValue) Arser arser; arser.add_argument("--three_color") - .nargs(3) - .type(arser::DataType::STR_VEC) - .help("insert your three favorite color"); + .nargs(3) + .type(arser::DataType::STR_VEC) + .help("insert your three favorite color"); Prompt prompt("./color_factory --three_color red blue yellow"); /* act */ @@ -255,8 +255,8 @@ TEST(BasicTest, ExitWithFunctionCallWithBind) Arser arser; arser.add_argument("--version") - .help("Show version and exit") - .exit_with(std::bind(printVersion, "1.2.0")); + .help("Show version and exit") + .exit_with(std::bind(printVersion, "1.2.0")); Prompt prompt("./arser --version"); /* act */ /* assert */ @@ -286,34 +286,34 @@ TEST(BasicTest, DefaultValue) Arser arser; arser.add_argument("--delivery") - .nargs(3) - .type(arser::DataType::STR_VEC) - .default_value("pizza", "chicken", "hamburger") - .help("Enter three foods that you want to deliver"); + .nargs(3) + .type(arser::DataType::STR_VEC) + .default_value("pizza", "chicken", "hamburger") + .help("Enter three foods that you want to deliver"); arser.add_argument("--assistant") - .type(arser::DataType::STR) - .default_value("Bixby") - .help("Enter name of your assistant"); + .type(arser::DataType::STR) + .default_value("Bixby") + .help("Enter name of your assistant"); arser.add_argument("--sound") - .type(arser::DataType::BOOL) - .nargs(1) - .default_value(true) - .help("Sound on/off"); + .type(arser::DataType::BOOL) + .nargs(1) + .default_value(true) + .help("Sound on/off"); arser.add_argument("--number") - .type(arser::DataType::INT32_VEC) - .nargs(4) - .default_value(1, 2, 3, 4) - .help("Enter the number that you want to call"); + .type(arser::DataType::INT32_VEC) + .nargs(4) + .default_value(1, 2, 3, 4) + .help("Enter the number that you want to call"); arser.add_argument("--time") - .type(arser::DataType::INT32_VEC) - .nargs(3) - .default_value(0, 0, 0) - .help("Current time(H/M/S)"); + .type(arser::DataType::INT32_VEC) + .nargs(3) + .default_value(0, 0, 0) + .help("Current time(H/M/S)"); arser.add_argument("--name") - .type(arser::DataType::STR) - .nargs(1) - .default_value("no name") - .help("Enter your name"); + .type(arser::DataType::STR) + .nargs(1) + .default_value("no name") + .help("Enter your name"); Prompt prompt("/phone --time 1 52 34 --name arser"); /* act */ @@ -342,3 +342,102 @@ TEST(BasicTest, DefaultValue) // 1 string, 1 argument EXPECT_EQ("arser", arser.get<std::string>("--name")); } + +TEST(BasicTest, shortOption) +{ + /* arrange */ + Arser arser; + + arser.add_argument("--input_path", "-i") + .nargs(1) + .type(arser::DataType::STR) + .help("input path of this program.") + .required(); + arser.add_argument("--output_path", "-o") + .nargs(1) + .type(arser::DataType::STR) + .help("output path of this program.") + .required(true); + + Prompt prompt("./driver -i /I/am/in.put --output_path I/am/out.put"); + /* act */ + arser.parse(prompt.argc(), prompt.argv()); + /* assert */ + EXPECT_TRUE(arser["--input_path"]); + EXPECT_EQ("/I/am/in.put", arser.get<std::string>("--input_path")); + EXPECT_TRUE(arser["--output_path"]); + EXPECT_EQ("I/am/out.put", arser.get<std::string>("--output_path")); +} + +TEST(BasicTest, shortMultipleOption) +{ + /* arrange */ + Arser arser; + + arser.add_argument("--input_path", "-i", "--input", "--in") + .nargs(1) + .type(arser::DataType::STR) + .help("input path of this program.") + .required(); + arser.add_argument("--output_path", "-o") + .nargs(1) + .type(arser::DataType::STR) + .help("output path of this program.") + .required(true); + + Prompt prompt("./driver --in /I/am/in.put -o I/am/out.put"); + /* act */ + arser.parse(prompt.argc(), prompt.argv()); + /* assert */ + EXPECT_TRUE(arser["--input"]); + EXPECT_EQ("/I/am/in.put", arser.get<std::string>("--input")); + EXPECT_TRUE(arser["--output_path"]); + EXPECT_EQ("I/am/out.put", arser.get<std::string>("--output_path")); +} + +TEST(BasicTest, OptWithRequiredDuplicate) +{ + /* arrange */ + Arser arser; + + arser.add_argument("--input_path", "-i", "--input", "--in") + .nargs(1) + .type(arser::DataType::STR) + .help("input path of this program.") + .required(); + arser.add_argument("--output_path", "-o") + .nargs(1) + .type(arser::DataType::STR) + .help("output path of this program.") + .required(true); + + Prompt prompt("./driver --in /I/am/in.put -o I/am/out.put -i /I/am/duplicate"); + /* act */ /* assert */ + EXPECT_THROW(arser.parse(prompt.argc(), prompt.argv()), std::runtime_error); +} + +TEST(BasicTest, OptWithNonRequiredDuplicate) +{ + /* arrange */ + Arser arser; + + arser.add_argument("--input_path", "-i", "--input", "--in") + .nargs(1) + .type(arser::DataType::STR) + .help("input path of this program."); + /* .required() */ + arser.add_argument("--output_path", "-o") + .nargs(1) + .type(arser::DataType::STR) + .help("output path of this program.") + .required(true); + + Prompt prompt("./driver --in /I/am/in.put -o I/am/out.put -i /I/am/duplicate"); + /* act */ + arser.parse(prompt.argc(), prompt.argv()); + /* assert */ + EXPECT_TRUE(arser["--input"]); + EXPECT_EQ("/I/am/duplicate", arser.get<std::string>("--input")); + EXPECT_TRUE(arser["--output_path"]); + EXPECT_EQ("I/am/out.put", arser.get<std::string>("--output_path")); +} diff --git a/compiler/bino/include/bino.h b/compiler/bino/include/bino.h index fc22d1285..bf540dffe 100644 --- a/compiler/bino/include/bino.h +++ b/compiler/bino/include/bino.h @@ -33,8 +33,8 @@ public: public: template <typename T> auto operator()(const std::pair<T, T> &p) const - -> decltype(std::make_pair(std::declval<Callable>()(p.first), - std::declval<Callable>()(p.second))) + -> decltype(std::make_pair(std::declval<Callable>()(p.first), + std::declval<Callable>()(p.second))) { return std::make_pair(f(p.first), f(p.second)); } diff --git a/compiler/caffegen/CMakeLists.txt b/compiler/caffegen/CMakeLists.txt index 334174dcd..b963b5294 100644 --- a/compiler/caffegen/CMakeLists.txt +++ b/compiler/caffegen/CMakeLists.txt @@ -7,7 +7,6 @@ endif(NOT Caffe_FOUND) file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(caffegen ${SOURCES}) -target_link_libraries(caffegen stdex) target_link_libraries(caffegen cli) # NOTE "Caffe" package provides both caffe and caffeproto target # NOTE "caffeproto" is linked to "caffe" diff --git a/compiler/caffegen/src/Driver.cpp b/compiler/caffegen/src/Driver.cpp index 81b01e6f1..17e3ebb7f 100644 --- a/compiler/caffegen/src/Driver.cpp +++ b/compiler/caffegen/src/Driver.cpp @@ -20,12 +20,12 @@ #include "MergeCommand.h" #include <cli/App.h> -#include <stdex/Memory.h> +#include <memory> #include <map> #include <string> -using stdex::make_unique; +using std::make_unique; int main(int argc, char **argv) { diff --git a/compiler/circle-inspect/driver/Driver.cpp b/compiler/circle-inspect/driver/Driver.cpp index 72cfa28a3..a450fd9e0 100644 --- a/compiler/circle-inspect/driver/Driver.cpp +++ b/compiler/circle-inspect/driver/Driver.cpp @@ -29,11 +29,11 @@ int entry(int argc, char **argv) { arser::Arser arser{ - "circle-inspect allows users to retrieve various information from a Circle model file"}; + "circle-inspect allows users to retrieve various information from a Circle model file"}; arser.add_argument("--operators").nargs(0).help("Dump operators in circle file"); arser.add_argument("--conv2d_weight") - .nargs(0) - .help("Dump Conv2D series weight operators in circle file"); + .nargs(0) + .help("Dump Conv2D series weight operators in circle file"); arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in circle file"); arser.add_argument("circle").type(arser::DataType::STR).help("Circle file to inspect"); diff --git a/compiler/circle-part-driver/CMakeLists.txt b/compiler/circle-part-driver/CMakeLists.txt new file mode 100644 index 000000000..cb708742c --- /dev/null +++ b/compiler/circle-part-driver/CMakeLists.txt @@ -0,0 +1,17 @@ +set(SRCS_PART_TESTER + src/Driver.cpp + src/PModelsRunner.cpp + ) + +add_executable(circle_part_driver ${SRCS_PART_TESTER}) +target_link_libraries(circle_part_driver foder) +target_link_libraries(circle_part_driver loco) +target_link_libraries(circle_part_driver luci_import) +target_link_libraries(circle_part_driver luci_lang) +target_link_libraries(circle_part_driver luci_log) +target_link_libraries(circle_part_driver luci_interpreter) +target_link_libraries(circle_part_driver crew) +target_link_libraries(circle_part_driver safemain) +target_link_libraries(circle_part_driver nncc_common) + +install(TARGETS circle_part_driver DESTINATION bin) diff --git a/compiler/circle-part-driver/README.md b/compiler/circle-part-driver/README.md new file mode 100644 index 000000000..d66ecf5fa --- /dev/null +++ b/compiler/circle-part-driver/README.md @@ -0,0 +1,3 @@ +# circle-part-driver + +_circle-part-driver_ is test driver to run partitioned circle models diff --git a/compiler/circle-part-driver/requires.cmake b/compiler/circle-part-driver/requires.cmake new file mode 100644 index 000000000..72296e32f --- /dev/null +++ b/compiler/circle-part-driver/requires.cmake @@ -0,0 +1,6 @@ +require("foder") +require("loco") +require("luci") +require("luci-interpreter") +require("crew") +require("safemain") diff --git a/compiler/circle-part-driver/src/Driver.cpp b/compiler/circle-part-driver/src/Driver.cpp new file mode 100644 index 000000000..a39bbf187 --- /dev/null +++ b/compiler/circle-part-driver/src/Driver.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 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 "PModelsRunner.h" + +#include <luci/Log.h> + +#include <iostream> + +int entry(int argc, char **argv) +{ + LOGGER(l); + + if (argc != 5) + { + std::cerr + << "Usage: " << argv[0] + << " <path/to/partition/config> <num_inputs> <path/to/input/prefix> <path/to/output/file>\n"; + return EXIT_FAILURE; + } + // NOTE: about input/output data file name + // - I/O file name format is like filename.ext0, filename.ext1, ... + // NOTE: about output shape + // - file name with filename.ext0.shape, filename.ext1.shape, ... + // having one line text content of CSV format(like H,W or N,C,H,W) + + const char *config_filename = argv[1]; + const int32_t num_inputs = atoi(argv[2]); + const char *input_prefix = argv[3]; + const char *output_file = argv[4]; + + prunner::PModelsRunner pmrunner; + + INFO(l) << "Read config file: " << config_filename << std::endl; + if (not pmrunner.load_config(config_filename)) + return EXIT_FAILURE; + + INFO(l) << "Read input file: " << input_prefix << ", #inputs: " << num_inputs << std::endl; + pmrunner.load_inputs(input_prefix, num_inputs); + + INFO(l) << "Run all partitioned models..." << std::endl; + if (!pmrunner.run()) + return EXIT_FAILURE; + + INFO(l) << "Save output file: " << output_file << std::endl; + pmrunner.save_outputs(output_file); + + return EXIT_SUCCESS; +} diff --git a/compiler/circle-part-driver/src/PModelsRunner.cpp b/compiler/circle-part-driver/src/PModelsRunner.cpp new file mode 100644 index 000000000..453ce9b5f --- /dev/null +++ b/compiler/circle-part-driver/src/PModelsRunner.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2021 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 "PModelsRunner.h" + +#include <luci/IR/Nodes/CircleInput.h> +#include <luci/IR/Nodes/CircleOutput.h> +#include <luci/Importer.h> +#include <luci/Log.h> +#include <luci_interpreter/Interpreter.h> + +#include <foder/FileLoader.h> +#include <crew/PConfig.h> + +#include <fstream> +#include <iostream> +#include <vector> +#include <string> +#include <stdexcept> + +namespace +{ + +void write_file(const std::string &filename, const char *data, size_t data_size) +{ + std::ofstream fs(filename, std::ofstream::binary); + if (fs.fail()) + throw std::runtime_error("Cannot open file \"" + filename + "\".\n"); + if (fs.write(data, data_size).fail()) + { + throw std::runtime_error("Failed to write data to file \"" + filename + "\".\n"); + } +} + +std::unique_ptr<luci::Module> import_circle(const std::string &filename) +{ + std::ifstream fs(filename, std::ifstream::binary); + if (fs.fail()) + { + throw std::runtime_error("Cannot open model file \"" + filename + "\".\n"); + } + std::vector<char> model_data((std::istreambuf_iterator<char>(fs)), + std::istreambuf_iterator<char>()); + + return luci::Importer().importModule(circle::GetModel(model_data.data())); +} + +void save_shape(const std::string &shape_filename, const luci::CircleOutput *output_node) +{ + if (output_node->rank() == 0) + { + write_file(shape_filename, "1", 1); + } + else + { + auto shape_str = std::to_string(output_node->dim(0).value()); + for (uint32_t j = 1; j < output_node->rank(); j++) + { + shape_str += ","; + shape_str += std::to_string(output_node->dim(j).value()); + } + write_file(shape_filename, shape_str.c_str(), shape_str.size()); + } +} + +template <typename NodeT> size_t tensor_size(const NodeT *node) +{ + uint32_t tsize = loco::size(node->dtype()); + for (uint32_t i = 0; i < node->rank(); ++i) + { + assert(node->dim(i).known()); + tsize *= node->dim(i).value(); + } + return tsize; +} + +} // namespace + +namespace prunner +{ + +bool PModelsRunner::load_config(const std::string &filename) +{ + if (!crew::read_ini(filename, _pconfig)) + { + std::cerr << "ERROR: Invalid config ini file: '" << filename << "'" << std::endl; + return false; + } + + for (auto &part : _pconfig.parts) + { + _models_to_run.push_back(part.model_file); + } + return true; +} + +void PModelsRunner::load_inputs(const std::string &input_prefix, int32_t num_inputs) +{ + LOGGER(l); + + auto its = _pconfig.source.inputs.begin(); + for (int32_t i = 0; i < num_inputs; ++i, ++its) + { + std::string filename = input_prefix + std::to_string(i); + + INFO(l) << "Load input data: " << filename << std::endl; + foder::FileLoader file_loader{filename}; + + std::string input_name = *its; + _data_stage[input_name] = file_loader.load(); + + INFO(l) << "Input: [" << input_name << "], size " << _data_stage[input_name].size() + << std::endl; + } +} + +/** + * @brief return true if all inputs of the model is ready in _data_storage + */ +bool PModelsRunner::is_input_ready(const RunModel &model) +{ + for (auto &part : _pconfig.parts) + { + if (part.model_file != model) + continue; + + for (auto &input : part.inputs) + { + auto it = _data_stage.find(input); + if (it == _data_stage.end()) + return false; + } + } + return true; +} + +bool PModelsRunner::run(void) +{ + LOGGER(l); + + // for each partitioned model, if the inputs of the model are ready, run the model + do + { + bool found_model = false; + + for (auto it = _models_to_run.begin(); it != _models_to_run.end(); ++it) + { + auto model_fname = *it; + + INFO(l) << "Check model input ready: " << model_fname << std::endl; + if (is_input_ready(model_fname)) + { + found_model = true; + + INFO(l) << "Run model: " << model_fname << std::endl; + auto module = import_circle(model_fname); + + luci_interpreter::Interpreter interpreter(module.get()); + + // Set input + // TODO support multiple subgraphs + assert(module->size() == 1); + const auto input_nodes = loco::input_nodes(module->graph()); + int32_t num_inputs = static_cast<int32_t>(input_nodes.size()); + for (int32_t i = 0; i < num_inputs; i++) + { + const auto *input_node = loco::must_cast<const luci::CircleInput *>(input_nodes[i]); + + auto input_name = input_node->name(); + assert(_data_stage.find(input_name) != _data_stage.end()); + + auto input_data = _data_stage[input_name]; + + interpreter.writeInputTensor(input_node, input_data.data(), input_data.size()); + } + + // Run interpreter + interpreter.interpret(); + INFO(l) << "Run model: " << model_fname << " done" << std::endl; + + // Get output. + const auto output_nodes = loco::output_nodes(module->graph()); + for (uint32_t i = 0; i < module->graph()->outputs()->size(); i++) + { + const auto *output_node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]); + auto output_name = output_node->name(); + + Buffer output_data(tensor_size(output_node)); + + interpreter.readOutputTensor(output_node, output_data.data(), output_data.size()); + + // There should not exist same output names + // TODO check with multiple virtual outputs + assert(_data_stage.find(output_name) == _data_stage.end()); + _data_stage[output_name] = output_data; + } + + // We've ran this model, remove from the model list + _models_to_run.erase(it); + break; + } + } + + if (not found_model) + { + std::cerr << "ERROR: model partition or configuration has problems" << std::endl; + return false; + } + } while (not _models_to_run.empty()); + + return true; +} + +void PModelsRunner::save_outputs(const std::string &output_file) +{ + // load source model as we need to get both shape and node name + // TODO check for unknown shape + auto source_fname = _pconfig.source.model_file; + + auto module = import_circle(source_fname); + + const auto output_nodes = loco::output_nodes(module->graph()); + for (uint32_t i = 0; i < module->graph()->outputs()->size(); i++) + { + const auto *output_node = loco::must_cast<const luci::CircleOutput *>(output_nodes[i]); + + auto output_name = output_node->name(); + assert(_data_stage.find(output_name) != _data_stage.end()); + + auto tensor_data = _data_stage[output_name]; + auto output_filename = output_file + std::to_string(i); + + write_file(output_filename, tensor_data.data(), tensor_data.size()); + save_shape(output_filename + ".shape", output_node); + } +} + +} // namespace prunner diff --git a/compiler/circle-part-driver/src/PModelsRunner.h b/compiler/circle-part-driver/src/PModelsRunner.h new file mode 100644 index 000000000..c1a45f01c --- /dev/null +++ b/compiler/circle-part-driver/src/PModelsRunner.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 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_PRUNNER_PMODELS_RUNNER_H__ +#define __CIRCLE_PRUNNER_PMODELS_RUNNER_H__ + +#include <crew/PConfig.h> + +#include <map> +#include <string> +#include <vector> + +namespace prunner +{ + +using Buffer = std::vector<char>; + +using Buffers = std::map<std::string, Buffer>; + +using RunModel = std::string; + +using RunModels = std::vector<RunModel>; + +/** + * @brief PModelsRunner runs partitioned models from input data file and stores + * output data to a file + */ +class PModelsRunner +{ +public: + PModelsRunner() = default; + +public: + bool load_config(const std::string &filename); + void load_inputs(const std::string &input_prefix, int32_t num_inputs); + bool run(void); + void save_outputs(const std::string &output_file); + +private: + bool is_input_ready(const RunModel &model); + +private: + crew::PConfig _pconfig; + RunModels _models_to_run; + Buffers _data_stage; +}; + +} // namespace prunner + +#endif // __CIRCLE_PRUNNER_PMODELS_RUNNER_H__ diff --git a/compiler/circle-part-value-test/CMakeLists.txt b/compiler/circle-part-value-test/CMakeLists.txt new file mode 100644 index 000000000..d75b17d1f --- /dev/null +++ b/compiler/circle-part-value-test/CMakeLists.txt @@ -0,0 +1,99 @@ +# +# this project validates partitioned models produced by circle-partitioner +# with circle-part-driver and two scripts; part_eval_all.sh and part_eval_one.py +# + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) + +unset(RECIPE_LIST) +unset(PARTITION_LIST) +unset(TEST_DEPS) + +macro(add RECIPE_NAME PARTITION_NAME) + list(APPEND RECIPE_LIST ${RECIPE_NAME}) + list(APPEND PARTITION_LIST ${PARTITION_NAME}) +endmacro(add) + +# Read "test.lst" +include("test.lst") + +list(LENGTH RECIPE_LIST RECIPE_LENGTH) +math(EXPR RECIPE_LENGTH_M1 "${RECIPE_LENGTH} - 1") + +foreach(IDX RANGE ${RECIPE_LENGTH_M1}) + list(GET RECIPE_LIST ${IDX} RECIPE_NAME) + list(GET PARTITION_LIST ${IDX} PARTITION_NAME) + + # NOTE about the name: + # Use '.recipe' name for source tflite and circle files + # Use '.part' name for actual test folder and test files + + # Output to a folder + set(PARTITIONER_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PARTITION_NAME}") + + add_custom_command(OUTPUT ${PARTITIONER_OUTPUT_PATH} + COMMAND ${CMAKE_COMMAND} -E make_directory "${PARTITIONER_OUTPUT_PATH}" + COMMENT "Make directory ${PARTITIONER_OUTPUT_PATH}" + ) + + # Copy tflite + set(TFLITE_SRC_PATH "${ARTIFACTS_BIN_PATH}/${RECIPE_NAME}.tflite") + set(TFLITE_DST_PATH "${PARTITIONER_OUTPUT_PATH}/${PARTITION_NAME}.tflite") + + add_custom_command(OUTPUT ${TFLITE_DST_PATH} + COMMAND ${CMAKE_COMMAND} -E copy "${TFLITE_SRC_PATH}" "${TFLITE_DST_PATH}" + DEPENDS ${TFLITE_SRC_PATH} + COMMENT "Copy ${RECIPE_NAME}.tflite" + ) + list(APPEND TEST_DEPS ${TFLITE_DST_PATH}) + + # Copy circle + set(CIRCLE_SRC_PATH "${ARTIFACTS_BIN_PATH}/${RECIPE_NAME}.circle") + set(CIRCLE_DST_PATH "${PARTITIONER_OUTPUT_PATH}/${PARTITION_NAME}.circle") + + add_custom_command(OUTPUT ${CIRCLE_DST_PATH} + COMMAND ${CMAKE_COMMAND} -E copy "${CIRCLE_SRC_PATH}" "${CIRCLE_DST_PATH}" + DEPENDS ${CIRCLE_SRC_PATH} + COMMENT "Copy ${RECIPE_NAME}.circle" + ) + list(APPEND TEST_DEPS ${CIRCLE_DST_PATH}) + + # Copy .part + set(PART_FILE "${PARTITION_NAME}.part") + set(PART_SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/parts/${PART_FILE}") + set(PART_DST_PATH "${PARTITIONER_OUTPUT_PATH}/${PART_FILE}") + + add_custom_command(OUTPUT ${PART_DST_PATH} + COMMAND ${CMAKE_COMMAND} -E copy "${PART_SRC_PATH}" "${PART_DST_PATH}" + DEPENDS ${PART_SRC_PATH} + COMMENT "Copy ${PART_FILE}" + ) + list(APPEND TEST_DEPS ${PART_DST_PATH}) + + # Partition connection file to generate + set(PARTITIONER_CONN_JSON "${PARTITIONER_OUTPUT_PATH}/${PARTITION_NAME}.conn.json") + + # Run partitioner + add_custom_command(OUTPUT ${PARTITIONER_CONN_JSON} + COMMAND circle_partitioner "${PART_FILE}" "${PARTITION_NAME}.circle" "${PARTITIONER_OUTPUT_PATH}" + DEPENDS circle_partitioner ${PART_DST_PATH} ${CIRCLE_DST_PATH} + COMMENT "Parition ${RECIPE_NAME}.circle with ${PART_FILE}" + ) + list(APPEND TEST_DEPS ${PARTITIONER_CONN_JSON}) +endforeach(IDX) + +add_custom_target(circle_part_value_test_prepare ALL DEPENDS ${TEST_DEPS}) +add_dependencies(circle_part_value_test_prepare common_artifacts_deps) + +# run evaluation +add_test(NAME circle_part_value_test + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/part_eval_all.sh" + "${CMAKE_CURRENT_BINARY_DIR}" + "${NNCC_OVERLAY_DIR}/venv_2_3_0" + "$<TARGET_FILE:circle_part_driver>" + ${PARTITION_LIST} +) diff --git a/compiler/circle-part-value-test/README.md b/compiler/circle-part-value-test/README.md new file mode 100644 index 000000000..6322b0791 --- /dev/null +++ b/compiler/circle-part-value-test/README.md @@ -0,0 +1,15 @@ +# circle-part-value-test + +_circle-part-value-test_ evaluates partitioned models produced by circle-partitioner. + +### Process of evaluation + +Evaluation process is like how _luci-value-test_ does. + +1) generates random input and stores to reference input file(s) +2) executes tflite file from common-artifacts for reference output +3) partitions circle file with .part file and produces into output folder +4) executes produced partitioned circle models with reference input file(s) +5) saves output(s) of circle models to file(s) +6) compares reference output with saved output file(s) +7) fail test if values differ diff --git a/compiler/circle-part-value-test/part_eval_all.sh b/compiler/circle-part-value-test/part_eval_all.sh new file mode 100755 index 000000000..ae8ae4731 --- /dev/null +++ b/compiler/circle-part-value-test/part_eval_all.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# This script verifies the basic behavior of circle-partitioner +# +# HOW TO USE +# +# ./part_eval_all.sh <path/to/work_dir> <path/to/venv_dir> <path/to/driver> <TEST 1> <TEST 2> ... +# +# bin_dir : build directory of circle-part-value-test (ex: build/compiler/circle-part-value-test) +# work_dir : artifacts directoy where test materials exist +# venv_dir : python virtual environment home directory + +VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VERIFY_SCRIPT_PATH="${VERIFY_SOURCE_PATH}/part_eval_one.py" +WORKDIR="$1"; shift +VIRTUALENV="$1"; shift +CIRCLE_PART_DRIVER_PATH="$1"; shift + +TESTED=() +PASSED=() +FAILED=() + +for TESTCASE in "$@"; do + TESTED+=("${TESTCASE}") + + # for simplicity, folder uses same ${TESTCASE} + TESTCASE_FOLDER="${WORKDIR}/${TESTCASE}" + + PASSED_TAG="${TESTCASE_FOLDER}.passed" + rm -f "${PASSED_TAG}" + + cat > "${TESTCASE_FOLDER}.log" <( + exec 2>&1 + set -ex + + # chdir into the folder as ini has relative filename of the model + pushd ${TESTCASE_FOLDER} + + source "${VIRTUALENV}/bin/activate" + "${VIRTUALENV}/bin/python" "${VERIFY_SCRIPT_PATH}" \ + --driver "${CIRCLE_PART_DRIVER_PATH}" \ + --name "${TESTCASE}" + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + + popd + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("${TESTCASE}") + else + FAILED+=("${TESTCASE}") + fi +done + +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/compiler/circle-part-value-test/part_eval_one.py b/compiler/circle-part-value-test/part_eval_one.py new file mode 100755 index 000000000..b0b65fd74 --- /dev/null +++ b/compiler/circle-part-value-test/part_eval_one.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +import numpy as np +import tensorflow as tf +import subprocess +import argparse +import traceback + +# +# This script compares the execution result of TFLite interpreter and +# partitioned model(s) from a circle model +# +# Basic usage for example: +# part_eval_one.py \ +# --driver build/compiler/circle-part-driver/circle-part-driver \ +# --name test_file +# +parser = argparse.ArgumentParser() +parser.add_argument('--driver', type=str, required=True) +parser.add_argument('--name', type=str, required=True) +args = parser.parse_args() + +driver = args.driver +tflite_model = args.name + ".tflite" +circle_model = args.name + ".circle" +partition_conn_ini = args.name + ".conn.ini" + +# Build TFLite interpreter. +interpreter = tf.lite.Interpreter(tflite_model) +interpreter.allocate_tensors() + +# Generate random input data. +num_inputs = len(interpreter.get_input_details()) +for i in range(num_inputs): + input_details = interpreter.get_input_details()[i] + if input_details["dtype"] == np.float32: + input_data = np.array( + np.random.random_sample(input_details["shape"]), input_details["dtype"]) + elif input_details["dtype"] == np.uint8: + input_data = np.array( + np.random.randint(0, 256, size=input_details["shape"]), + input_details["dtype"]) + elif input_details["dtype"] == np.bool_: + input_data = np.array( + np.random.choice(a=[True, False], size=input_details["shape"]), + input_details["dtype"]) + else: + raise SystemExit("Unsupported input dtype") + + interpreter.set_tensor(input_details["index"], input_data) + input_data.tofile(circle_model + ".input" + str(i)) + +# Do inference +interpreter.invoke() + +# Execute circle-part-driver. +partition_command = [ + driver, partition_conn_ini, + str(num_inputs), circle_model + ".input", circle_model + ".output" +] +print("Run: ") +for arg in partition_command: + print(" ", arg, "\\") +print("", flush=True) + +subprocess.run(partition_command, check=True) + +# Compare the results. +for idx in range(len(interpreter.get_output_details())): + output_details = interpreter.get_output_details()[idx] + output_data = np.fromfile(circle_model + ".output" + str(idx), + output_details["dtype"]) + shape_file = open(circle_model + ".output" + str(idx) + ".shape", 'r') + output_shape = [int(i) for i in shape_file.read().split(',')] + luci_output_data = np.reshape(output_data, output_shape) + try: + if output_details["dtype"] == np.uint8: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=0, + atol=0) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + elif output_details["dtype"] == np.float32: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=1.e-5, + atol=1.e-5) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + elif output_details["dtype"] == np.int64: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=0, + atol=0) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + elif output_details["dtype"] == np.int32: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=0, + atol=0) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + else: + raise SystemExit("Unsupported data type: ", output_details["dtype"]) + except: + print(traceback.format_exc()) + quit(255) + +quit(0) diff --git a/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.001.part b/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.001.part new file mode 100644 index 000000000..01b8c704e --- /dev/null +++ b/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.001.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +ADD=acl_cl diff --git a/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.002.part b/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.002.part new file mode 100644 index 000000000..dc378a448 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.002.part @@ -0,0 +1,8 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SUB=acl_cl +DIV=acl_cl diff --git a/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.part b/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.part new file mode 100644 index 000000000..d4d439d27 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Net_InstanceNorm_003.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +DIV=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Add_Sqrt_000.part b/compiler/circle-part-value-test/parts/Part_Add_Sqrt_000.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Add_Sqrt_000.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Add_Sqrt_Rsqrt_000.part b/compiler/circle-part-value-test/parts/Part_Add_Sqrt_Rsqrt_000.part new file mode 100644 index 000000000..c6dba9f94 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Add_Sqrt_Rsqrt_000.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +RSQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Add_Sub_000.part b/compiler/circle-part-value-test/parts/Part_Add_Sub_000.part new file mode 100644 index 000000000..905137ce7 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Add_Sub_000.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SUB=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_000.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_000.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_000.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_001.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_001.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_001.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_002.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_002.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_002.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_003.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_003.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_003.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_000.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_000.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_000.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_001.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_001.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_001.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_002.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_002.part new file mode 100644 index 000000000..402af87e9 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_002.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +SQRT=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_003.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_003.part new file mode 100644 index 000000000..0ec264c94 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_003.part @@ -0,0 +1,7 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] +WWW=acl_cl diff --git a/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_004.part b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_004.part new file mode 100644 index 000000000..febab2246 --- /dev/null +++ b/compiler/circle-part-value-test/parts/Part_Sqrt_Rsqrt_Add_004.part @@ -0,0 +1,6 @@ +[partition] +backends=cpu,acl_cl +default=cpu +comply=opcode + +[OPCODE] diff --git a/compiler/circle-part-value-test/requires.cmake b/compiler/circle-part-value-test/requires.cmake new file mode 100644 index 000000000..a9301f947 --- /dev/null +++ b/compiler/circle-part-value-test/requires.cmake @@ -0,0 +1,3 @@ +require("common-artifacts") +require("circle-partitioner") +require("circle-part-driver") diff --git a/compiler/circle-part-value-test/test.lst b/compiler/circle-part-value-test/test.lst new file mode 100644 index 000000000..8316560f0 --- /dev/null +++ b/compiler/circle-part-value-test/test.lst @@ -0,0 +1,20 @@ +# Add recipe names from /res/TensorFlowLiteRecipes to test. +# Only add items exist in common-artifacts test: tflite/circle files are copied as source. +# +# add(RECIPE_NAME PARTITION_NAME) + +add(Part_Add_Sub_000 Part_Add_Sub_000) +add(Part_Sqrt_Rsqrt_000 Part_Sqrt_Rsqrt_000) +add(Part_Sqrt_Rsqrt_001 Part_Sqrt_Rsqrt_001) +add(Part_Sqrt_Rsqrt_002 Part_Sqrt_Rsqrt_002) +add(Part_Sqrt_Rsqrt_003 Part_Sqrt_Rsqrt_003) +add(Part_Sqrt_Rsqrt_Add_000 Part_Sqrt_Rsqrt_Add_000) +add(Part_Sqrt_Rsqrt_Add_001 Part_Sqrt_Rsqrt_Add_001) +add(Part_Sqrt_Rsqrt_Add_002 Part_Sqrt_Rsqrt_Add_002) +add(Part_Sqrt_Rsqrt_Add_003 Part_Sqrt_Rsqrt_Add_003) +add(Part_Sqrt_Rsqrt_Add_004 Part_Sqrt_Rsqrt_Add_004) +add(Part_Add_Sqrt_000 Part_Add_Sqrt_000) +add(Part_Add_Sqrt_Rsqrt_000 Part_Add_Sqrt_Rsqrt_000) +add(Net_InstanceNorm_003 Net_InstanceNorm_003) +add(Net_InstanceNorm_003 Net_InstanceNorm_003.001) +add(Net_InstanceNorm_003 Net_InstanceNorm_003.002) diff --git a/compiler/circle-partitioner/CMakeLists.txt b/compiler/circle-partitioner/CMakeLists.txt new file mode 100644 index 000000000..573e6ec12 --- /dev/null +++ b/compiler/circle-partitioner/CMakeLists.txt @@ -0,0 +1,17 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") + +add_executable(circle_partitioner "${SOURCES}") +target_link_libraries(circle_partitioner foder) +target_link_libraries(circle_partitioner crew) +target_link_libraries(circle_partitioner safemain) +target_link_libraries(circle_partitioner luci_lang) +target_link_libraries(circle_partitioner luci_log) +target_link_libraries(circle_partitioner luci_import) +target_link_libraries(circle_partitioner luci_service) +target_link_libraries(circle_partitioner luci_export) +target_link_libraries(circle_partitioner luci_partition) +target_link_libraries(circle_partitioner arser) +target_link_libraries(circle_partitioner vconone) +target_link_libraries(circle_partitioner nncc_common) + +install(TARGETS circle_partitioner DESTINATION bin) diff --git a/compiler/circle-partitioner/README.md b/compiler/circle-partitioner/README.md new file mode 100644 index 000000000..7c387cf76 --- /dev/null +++ b/compiler/circle-partitioner/README.md @@ -0,0 +1,3 @@ +# circle-partitioner + +_circle-partitioner_ provides model partitioning of circle model to two or more circle models. diff --git a/compiler/circle-partitioner/requires.cmake b/compiler/circle-partitioner/requires.cmake new file mode 100644 index 000000000..507a4d89d --- /dev/null +++ b/compiler/circle-partitioner/requires.cmake @@ -0,0 +1,6 @@ +require("foder") +require("crew") +require("safemain") +require("luci") +require("arser") +require("vconone") diff --git a/compiler/circle-partitioner/src/CirclePartitioner.cpp b/compiler/circle-partitioner/src/CirclePartitioner.cpp new file mode 100644 index 000000000..28ff22abc --- /dev/null +++ b/compiler/circle-partitioner/src/CirclePartitioner.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2021 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 "PartitionRead.h" +#include "PartitionExport.h" +#include "HelperPath.h" +#include "HelperStrings.h" + +#include <foder/FileLoader.h> + +#include <luci/Importer.h> +#include <luci/Service/Validate.h> +#include <luci/CircleExporter.h> +#include <luci/CircleFileExpContract.h> +#include <luci/Log.h> + +#include <arser/arser.h> +#include <vconone/vconone.h> + +#include <iostream> +#include <string> + +namespace +{ + +const char *opt_bks = "--backends"; +const char *opt_def = "--default"; +const char *opt_part = "partition"; +const char *opt_input = "input"; +const char *opt_work = "work"; + +void print_version(void) +{ + std::cout << "circle-partitioner version " << vconone::get_string() << std::endl; + std::cout << vconone::get_copyright() << std::endl; +} + +void build_arser(arser::Arser &arser) +{ + arser.add_argument("--version") + .nargs(0) + .required(false) + .default_value(false) + .help("Show version information and exit") + .exit_with(print_version); + + arser.add_argument(opt_bks) + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .help("Backends in CSV to use for partitioning"); + + arser.add_argument(opt_def) + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .help("Default backend to assign"); + + arser.add_argument(opt_part) + .nargs(1) + .type(arser::DataType::STR) + .help("Partition file which provides backend to assign"); + arser.add_argument(opt_input) + .nargs(1) + .type(arser::DataType::STR) + .help("Input circle model filename"); + arser.add_argument(opt_work) + .nargs(1) + .type(arser::DataType::STR) + .help("Work folder of partition, input files exist and output files are produced"); +} + +std::unique_ptr<luci::Module> load_model(const std::string &input_path) +{ + // Load model from the file + foder::FileLoader file_loader{input_path}; + std::vector<char> model_data = file_loader.load(); + + // Verify flatbuffers + flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()}; + if (!circle::VerifyModelBuffer(verifier)) + { + std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl; + return nullptr; + } + + const circle::Model *circle_model = circle::GetModel(model_data.data()); + if (circle_model == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; + return nullptr; + } + + // Import from input Circle file + luci::Importer importer; + return importer.importModule(circle_model); +} + +bool validate_module(luci::Module *module) +{ + for (size_t g = 0; g < module->size(); ++g) + { + auto graph = module->graph(g); + if (!luci::validate(graph)) + { + std::cerr << "ERROR: Invalid circle model" << std::endl; + return false; + } + if (!luci::validate_name(graph)) + { + std::cerr << "ERROR: circle model has empty name" << std::endl; + return false; + } + } + + if (!luci::validate_unique_name(module)) + { + std::cerr << "ERROR: circle model has duplicate names" << std::endl; + return false; + } + + return true; +} + +bool validate_partition(luci::PartitionTable &partition) +{ + if (partition.groups.size() == 0) + { + std::cerr << "There is no 'backends' information"; + return false; + } + if (partition.default_group.empty()) + { + std::cerr << "There is no 'default' backend information"; + return false; + } + if (!partee::is_one_of(partition.default_group, partition.groups)) + { + std::cerr << "'default' backend is not one of 'backends' item"; + return false; + } + for (auto &byopcode : partition.byopcodes) + { + if (!partee::is_one_of(byopcode.second, partition.groups)) + { + std::cerr << "OPCODE " << byopcode.first << " is not assigned to one of 'backends' items"; + return false; + } + } + return true; +} + +void dump(std::ostream &os, const luci::PartitionTable &table) +{ + os << "Backends:"; + for (auto &group : table.groups) + { + os << " " << group; + if (table.default_group == group) + os << "(default)"; + } + os << std::endl; + + os << "Assign by OPCODE: " << std::endl; + for (auto &item : table.byopcodes) + os << " " << item.first << "=" << item.second << std::endl; +} + +std::ostream &operator<<(std::ostream &os, const luci::PartitionTable &table) +{ + dump(os, table); + return os; +} + +} // namespace + +int entry(int argc, char **argv) +{ + LOGGER(l); + + arser::Arser arser("circle-partitioner provides circle model partitioning"); + + build_arser(arser); + + try + { + arser.parse(argc, argv); + } + catch (const std::runtime_error &err) + { + std::cerr << err.what() << std::endl; + std::cerr << arser; + return EXIT_FAILURE; + } + + std::string partition_file = arser.get<std::string>(opt_part); + std::string input_file = arser.get<std::string>(opt_input); + std::string work_folder = arser.get<std::string>(opt_work); + + std::string partition_path = work_folder + "/" + partition_file; + std::string input_path = work_folder + "/" + input_file; + + auto module = load_model(input_path); + if (module.get() == nullptr) + { + return EXIT_FAILURE; + } + if (!validate_module(module.get())) + { + return EXIT_FAILURE; + } + + // Read partition information + INFO(l) << "--- Read PartitionConfig-----------------------" << std::endl; + auto partition = partee::read(partition_path); + INFO(l) << partition << std::endl; + + // override with command line arguments + { + if (arser[opt_bks]) + { + auto backend_backends = arser.get<std::string>(opt_bks); + partition.groups = partee::csv_to_vector<std::string>(backend_backends); + } + if (arser[opt_def]) + { + partition.default_group = arser.get<std::string>(opt_def); + } + } + if (!validate_partition(partition)) + { + return EXIT_FAILURE; + } + + INFO(l) << "--- PartitionConfig final----------------------" << std::endl; + INFO(l) << partition << std::endl; + + // apply partition to module + auto pms = luci::apply(module.get(), partition); + + // validate partitioned modules + for (auto &pmodule : pms.pmodules) + { + for (size_t g = 0; g < pmodule.module->size(); ++g) + { + auto graph = pmodule.module->graph(g); + if (graph == nullptr) + { + std::cerr << "ERROR: Failed to create partition model" << std::endl; + return EXIT_FAILURE; + } + if (!luci::validate(graph)) + { + std::cerr << "ERROR: Failed to create partition model" << std::endl; + return EXIT_FAILURE; + } + } + } + + INFO(l) << "--- Partition Export---------------------------" << std::endl; + uint32_t idx = 1; + for (auto &pmodule : pms.pmodules) + { + // Export to output circle file + luci::CircleExporter exporter; + + auto output_path = partee::make_path(work_folder, input_path, idx, pmodule.group); + pmodule.name = partee::get_filename_ext(output_path); + INFO(l) << "--- " << output_path << ": " << pmodule.name << std::endl; + + luci::CircleFileExpContract contract(pmodule.module.get(), output_path); + if (!exporter.invoke(&contract)) + { + std::cerr << "ERROR: Failed to export '" << output_path << "'" << std::endl; + return EXIT_FAILURE; + } + idx++; + } + + INFO(l) << "--- Partition connection information-----------" << std::endl; + if (!partee::export_part_conn_json(work_folder, input_file, module.get(), pms)) + { + return EXIT_FAILURE; + } + if (!partee::export_part_conn_ini(work_folder, input_file, module.get(), pms)) + { + return EXIT_FAILURE; + } + + INFO(l) << "--- Partition done-----------------------------" << std::endl << std::endl; + + return EXIT_SUCCESS; +} diff --git a/compiler/circle-partitioner/src/HelperPath.cpp b/compiler/circle-partitioner/src/HelperPath.cpp new file mode 100644 index 000000000..fc4bb2c70 --- /dev/null +++ b/compiler/circle-partitioner/src/HelperPath.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 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 "HelperPath.h" + +#include <cassert> +#include <sstream> +#include <stdlib.h> + +namespace partee +{ + +bool make_dir(const std::string &path) +{ + std::string command("mkdir -p "); + command += path; + int ret = ::system(command.c_str()); + return ret == 0; +} + +std::string get_filename_ext(const std::string &base) +{ + // find last '/' to get filename.ext + auto pos = base.find_last_of("/"); + if (pos == std::string::npos) + return base; + + return base.substr(pos + 1); +} + +std::string make_path(const std::string &base, const std::string &input, uint32_t idx, + const std::string &backend) +{ + auto filename_ext = get_filename_ext(input); + + // We will assume file type .circle if not given + // TODO maybe throw if there is no extension? + std::string filename = filename_ext; + std::string ext = "circle"; + + auto pos = filename_ext.find_last_of("."); + if (pos != std::string::npos) + { + filename = filename_ext.substr(0, pos); + ext = filename_ext.substr(pos + 1); + } + + // format idx with 5 '0' paddings like '00123' + uint32_t length = 5; + auto seq = std::string(length, '0').append(std::to_string(idx)); + auto seq_fmt = seq.substr(seq.size() - length); + + return base + "/" + filename + "." + seq_fmt + "_" + backend + "." + ext; +} + +} // namespace partee diff --git a/compiler/circle-partitioner/src/HelperPath.h b/compiler/circle-partitioner/src/HelperPath.h new file mode 100644 index 000000000..e38e3a903 --- /dev/null +++ b/compiler/circle-partitioner/src/HelperPath.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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_HELPER_PATH_H__ +#define __CIRCLE_HELPER_PATH_H__ + +#include <string> + +namespace partee +{ + +/** + * @brief create folder + */ +bool make_dir(const std::string &path); + +/** + * @brief get filename part of base + */ +std::string get_filename_ext(const std::string &base); + +/** + * @brief Make file path from base and backend + */ +std::string make_path(const std::string &base, const std::string &input, uint32_t idx, + const std::string &backend); + +} // namespace partee + +#endif // __CIRCLE_HELPER_PATH_H__ diff --git a/compiler/circle-partitioner/src/HelperStrings.cpp b/compiler/circle-partitioner/src/HelperStrings.cpp new file mode 100644 index 000000000..96d000c74 --- /dev/null +++ b/compiler/circle-partitioner/src/HelperStrings.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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 "HelperStrings.h" + +#include <algorithm> +#include <sstream> + +namespace partee +{ + +template <> std::vector<std::string> csv_to_vector(const std::string &str) +{ + std::vector<std::string> ret; + std::istringstream is(str); + for (std::string item; std::getline(is, item, ',');) + { + ret.push_back(item); + } + return ret; +} + +bool is_one_of(const std::string &item, const std::vector<std::string> &items) +{ + return std::find(items.begin(), items.end(), item) != items.end(); +} + +} // namespace partee diff --git a/compiler/circle-partitioner/src/HelperStrings.h b/compiler/circle-partitioner/src/HelperStrings.h new file mode 100644 index 000000000..2af14c1ff --- /dev/null +++ b/compiler/circle-partitioner/src/HelperStrings.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 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_HELPER_STRINGS_H__ +#define __CIRCLE_HELPER_STRINGS_H__ + +#include <string> +#include <vector> + +namespace partee +{ + +template <typename T> std::vector<T> csv_to_vector(const std::string &str); + +bool is_one_of(const std::string &item, const std::vector<std::string> &items); + +} // namespace partee + +#endif // __CIRCLE_HELPER_STRINGS_H__ diff --git a/compiler/circle-partitioner/src/PartitionExport.cpp b/compiler/circle-partitioner/src/PartitionExport.cpp new file mode 100644 index 000000000..a61451d66 --- /dev/null +++ b/compiler/circle-partitioner/src/PartitionExport.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2021 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 "PartitionExport.h" +#include "HelperPath.h" + +#include <crew/PConfig.h> + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> + +namespace +{ + +std::string export_file_path(const std::string &output_base, const std::string &input, + const std::string &ext) +{ + auto filename_ext = partee::get_filename_ext(input); + auto pos = filename_ext.find_last_of("."); + assert(pos > 0); + auto filename = filename_ext.substr(0, pos); + auto filepath = output_base + "/" + filename + ".conn" + ext; + return filepath; +} + +} // namespace + +namespace +{ + +void graph_io_to_config_part(loco::Graph *graph, crew::Part &part) +{ + assert(graph != nullptr); + + auto *gis = graph->inputs(); + auto *gos = graph->outputs(); + for (uint32_t i = 0; i < gis->size(); ++i) + { + auto *gi = gis->at(i); + assert(gi != nullptr); + part.inputs.push_back(gi->name()); + } + for (uint32_t i = 0; i < gos->size(); ++i) + { + auto *go = gos->at(i); + assert(go != nullptr); + part.outputs.push_back(go->name()); + } +} + +void pms2config(const luci::PartedModules &pms, crew::PConfig &pconfig) +{ + for (auto &pmodule : pms.pmodules) + { + auto *graph = pmodule.module->graph(); + + crew::Part part; + part.model_file = pmodule.name; + graph_io_to_config_part(graph, part); + + pconfig.parts.push_back(part); + } +} + +} // namespace + +namespace partee +{ + +bool export_part_conn_json(const std::string &output_base, const std::string &input, + const luci::Module *source, luci::PartedModules &pms) +{ + crew::PConfig pconfig; + + // TODO is graph I/O using main graph is enough? + auto *graph = source->graph(); + + pconfig.source.model_file = input; + graph_io_to_config_part(graph, pconfig.source); + + pms2config(pms, pconfig); + + auto filepath_json = export_file_path(output_base, input, ".json"); + std::ofstream fs(filepath_json.c_str(), std::ofstream::binary | std::ofstream::trunc); + if (not fs.good()) + { + std::cerr << "ERROR: Failed to create file: " << filepath_json; + return false; + } + if (not write_json(fs, pconfig)) + { + std::cerr << "ERROR: Failed to write json file: " << filepath_json; + return false; + } + fs.close(); + + return true; +} + +bool export_part_conn_ini(const std::string &output_base, const std::string &input, + const luci::Module *source, luci::PartedModules &pms) +{ + crew::PConfig pconfig; + + // TODO is graph I/O using main graph is enough? + auto *graph = source->graph(); + + pconfig.source.model_file = input; + graph_io_to_config_part(graph, pconfig.source); + + pms2config(pms, pconfig); + + auto filepath_ini = export_file_path(output_base, input, ".ini"); + std::ofstream fs(filepath_ini.c_str(), std::ofstream::binary | std::ofstream::trunc); + if (not fs.good()) + { + std::cerr << "ERROR: Failed to create file: " << filepath_ini; + return false; + } + if (not write_ini(fs, pconfig)) + { + std::cerr << "ERROR: Failed to write ini file: " << filepath_ini; + return false; + } + fs.close(); + + return true; +} + +} // namespace partee diff --git a/compiler/circle-partitioner/src/PartitionExport.h b/compiler/circle-partitioner/src/PartitionExport.h new file mode 100644 index 000000000..fd287dcd3 --- /dev/null +++ b/compiler/circle-partitioner/src/PartitionExport.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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_PARTITION_EXPORT_H__ +#define __CIRCLE_PARTITION_EXPORT_H__ + +#include <luci/Partition.h> + +#include <string> + +namespace partee +{ + +/** + * @brief This will save partition connection to json format file + */ +bool export_part_conn_json(const std::string &output_base, const std::string &input, + const luci::Module *source, luci::PartedModules &pms); + +/** + * @brief This will save partition connection to ini format file + */ +bool export_part_conn_ini(const std::string &output_base, const std::string &input, + const luci::Module *source, luci::PartedModules &pms); + +} // namespace partee + +#endif // __CIRCLE_PARTITION_EXPORT_H__ diff --git a/compiler/circle-partitioner/src/PartitionRead.cpp b/compiler/circle-partitioner/src/PartitionRead.cpp new file mode 100644 index 000000000..b179ecb59 --- /dev/null +++ b/compiler/circle-partitioner/src/PartitionRead.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 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 "PartitionRead.h" +#include "HelperStrings.h" + +#include <crew/PConfigIni.h> +#include <crew/PConfigIniDump.h> +#include <luci/Log.h> + +#include <stdexcept> + +namespace +{ + +using namespace partee; + +const char *_section_partition = "partition"; +const char *_section_OPCODE = "OPCODE"; + +const char *_key_backends = "backends"; +const char *_key_default = "default"; +const char *_key_underscore = "_"; + +luci::PartitionTable parse_table(const crew::Sections §ions) +{ + luci::PartitionTable table; + + for (auto §ion : sections) + { + if (section.name == _section_partition) + { + auto &items = section.items; + if (items.find(_key_backends) == items.end()) + { + throw std::invalid_argument("'backends' is required"); + } + if (items.find(_key_default) == items.end()) + { + throw std::invalid_argument("'default' is required"); + } + + table.groups = csv_to_vector<std::string>(items.at(_key_backends)); + table.default_group = items.at(_key_default); + } + else if (section.name == _section_OPCODE) + { + auto &items = section.items; + + for (auto &item : items) + { + if (item.first == _key_underscore) + table.default_group = item.second; + else + { + table.byopcodes.emplace(item.first, item.second); + } + } + } + } + + return table; +} + +} // namespace + +namespace partee +{ + +luci::PartitionTable read(const std::string &path) +{ + LOGGER(l); + + INFO(l) << "PartitionConfig: " << path << std::endl; + + auto partition_config = crew::read_ini(path); + + INFO(l) << partition_config << std::endl; + + auto partition_table = parse_table(partition_config); + + return partition_table; +} + +} // namespace partee diff --git a/compiler/circle-partitioner/src/PartitionRead.h b/compiler/circle-partitioner/src/PartitionRead.h new file mode 100644 index 000000000..9b07b328b --- /dev/null +++ b/compiler/circle-partitioner/src/PartitionRead.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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_PARTITION_READ_H__ +#define __CIRCLE_PARTITION_READ_H__ + +#include <luci/IR/Module.h> +#include <luci/Partition.h> + +#include <string> +#include <unordered_map> +#include <vector> + +namespace partee +{ + +/** + * @brief Reads and parse file and return PartitionTable + */ +luci::PartitionTable read(const std::string &path); + +} // namespace partee + +#endif // __CIRCLE_PARTITION_READ_H__ diff --git a/compiler/circle-quantizer/CMakeLists.txt b/compiler/circle-quantizer/CMakeLists.txt index 5075b13d5..a5f5f61c4 100644 --- a/compiler/circle-quantizer/CMakeLists.txt +++ b/compiler/circle-quantizer/CMakeLists.txt @@ -10,6 +10,7 @@ target_link_libraries(circle-quantizer luci_import) target_link_libraries(circle-quantizer luci_service) target_link_libraries(circle-quantizer luci_pass) target_link_libraries(circle-quantizer luci_export) +target_link_libraries(circle-quantizer luci_env) target_link_libraries(circle-quantizer arser) target_link_libraries(circle-quantizer vconone) diff --git a/compiler/circle-quantizer/src/CircleQuantizer.cpp b/compiler/circle-quantizer/src/CircleQuantizer.cpp index 54b38a170..720430e5a 100644 --- a/compiler/circle-quantizer/src/CircleQuantizer.cpp +++ b/compiler/circle-quantizer/src/CircleQuantizer.cpp @@ -21,6 +21,7 @@ #include <luci/Service/Validate.h> #include <luci/CircleExporter.h> #include <luci/CircleFileExpContract.h> +#include <luci/UserSettings.h> #include <oops/InternalExn.h> #include <arser/arser.h> @@ -57,47 +58,53 @@ int entry(int argc, char **argv) luci::CircleOptimizer optimizer; auto options = optimizer.options(); + auto settings = luci::UserSettings::settings(); const std::string qdqw = "--quantize_dequantize_weights"; const std::string qwmm = "--quantize_with_minmax"; const std::string rq = "--requantize"; + const std::string gpd = "--generate_profile_data"; + arser::Arser arser("circle-quantizer provides circle model quantization"); arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); + .nargs(0) + .required(false) + .default_value(false) + .help("Show version information and exit") + .exit_with(print_version); arser.add_argument(qdqw) - .nargs(3) - .type(arser::DataType::STR_VEC) - .required(false) - .help("Quantize-dequantize weight values required action before quantization. " - "Three arguments required: input_dtype(float32) " - "output_dtype(uint8) granularity(layer, channel)"); + .nargs(3) + .type(arser::DataType::STR_VEC) + .required(false) + .help("Quantize-dequantize weight values required action before quantization. " + "Three arguments required: input_dtype(float32) " + "output_dtype(uint8) granularity(layer, channel)"); arser.add_argument(qwmm) - .nargs(3) - .type(arser::DataType::STR_VEC) - .required(false) - .help("Quantize with min/max values. " - "Three arguments required: input_dtype(float32) " - "output_dtype(uint8) granularity(layer, channel)"); + .nargs(3) + .type(arser::DataType::STR_VEC) + .required(false) + .help("Quantize with min/max values. " + "Three arguments required: input_dtype(float32) " + "output_dtype(uint8) granularity(layer, channel)"); arser.add_argument(rq) - .nargs(2) - .type(arser::DataType::STR_VEC) - .required(false) - .help("Requantize a quantized model. " - "Two arguments required: input_dtype(int8) " - "output_dtype(uint8)"); + .nargs(2) + .type(arser::DataType::STR_VEC) + .required(false) + .help("Requantize a quantized model. " + "Two arguments required: input_dtype(int8) " + "output_dtype(uint8)"); arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model"); arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model"); + arser.add_argument(gpd).nargs(0).required(false).default_value(false).help( + "This will turn on profiling data generation."); + try { arser.parse(argc, argv); @@ -109,13 +116,20 @@ int entry(int argc, char **argv) return 255; } - if (arser[qdqw]) { - if (arser[qwmm] || arser[rq]) + // only one of qdqw, qwmm, rq option can be used + int32_t opt_used = arser[qdqw] ? 1 : 0; + opt_used += arser[qwmm] ? 1 : 0; + opt_used += arser[rq] ? 1 : 0; + if (opt_used != 1) { print_exclusive_options(); return 255; } + } + + if (arser[qdqw]) + { auto values = arser.get<std::vector<std::string>>(qdqw); if (values.size() != 3) { @@ -131,11 +145,6 @@ int entry(int argc, char **argv) if (arser[qwmm]) { - if (arser[qdqw] || arser[rq]) - { - print_exclusive_options(); - return 255; - } auto values = arser.get<std::vector<std::string>>(qwmm); if (values.size() != 3) { @@ -151,11 +160,6 @@ int entry(int argc, char **argv) if (arser[rq]) { - if (arser[qwmm] || arser[qdqw]) - { - print_exclusive_options(); - return 255; - } auto values = arser.get<std::vector<std::string>>(rq); if (values.size() != 2) { @@ -171,6 +175,9 @@ int entry(int argc, char **argv) std::string input_path = arser.get<std::string>("input"); std::string output_path = arser.get<std::string>("output"); + if (arser[gpd]) + settings->set(luci::UserSettings::Key::ProfilingDataGen, true); + // Load model from the file foder::FileLoader file_loader{input_path}; std::vector<char> model_data = file_loader.load(); diff --git a/compiler/circle-tensordump/driver/Driver.cpp b/compiler/circle-tensordump/driver/Driver.cpp index 5bab9f59e..70f3c8d84 100644 --- a/compiler/circle-tensordump/driver/Driver.cpp +++ b/compiler/circle-tensordump/driver/Driver.cpp @@ -29,14 +29,14 @@ int entry(int argc, char **argv) { arser::Arser arser{ - "circle-tensordump allows users to retrieve tensor information from a Circle model file"}; + "circle-tensordump allows users to retrieve tensor information from a Circle model file"}; arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Circle file path to dump"); arser.add_argument("--tensors").nargs(0).help("Dump to console"); arser.add_argument("--tensors_to_hdf5") - .nargs(1) - .type(arser::DataType::STR) - .help("Dump to hdf5 file. Specify hdf5 file path to be dumped"); + .nargs(1) + .type(arser::DataType::STR) + .help("Dump to hdf5 file. Specify hdf5 file path to be dumped"); try { diff --git a/compiler/circle-tensordump/src/Dump.cpp b/compiler/circle-tensordump/src/Dump.cpp index dee2f3620..d5c3fe6fa 100644 --- a/compiler/circle-tensordump/src/Dump.cpp +++ b/compiler/circle-tensordump/src/Dump.cpp @@ -253,7 +253,7 @@ void write_vector_data_to_hdf5(H5::H5File &file, std::string &group_name, std::s return; auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data()); auto dataset = std::make_unique<H5::DataSet>( - file.createDataSet(group_name + "/" + dataset_name, type, *dataspace)); + file.createDataSet(group_name + "/" + dataset_name, type, *dataspace)); dataset->write(data->data(), type); } @@ -264,7 +264,7 @@ void write_scalar_data_to_hdf5(H5::H5File &file, std::string &group_name, std::s { auto dataspace = std::make_unique<H5::DataSpace>(H5S_SCALAR); auto dataset = std::make_unique<H5::DataSet>( - file.createDataSet(group_name + "/" + dataset_name, type, *dataspace)); + file.createDataSet(group_name + "/" + dataset_name, type, *dataspace)); dataset->write(&data, type); } @@ -308,7 +308,7 @@ void DumpTensorsToHdf5::run(std::ostream &os, const circle::Model *model, // create a group for each tensor whose name is its tensor name std::string group_name = ::mangle(tensor->name()->c_str()); std::unique_ptr<H5::Group> tensor_group = - std::make_unique<H5::Group>(file.createGroup(group_name)); + std::make_unique<H5::Group>(file.createGroup(group_name)); // write a buffer data uint32_t buff_idx = tensor->buffer(); diff --git a/compiler/circle2circle-dredd-recipe-test/test.lst b/compiler/circle2circle-dredd-recipe-test/test.lst index 3a95e2be2..bb944201e 100644 --- a/compiler/circle2circle-dredd-recipe-test/test.lst +++ b/compiler/circle2circle-dredd-recipe-test/test.lst @@ -11,14 +11,27 @@ ## TFLITE RECIPE Add(Net_Preactivation_BN_000 PASS fuse_preactivation_batchnorm) +Add(Net_BroadcastTo_AddV2_000 PASS resolve_customop_add) +Add(Net_BroadcastTo_AddV2_001 PASS resolve_customop_add) +Add(Net_Conv_Add_Mul_000 PASS fuse_batchnorm_with_conv) +Add(Net_Conv_Add_Mul_001 PASS fuse_batchnorm_with_conv) +Add(Net_Conv_Add_Mul_002 PASS fuse_batchnorm_with_conv) +Add(Net_Conv_Min_Max_000 PASS transform_min_max_to_relu6) +Add(Net_Conv_Relu6_000 PASS fuse_activation_function) +Add(Net_DwConv_BN_000 PASS fuse_batchnorm_with_dwconv) +Add(Net_DwConv_BN_001 PASS fuse_batchnorm_with_dwconv) +Add(Net_Reshape_Reshape_000 PASS remove_redundant_reshape) +Add(Net_Squeeze_Squeeze_000 PASS substitute_squeeze_to_reshape) Add(Net_TConv_Add_000 PASS fuse_add_with_tconv) Add(Net_TConv_Add_001 PASS fuse_add_with_tconv) Add(Net_TConv_Add_002 PASS fuse_add_with_tconv) Add(Net_TConv_BN_000 PASS fuse_batchnorm_with_tconv) Add(Net_TConv_BN_001 PASS fuse_batchnorm_with_tconv) +Add(Net_TConv_BN_002 PASS fuse_batchnorm_with_tconv) Add(Net_InstanceNorm_001 PASS fuse_instnorm) Add(Net_InstanceNorm_002 PASS fuse_instnorm) Add(Net_InstanceNorm_003 PASS fuse_instnorm) +Add(Net_Maximum_Minimum_000 PASS transform_min_max_to_relu6) Add(BatchMatMulV2_000 PASS resolve_customop_batchmatmul) Add(MatMul_000 PASS resolve_customop_matmul) Add(DepthwiseConv2D_003 PASS) diff --git a/compiler/circle2circle/src/Circle2Circle.cpp b/compiler/circle2circle/src/Circle2Circle.cpp index cde5de8fd..da05a0a9a 100644 --- a/compiler/circle2circle/src/Circle2Circle.cpp +++ b/compiler/circle2circle/src/Circle2Circle.cpp @@ -51,157 +51,266 @@ int entry(int argc, char **argv) arser::Arser arser("circle2circle provides circle model optimization and transformations"); arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); - - arser.add_argument("--all").nargs(0).required(false).default_value(false).help( - "Enable all optimize options"); + .nargs(0) + .required(false) + .default_value(false) + .help("Show version information and exit") + .exit_with(print_version); + + arser.add_argument("--O1").nargs(0).required(false).default_value(false).help( + "Enable O1 optimize options"); + + arser.add_argument("--fold_add_v2") + .nargs(0) + .required(false) + .default_value(false) + .help("This will fold AddV2 operators with constant inputs"); + + arser.add_argument("--fold_cast") + .nargs(0) + .required(false) + .default_value(false) + .help("This will fold Cast operators with constant input"); arser.add_argument("--fold_dequantize") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fold dequantize op"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fold dequantize op"); + + arser.add_argument("--fold_sparse_to_dense") + .nargs(0) + .required(false) + .default_value(false) + .help("This will fold SparseToDense operator"); + + arser.add_argument("--forward_reshape_to_unaryop") + .nargs(0) + .required(false) + .default_value(false) + .help("This will move Reshape after UnaryOp for centain condition"); arser.add_argument("--fuse_activation_function") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse Activation function to a preceding operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse Activation function to a preceding operator"); arser.add_argument("--fuse_add_with_tconv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse Add operator to Transposed Convolution operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse Add operator to Transposed Convolution operator"); + + arser.add_argument("--fuse_batchnorm_with_conv") + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse BatchNorm operators to Convolution operator"); + + arser.add_argument("--fuse_batchnorm_with_dwconv") + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse BatchNorm operators to Depthwise Convolution operator"); arser.add_argument("--fuse_batchnorm_with_tconv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse BatchNorm operators to Transposed Convolution operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse BatchNorm operators to Transposed Convolution operator"); arser.add_argument("--fuse_bcq") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse operators and apply Binary Coded Quantization"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse operators and apply Binary Coded Quantization"); arser.add_argument("--fuse_instnorm") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse operators to InstanceNorm operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse operators to InstanceNorm operator"); arser.add_argument("--make_batchnorm_gamma_positive") - .nargs(0) - .required(false) - .default_value(false) - .help("This will make negative gamma of BatchNorm into a small positive value (1e-10). Note " - "that this pass can change the execution result of the model. So, use it only when the " - "impact is known to be acceptable."); + .nargs(0) + .required(false) + .default_value(false) + .help("This will make negative gamma of BatchNorm into a small positive value (1e-10). Note " + "that this pass can change the execution result of the model. So, use it only when the " + "impact is known to be acceptable."); arser.add_argument("--fuse_preactivation_batchnorm") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse BatchNorm operators of pre-activations to Convolution operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse BatchNorm operators of pre-activations to Convolution operator"); + + arser.add_argument("--remove_redundant_reshape") + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse or remove subsequent Reshape operators"); arser.add_argument("--remove_redundant_transpose") - .nargs(0) - .required(false) - .default_value(false) - .help("This will fuse or remove subsequent Transpose operators"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will fuse or remove subsequent Transpose operators"); + + arser.add_argument("--remove_unnecessary_reshape") + .nargs(0) + .required(false) + .default_value(false) + .help("This will remove unnecessary reshape operators"); + + arser.add_argument("--remove_unnecessary_slice") + .nargs(0) + .required(false) + .default_value(false) + .help("This will remove unnecessary slice operators"); + + arser.add_argument("--remove_unnecessary_strided_slice") + .nargs(0) + .required(false) + .default_value(false) + .help("This will remove unnecessary strided slice operators"); + + arser.add_argument("--remove_unnecessary_split") + .nargs(0) + .required(false) + .default_value(false) + .help("This will remove unnecessary split operators"); arser.add_argument("--replace_cw_mul_add_with_depthwise_conv") - .nargs(0) - .required(false) - .default_value(false) - .help("This will replace channel-wise mul/add with DepthwiseConv2D operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will replace channel-wise mul/add with DepthwiseConv2D operator"); arser.add_argument("--resolve_customop_add") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert Custom(Add) to Add operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will convert Custom(Add) to Add operator"); arser.add_argument("--resolve_customop_batchmatmul") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert Custom(BatchMatmul) to BatchMatmul operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will convert Custom(BatchMatmul) to BatchMatmul operator"); arser.add_argument("--resolve_customop_matmul") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert Custom(Matmul) to Matmul operator"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will convert Custom(Matmul) to Matmul operator"); arser.add_argument("--shuffle_weight_to_16x1float32") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert weight format of FullyConnected to SHUFFLED16x1FLOAT32. Note that " - "it only converts weights whose row is a multiple of 16"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will convert weight format of FullyConnected to SHUFFLED16x1FLOAT32. Note that " + "it only converts weights whose row is a multiple of 16"); arser.add_argument("--substitute_pack_to_reshape") - .nargs(0) - .required(false) - .default_value(false) - .help("This will convert single input Pack to Reshape"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will convert single input Pack to Reshape"); + + arser.add_argument("--substitute_squeeze_to_reshape") + .nargs(0) + .required(false) + .default_value(false) + .help("This will convert certain condition Squeeze to Reshape"); + + arser.add_argument("--substitute_transpose_to_reshape") + .nargs(0) + .required(false) + .default_value(false) + .help("This will convert single input Transpose to Reshape"); + + arser.add_argument("--convert_nchw_to_nhwc") + .nargs(0) + .required(false) + .default_value(false) + .help("Experimental: This will convert NCHW operators to NHWC under the assumption that " + "input model is NCHW."); + + arser.add_argument("--nchw_to_nhwc_preserve_input_shape") + .nargs(0) + .required(false) + .default_value(false) + .help("Preserve the input shape of the model (argument for --convert_nchw_to_nhwc)."); + + arser.add_argument("--nchw_to_nhwc_preserve_output_shape") + .nargs(0) + .required(false) + .default_value(false) + .help("Preserve the output shape of the model (argument for --convert_nchw_to_nhwc)."); + + arser.add_argument("--transform_min_max_to_relu6") + .nargs(0) + .required(false) + .default_value(false) + .help("Transform Minimum-Maximum pattern to Relu6 operator"); arser.add_argument("--mute_warnings") - .nargs(0) - .required(false) - .default_value(false) - .help("This will turn off warning messages"); + .nargs(0) + .required(false) + .default_value(false) + .help("This will turn off warning messages"); arser.add_argument("--disable_validation") - .nargs(0) - .required(false) - .default_value(false) - .help("This will turn off operator validations. May help input model investigation."); + .nargs(0) + .required(false) + .default_value(false) + .help("This will turn off operator validations. May help input model investigation."); + + arser.add_argument("--generate_profile_data") + .nargs(0) + .required(false) + .default_value(false) + .help("This will turn on profiling data generation."); arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model"); arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model"); // sparsification argument arser.add_argument("--sparsify_tensor") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Tensor name that you want to sparsify"); + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .help("Tensor name that you want to sparsify"); arser.add_argument("--sparsify_traversal_order") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .default_value("0,1,2,3") - .help("Traversal order of dimensions. Default value: 0,1,2,3"); + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .default_value("0,1,2,3") + .help("Traversal order of dimensions. Default value: 0,1,2,3"); arser.add_argument("--sparsify_format") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .default_value("d,s") - .help("Format of each dimension. 'd' stands for dense, 's' stands for sparse(CSR). Default " - "value: d,s"); + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .default_value("d,s") + .help("Format of each dimension. 'd' stands for dense, 's' stands for sparse(CSR). Default " + "value: d,s"); arser.add_argument("--sparsify_block_size") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .help("Size of each block dimension"); + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .help("Size of each block dimension"); arser.add_argument("--sparsify_block_map") - .nargs(1) - .type(arser::DataType::STR) - .required(false) - .default_value("0,1") - .help("Map from block dimension to the original tensor dimension. Default value: 0,1"); + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .default_value("0,1") + .help("Map from block dimension to the original tensor dimension. Default value: 0,1"); try { @@ -214,7 +323,7 @@ int entry(int argc, char **argv) return 255; } - if (arser.get<bool>("--all")) + if (arser.get<bool>("--O1")) { options->enable(Algorithms::FuseBCQ); options->enable(Algorithms::FuseInstanceNorm); @@ -224,12 +333,24 @@ int entry(int argc, char **argv) options->enable(Algorithms::RemoveRedundantTranspose); options->enable(Algorithms::SubstitutePackToReshape); } + if (arser.get<bool>("--fold_add_v2")) + options->enable(Algorithms::FoldAddV2); + if (arser.get<bool>("--fold_cast")) + options->enable(Algorithms::FoldCast); if (arser.get<bool>("--fold_dequantize")) options->enable(Algorithms::FoldDequantize); + if (arser.get<bool>("--fold_sparse_to_dense")) + options->enable(Algorithms::FoldSparseToDense); + if (arser.get<bool>("--forward_reshape_to_unaryop")) + options->enable(Algorithms::ForwardReshapeToUnaryOp); if (arser.get<bool>("--fuse_activation_function")) options->enable(Algorithms::FuseActivationFunction); + if (arser.get<bool>("--fuse_batchnorm_with_conv")) + options->enable(Algorithms::FuseBatchNormWithConv); if (arser.get<bool>("--fuse_add_with_tconv")) options->enable(Algorithms::FuseAddWithTConv); + if (arser.get<bool>("--fuse_batchnorm_with_dwconv")) + options->enable(Algorithms::FuseBatchNormWithDwConv); if (arser.get<bool>("--fuse_batchnorm_with_tconv")) options->enable(Algorithms::FuseBatchNormWithTConv); if (arser.get<bool>("--fuse_bcq")) @@ -240,8 +361,18 @@ int entry(int argc, char **argv) options->enable(Algorithms::MakeBatchNormGammaPositive); if (arser.get<bool>("--fuse_preactivation_batchnorm")) options->enable(Algorithms::FusePreActivationBatchNorm); + if (arser.get<bool>("--remove_redundant_reshape")) + options->enable(Algorithms::RemoveRedundantReshape); if (arser.get<bool>("--remove_redundant_transpose")) options->enable(Algorithms::RemoveRedundantTranspose); + if (arser.get<bool>("--remove_unnecessary_reshape")) + options->enable(Algorithms::RemoveUnnecessaryReshape); + if (arser.get<bool>("--remove_unnecessary_slice")) + options->enable(Algorithms::RemoveUnnecessarySlice); + if (arser.get<bool>("--remove_unnecessary_strided_slice")) + options->enable(Algorithms::RemoveUnnecessaryStridedSlice); + if (arser.get<bool>("--remove_unnecessary_split")) + options->enable(Algorithms::RemoveUnnecessarySplit); if (arser.get<bool>("--replace_cw_mul_add_with_depthwise_conv")) options->enable(Algorithms::ReplaceMulAddWithDepthwiseConv); if (arser.get<bool>("--resolve_customop_add")) @@ -254,11 +385,19 @@ int entry(int argc, char **argv) options->enable(Algorithms::ShuffleWeightTo16x1Float32); if (arser.get<bool>("--substitute_pack_to_reshape")) options->enable(Algorithms::SubstitutePackToReshape); + if (arser.get<bool>("--substitute_squeeze_to_reshape")) + options->enable(Algorithms::SubstituteSqueezeToReshape); + if (arser.get<bool>("--substitute_transpose_to_reshape")) + options->enable(Algorithms::SubstituteTransposeToReshape); + if (arser.get<bool>("--transform_min_max_to_relu6")) + options->enable(Algorithms::TransformMinMaxToRelu6Pass); if (arser.get<bool>("--mute_warnings")) settings->set(luci::UserSettings::Key::MuteWarnings, true); if (arser.get<bool>("--disable_validation")) settings->set(luci::UserSettings::Key::DisableValidation, true); + if (arser.get<bool>("--generate_profile_data")) + settings->set(luci::UserSettings::Key::ProfilingDataGen, true); std::string input_path = arser.get<std::string>("input"); std::string output_path = arser.get<std::string>("output"); @@ -284,6 +423,15 @@ int entry(int argc, char **argv) arser.get<std::string>("--sparsify_block_map")); } + if (arser.get<bool>("--convert_nchw_to_nhwc")) + { + options->enable(Algorithms::ConvertNCHWToNHWC); + if (arser.get<bool>("--nchw_to_nhwc_preserve_input_shape")) + options->param(AlgorithmParameters::NCHW_to_NHWC_preserve_input_shape, "true"); + if (arser.get<bool>("--nchw_to_nhwc_preserve_output_shape")) + options->param(AlgorithmParameters::NCHW_to_NHWC_preserve_output_shape, "true"); + } + // Load model from the file foder::FileLoader file_loader{input_path}; std::vector<char> model_data; diff --git a/compiler/circle2circle/src/TestHelper.h b/compiler/circle2circle/src/TestHelper.h index f4dbe23a9..1e055b217 100644 --- a/compiler/circle2circle/src/TestHelper.h +++ b/compiler/circle2circle/src/TestHelper.h @@ -39,7 +39,7 @@ public: { assert(_ptr < N); _argv[_ptr] = new char[strlen(in) + 1]; - strcpy(_argv[_ptr], in); + strncpy(_argv[_ptr], in, strlen(in) + 1); _ptr++; } @@ -47,7 +47,7 @@ public: private: pchar_t _argv[N] = { - nullptr, + nullptr, }; size_t _ptr = 0; }; diff --git a/compiler/circlechef/circle/CMakeLists.txt b/compiler/circlechef/circle/CMakeLists.txt index 2ca016b84..98a284c30 100644 --- a/compiler/circlechef/circle/CMakeLists.txt +++ b/compiler/circlechef/circle/CMakeLists.txt @@ -5,6 +5,5 @@ target_include_directories(circlechef_circle PUBLIC include) target_include_directories(circlechef_circle PRIVATE src) target_link_libraries(circlechef_circle circlechef_proto) target_link_libraries(circlechef_circle mio_circle) -target_link_libraries(circlechef_circle stdex) target_link_libraries(circlechef_circle cwrap) target_link_libraries(circlechef_circle souschef) diff --git a/compiler/circlechef/circle/src/Convert.cpp b/compiler/circlechef/circle/src/Convert.cpp index 77614d9b5..248687fed 100644 --- a/compiler/circlechef/circle/src/Convert.cpp +++ b/compiler/circlechef/circle/src/Convert.cpp @@ -33,10 +33,11 @@ circlechef::TensorType as_circlechef_type(const circle::TensorType type) return circlechef::UINT8; case circle::TensorType_BOOL: return circlechef::BOOL; + case circle::TensorType_INT16: + return circlechef::INT16; // TODO handle other types // TensorType_FLOAT16 // TensorType_STRING - // TensorType_INT16 // TensorType_COMPLEX64 default: throw std::runtime_error{"unsupported tensor type"}; diff --git a/compiler/circlechef/core/CMakeLists.txt b/compiler/circlechef/core/CMakeLists.txt index 54b3ea53d..0e8f47483 100644 --- a/compiler/circlechef/core/CMakeLists.txt +++ b/compiler/circlechef/core/CMakeLists.txt @@ -1,9 +1,23 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) add_library(circlechef_core STATIC ${SOURCES}) target_include_directories(circlechef_core PUBLIC include) target_include_directories(circlechef_core PRIVATE src) -target_link_libraries(circlechef_core circlechef_proto) -target_link_libraries(circlechef_core circlechef_log) -target_link_libraries(circlechef_core mio_circle) -target_link_libraries(circlechef_core souschef) +target_link_libraries(circlechef_core PUBLIC circlechef_proto) +target_link_libraries(circlechef_core PUBLIC circlechef_log) +target_link_libraries(circlechef_core PUBLIC mio_circle) +target_link_libraries(circlechef_core PUBLIC souschef) +target_link_libraries(circlechef_core PRIVATE nncc_coverage) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(circlechef_core_test ${TESTS}) +target_include_directories(circlechef_core_test PRIVATE src) +target_link_libraries(circlechef_core_test circlechef_core) +target_link_libraries(circlechef_core_test nncc_coverage) diff --git a/compiler/circlechef/core/src/Convert.cpp b/compiler/circlechef/core/src/Convert.cpp index 2db0a6212..d9bbd6e50 100644 --- a/compiler/circlechef/core/src/Convert.cpp +++ b/compiler/circlechef/core/src/Convert.cpp @@ -64,6 +64,8 @@ circle::TensorType as_circle_tensortype(const circlechef::TensorType &value) return circle::TensorType_INT64; case circlechef::BOOL: return circle::TensorType_BOOL; + case circlechef::INT16: + return circle::TensorType_INT16; default: break; } diff --git a/compiler/circlechef/core/src/Convert.test.cpp b/compiler/circlechef/core/src/Convert.test.cpp new file mode 100644 index 000000000..b17f5df44 --- /dev/null +++ b/compiler/circlechef/core/src/Convert.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 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 "Convert.h" + +#include <gtest/gtest.h> + +TEST(ConvertTest, as_circle_padding) +{ + ASSERT_EQ(circle::Padding_SAME, as_circle_padding(circlechef::SAME)); + ASSERT_EQ(circle::Padding_VALID, as_circle_padding(circlechef::VALID)); +} + +TEST(ConvertTest, as_circle_padding_NEG) +{ + EXPECT_THROW(as_circle_padding(static_cast<circlechef::Padding>(99)), std::runtime_error); +} + +TEST(ConvertTest, as_circle_activation) +{ + ASSERT_EQ(circle::ActivationFunctionType_NONE, as_circle_activation(circlechef::NONE)); + ASSERT_EQ(circle::ActivationFunctionType_RELU, as_circle_activation(circlechef::RELU)); + ASSERT_EQ(circle::ActivationFunctionType_RELU6, as_circle_activation(circlechef::RELU6)); +} + +TEST(ConvertTest, as_circle_activation_NEG) +{ + EXPECT_THROW(as_circle_activation(static_cast<circlechef::Activation>(99)), std::runtime_error); +} + +TEST(ConvertTest, as_circle_tensortype) +{ + ASSERT_EQ(circle::TensorType_FLOAT32, as_circle_tensortype(circlechef::FLOAT32)); + ASSERT_EQ(circle::TensorType_INT32, as_circle_tensortype(circlechef::INT32)); + ASSERT_EQ(circle::TensorType_UINT8, as_circle_tensortype(circlechef::UINT8)); + ASSERT_EQ(circle::TensorType_INT64, as_circle_tensortype(circlechef::INT64)); + ASSERT_EQ(circle::TensorType_BOOL, as_circle_tensortype(circlechef::BOOL)); + ASSERT_EQ(circle::TensorType_INT16, as_circle_tensortype(circlechef::INT16)); +} + +TEST(ConvertTest, as_circle_tensortype_NEG) +{ + EXPECT_THROW(as_circle_tensortype(static_cast<circlechef::TensorType>(99)), std::runtime_error); +} diff --git a/compiler/circlechef/core/src/ModelChef.cpp b/compiler/circlechef/core/src/ModelChef.cpp index 4f25d62c0..d7101f618 100644 --- a/compiler/circlechef/core/src/ModelChef.cpp +++ b/compiler/circlechef/core/src/ModelChef.cpp @@ -51,7 +51,7 @@ class GeneratedModelImpl final : public circlechef::GeneratedModel::Impl { public: GeneratedModelImpl(std::unique_ptr<flatbuffers::FlatBufferBuilder> &&builder) - : _builder{std::move(builder)} + : _builder{std::move(builder)} { // DO NOTHING } @@ -90,6 +90,7 @@ DataChefRegistry &data_chef_registry(const circlechef::TensorType &type) static DataChefRegistry fp32; static DataChefRegistry u8; static DataChefRegistry boolean; + static DataChefRegistry s16; switch (type) { @@ -103,6 +104,8 @@ DataChefRegistry &data_chef_registry(const circlechef::TensorType &type) return u8; case circlechef::BOOL: return boolean; + case circlechef::INT16: + return s16; default: break; } @@ -489,7 +492,7 @@ GeneratedModel cook(const ::circlechef::ModelRecipe &model_recipe) // Initialize Data Chef Registry #define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \ data_chef_registry(::circlechef::TYPE) \ - .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS())); + .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS())); #include <souschef/DataChef.def> #undef DATA_CHEF @@ -497,7 +500,7 @@ GeneratedModel cook(const ::circlechef::ModelRecipe &model_recipe) // Create FlatBufferBuilder // auto flatbuffer_builder = - std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024)); + std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024)); // Operand-related std::vector<flatbuffers::Offset<::circle::Buffer>> buffer_vec; @@ -510,7 +513,7 @@ GeneratedModel cook(const ::circlechef::ModelRecipe &model_recipe) // Create OperatorCode with Builtin Operator std::map<circle::BuiltinOperator, int32_t> builtin_code_map = - gather_builtincode_map(model_recipe); + gather_builtincode_map(model_recipe); for (auto const &opcode : builtin_code_map) { circle::OperatorCodeBuilder code_builder{*flatbuffer_builder}; @@ -592,7 +595,7 @@ GeneratedModel cook(const ::circlechef::ModelRecipe &model_recipe) // Return "GenerateModel" return GeneratedModel{ - std::unique_ptr<GeneratedModelImpl>(new GeneratedModelImpl(std::move(flatbuffer_builder)))}; + std::unique_ptr<GeneratedModelImpl>(new GeneratedModelImpl(std::move(flatbuffer_builder)))}; } } // namespace circlechef diff --git a/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp b/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp index 4c82c52cc..497cbb86b 100644 --- a/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp +++ b/compiler/circlechef/core/src/Op/BCQFullyConnected.cpp @@ -26,9 +26,9 @@ flatbuffers::Offset<void> BCQFullyConnectedChef::value(flatbuffers::FlatBufferBu circle::BCQFullyConnectedOptionsBuilder bcq_fully_connected_options_builder{fbb}; bcq_fully_connected_options_builder.add_weights_hidden_size( - operation.bcq_fully_connected_options().weights_hidden_size()); + operation.bcq_fully_connected_options().weights_hidden_size()); bcq_fully_connected_options_builder.add_fused_activation_function( - as_circle_activation(operation.bcq_fully_connected_options().activation())); + as_circle_activation(operation.bcq_fully_connected_options().activation())); return bcq_fully_connected_options_builder.Finish().Union(); } diff --git a/compiler/circlechef/core/src/Op/BCQGather.cpp b/compiler/circlechef/core/src/Op/BCQGather.cpp index 08f6f611f..3b343ee66 100644 --- a/compiler/circlechef/core/src/Op/BCQGather.cpp +++ b/compiler/circlechef/core/src/Op/BCQGather.cpp @@ -24,7 +24,7 @@ flatbuffers::Offset<void> BCQGatherChef::value(flatbuffers::FlatBufferBuilder &f circle::BCQGatherOptionsBuilder bcq_gather_options_builder{fbb}; bcq_gather_options_builder.add_input_hidden_size( - operation.bcq_gather_options().input_hidden_size()); + operation.bcq_gather_options().input_hidden_size()); bcq_gather_options_builder.add_axis(operation.bcq_gather_options().axis()); return bcq_gather_options_builder.Finish().Union(); diff --git a/compiler/circlechef/core/src/Op/BatchMatMul.cpp b/compiler/circlechef/core/src/Op/BatchMatMul.cpp index d98c0801a..645571abe 100644 --- a/compiler/circlechef/core/src/Op/BatchMatMul.cpp +++ b/compiler/circlechef/core/src/Op/BatchMatMul.cpp @@ -24,9 +24,9 @@ flatbuffers::Offset<void> BatchMatMulChef::value(flatbuffers::FlatBufferBuilder circle::BatchMatMulOptionsBuilder batch_matmul_options_options_builder{fbb}; batch_matmul_options_options_builder.add_adjoint_lhs( - operation.batch_matmul_options().adjoint_lhs()); + operation.batch_matmul_options().adjoint_lhs()); batch_matmul_options_options_builder.add_adjoint_rhs( - operation.batch_matmul_options().adjoint_rhs()); + operation.batch_matmul_options().adjoint_rhs()); return batch_matmul_options_options_builder.Finish().Union(); } diff --git a/compiler/circlechef/proto/circlechef.proto b/compiler/circlechef/proto/circlechef.proto index 83d2dfe9c..1c14b97ff 100644 --- a/compiler/circlechef/proto/circlechef.proto +++ b/compiler/circlechef/proto/circlechef.proto @@ -19,6 +19,7 @@ enum TensorType { UINT8 = 3; INT64 = 4; BOOL = 6; + INT16 = 7; } message TensorShape { diff --git a/compiler/circlechef/tests/short_int_datatype/test.recipe b/compiler/circlechef/tests/short_int_datatype/test.recipe new file mode 100644 index 000000000..e0f582527 --- /dev/null +++ b/compiler/circlechef/tests/short_int_datatype/test.recipe @@ -0,0 +1,32 @@ +operand { + name: "ifm1" + type: INT16 + shape { dim: 1 dim: 4 dim: 4 dim: 3 } +} +operand { + name: "constant" + type: INT16 + shape { dim: 1 dim: 4 dim: 3 dim: 4 } + filler { + tag: "gaussian" + arg: "3.0" + arg: "10.0" + } +} +operand { + name: "ofm" + type: INT16 + shape { dim: 1 dim: 4 dim: 4 dim: 4 } +} +operation { + type: "BatchMatMul" + input: "ifm1" + input: "constant" + output: "ofm" + batch_matmul_options { + adjoint_lhs: false + adjoint_rhs: false + } +} +input: "ifm1" +output: "ofm" diff --git a/compiler/circlechef/tests/short_int_datatype/test.reverse b/compiler/circlechef/tests/short_int_datatype/test.reverse new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/compiler/circlechef/tests/short_int_datatype/test.reverse diff --git a/compiler/circlechef/tools/console/CMakeLists.txt b/compiler/circlechef/tools/console/CMakeLists.txt index 10168fca3..faf0a94f0 100644 --- a/compiler/circlechef/tools/console/CMakeLists.txt +++ b/compiler/circlechef/tools/console/CMakeLists.txt @@ -1,3 +1,12 @@ add_executable(circlechef Driver.cpp) target_link_libraries(circlechef circlechef_core) target_link_libraries(circlechef safemain) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(circlechef_test Driver.test.cpp Driver.cpp) +target_link_libraries(circlechef_test circlechef_core) diff --git a/compiler/circlechef/tools/console/Driver.cpp b/compiler/circlechef/tools/console/Driver.cpp index 0909f5927..6aa4c3cc5 100644 --- a/compiler/circlechef/tools/console/Driver.cpp +++ b/compiler/circlechef/tools/console/Driver.cpp @@ -22,7 +22,7 @@ #include <iostream> -int entry(int argc, char **argv) +int entry_stream(std::istream &is) { int32_t model_version = 1; @@ -30,7 +30,7 @@ int entry(int argc, char **argv) // Read a model recipe from standard input { - google::protobuf::io::IstreamInputStream iis{&std::cin}; + google::protobuf::io::IstreamInputStream iis{&is}; if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe)) { std::cerr << "ERROR: Failed to parse recipe" << std::endl; @@ -56,3 +56,9 @@ int entry(int argc, char **argv) return 0; } + +int entry(int, char **) +{ + // forward to entry_stream + return entry_stream(std::cin); +} diff --git a/compiler/circlechef/tools/console/Driver.test.cpp b/compiler/circlechef/tools/console/Driver.test.cpp new file mode 100644 index 000000000..d8e4e657e --- /dev/null +++ b/compiler/circlechef/tools/console/Driver.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> + +// entry function to test from Driver.cpp +int entry_stream(std::istream &is); + +TEST(CircleChefDriverTest, entry_empty_NEG) +{ + std::istringstream empty_input(""); + + ASSERT_EQ(0, entry_stream(empty_input)); +} + +TEST(CircleChefDriverTest, entry_invaid_NEG) +{ + std::istringstream empty_input("invalid: input"); + + ASSERT_NE(0, entry_stream(empty_input)); +} + +TEST(CircleChefDriverTest, entry_invaid_version_NEG) +{ + std::istringstream empty_input("version: 9999"); + + ASSERT_NE(0, entry_stream(empty_input)); +} diff --git a/compiler/circlechef/tools/file/Driver.cpp b/compiler/circlechef/tools/file/Driver.cpp index bcc0c7ae9..76d0f3f7f 100644 --- a/compiler/circlechef/tools/file/Driver.cpp +++ b/compiler/circlechef/tools/file/Driver.cpp @@ -29,8 +29,8 @@ int entry(int argc, char **argv) { arser::Arser arser; arser.add_argument("recipe") - .type(arser::DataType::STR) - .help("Source recipe file path to convert"); + .type(arser::DataType::STR) + .help("Source recipe file path to convert"); arser.add_argument("circle").type(arser::DataType::STR).help("Target circle file path"); try diff --git a/compiler/circlechef/tools/reverse/Driver.cpp b/compiler/circlechef/tools/reverse/Driver.cpp index 8a2b85fc7..639e0af6f 100644 --- a/compiler/circlechef/tools/reverse/Driver.cpp +++ b/compiler/circlechef/tools/reverse/Driver.cpp @@ -26,8 +26,8 @@ int entry(int argc, char **argv) { arser::Arser arser; arser.add_argument("circle") - .type(arser::DataType::STR) - .help("Source circle file path to convert"); + .type(arser::DataType::STR) + .help("Source circle file path to convert"); arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path"); try diff --git a/compiler/circledump/README.md b/compiler/circledump/README.md index 686e918ac..e31c2d560 100644 --- a/compiler/circledump/README.md +++ b/compiler/circledump/README.md @@ -67,5 +67,4 @@ O T(3) ofm - mio-circle - safemain -- stdex - FlatBuffers diff --git a/compiler/circledump/src/Dump.cpp b/compiler/circledump/src/Dump.cpp index f8e2d61f3..42b4ad97a 100644 --- a/compiler/circledump/src/Dump.cpp +++ b/compiler/circledump/src/Dump.cpp @@ -18,6 +18,7 @@ #include "Read.h" #include "OpPrinter.h" +#include "MetadataPrinter.h" #include <ostream> @@ -362,6 +363,7 @@ void dump_model(std::ostream &os, const circle::Model *model) auto opcodes = reader.opcodes(); auto buffers = reader.buffers(); + auto metadata = reader.metadata(); // dump operator_codes os << "Operator Codes: [order] OpCodeName (OpCode Enum)" << std::endl; @@ -395,6 +397,26 @@ void dump_model(std::ostream &os, const circle::Model *model) } os << std::endl; + // dump metadata + if (metadata != nullptr) + { + os << "metadata : B(index) name" << std::endl; + for (uint32_t i = 0; i < metadata->Length(); ++i) + { + const auto buff_id = metadata->Get(i)->buffer(); + const auto metadata_name = metadata->Get(i)->name()->str(); + os << "B(" << buff_id << ") " << metadata_name << std::endl; + + const uint8_t *buff_data; + reader.buffer_info(buff_id, &buff_data); + if (auto meta_prn = MetadataPrinterRegistry::get().lookup(metadata_name)) + { + meta_prn->print(buff_data, os); + } + } + os << std::endl; + } + for (uint32_t sg = 0; sg < num_subgraph; ++sg) { reader.select_subgraph(sg); diff --git a/compiler/circledump/src/MetadataPrinter.cpp b/compiler/circledump/src/MetadataPrinter.cpp new file mode 100644 index 000000000..f2df9bc16 --- /dev/null +++ b/compiler/circledump/src/MetadataPrinter.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 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 "MetadataPrinter.h" + +#include <cassert> +#include <string> +#include <vector> + +namespace circledump +{ + +class SourceTablePrinter : public MetadataPrinter +{ +public: + /** + * source table consists of following parts + * - [ entry_number : uint32_t ] + * - [ id : uint32_t ][ length : uint32_t ][ data : 'length' Bytes ] * entry_number + */ + virtual void print(const uint8_t *buffer, std::ostream &os) const override + { + if (buffer) + { + os << " [node_id : node_name]" << std::endl; + auto cur = buffer; + // entry number + const uint32_t num = *reinterpret_cast<const uint32_t *>(cur); + cur += sizeof(uint32_t); + for (uint32_t entry = 0; entry < num; entry++) + { + // id + const uint32_t node_id = *reinterpret_cast<const uint32_t *>(cur); + cur += sizeof(uint32_t); + // length + const uint32_t len = *reinterpret_cast<const uint32_t *>(cur); + cur += sizeof(uint32_t); + assert(len != 0); + // data + // non-empty 'data' has trailing '\0'. Let's exclude it. + std::string node_name = std::string(cur, cur + len - 1); + cur += len; + + // print + os << " [" << node_id << " : " << node_name << "]" << std::endl; + } + } + } +}; + +class OpTablePrinter : public MetadataPrinter +{ +public: + /** + * op table consists of following parts + * - [ entry_number : uint32_t ] + * - [ id : uint32_t ][ length : uint32_t ][ origin_ids : length * uint32_t ] * entry_number + */ + virtual void print(const uint8_t *buffer, std::ostream &os) const override + { + if (buffer) + { + os << " [node_id : origin_ids]" << std::endl; + auto cur = buffer; + // entry number + const uint32_t num = *reinterpret_cast<const uint32_t *>(cur); + cur += sizeof(uint32_t); + for (uint32_t entry = 0; entry < num; entry++) + { + // id + const uint32_t node_id = *reinterpret_cast<const uint32_t *>(cur); + cur += sizeof(uint32_t); + // length + const uint32_t len = *reinterpret_cast<const uint32_t *>(cur); + cur += sizeof(uint32_t); + assert(len != 0); + // origin_ids + std::vector<uint32_t> origin_ids; + for (uint32_t o = 0; o < len; o++) + { + origin_ids.push_back(*reinterpret_cast<const uint32_t *>(cur)); + cur += sizeof(uint32_t); + } + + // print + os << " [" << node_id << " : "; + uint32_t i = 0; + for (const auto &id : origin_ids) + { + if (i++) + os << ", "; + os << id; + } + os << "]" << std::endl; + } + } + } +}; + +MetadataPrinterRegistry::MetadataPrinterRegistry() +{ + _metadata_map["ONE_source_table"] = std::make_unique<SourceTablePrinter>(); + _metadata_map["ONE_op_table"] = std::make_unique<OpTablePrinter>(); +} + +} // namespace circledump diff --git a/compiler/circledump/src/MetadataPrinter.h b/compiler/circledump/src/MetadataPrinter.h new file mode 100644 index 000000000..1dca2ca1e --- /dev/null +++ b/compiler/circledump/src/MetadataPrinter.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 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 __CIRCLEDUMP_METADATA_PRINTER_H__ +#define __CIRCLEDUMP_METADATA_PRINTER_H__ + +#include <ostream> +#include <string> +#include <map> +#include <memory> + +namespace circledump +{ + +class MetadataPrinter +{ +public: + virtual void print(const uint8_t * /* buffer */, std::ostream &) const = 0; +}; + +class MetadataPrinterRegistry +{ +public: + MetadataPrinterRegistry(); + +public: + const MetadataPrinter *lookup(std::string op) const + { + if (_metadata_map.find(op) == _metadata_map.end()) + return nullptr; + + return _metadata_map.at(op).get(); + } + +public: + static MetadataPrinterRegistry &get() + { + static MetadataPrinterRegistry me; + return me; + } + +private: + std::map<std::string /* metadata name */, std::unique_ptr<MetadataPrinter>> _metadata_map; +}; + +} // namespace circledump + +#endif // __CIRCLEDUMP_METADATA_PRINTER_H__ diff --git a/compiler/circledump/src/OpPrinter.cpp b/compiler/circledump/src/OpPrinter.cpp index ef22baaee..5319bb88d 100644 --- a/compiler/circledump/src/OpPrinter.cpp +++ b/compiler/circledump/src/OpPrinter.cpp @@ -90,6 +90,26 @@ public: } }; +class BidirectionalSequenceLSTMPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_BidirectionalSequenceLSTMOptions()) + { + os << " "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; + os << "cell_clip(" << params->cell_clip() << ") "; + os << "proj_clip(" << params->proj_clip() << ") "; + os << "time_major(" << params->time_major() << ") "; + os << "asymmetric_quantize_inputs(" << params->asymmetric_quantize_inputs() << ") "; + os << "merge_outputs(" << params->merge_outputs() << ") "; + os << std::endl; + } + } +}; + class CastPrinter : public OpPrinter { public: @@ -279,7 +299,7 @@ public: os << "Stride.H(" << conv_params->stride_h() << ") "; os << "DepthMultiplier(" << conv_params->depth_multiplier() << ") "; os << "Dilation.W(" << conv_params->dilation_w_factor() << ") "; - os << "Dilation.H(" << conv_params->dilation_h_factor() << ")"; + os << "Dilation.H(" << conv_params->dilation_h_factor() << ") "; os << "Activation(" << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ") "; os << std::endl; @@ -287,6 +307,25 @@ public: } }; +class FakeQuantPrinter : public OpPrinter +{ +public: + void options(const circle::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_FakeQuantOptions()) + { + os << " "; + os << "Min(" << params->min() << ") "; + os << "Max(" << params->max() << ") "; + os << "NumBits(" << params->num_bits() << ") "; + os << std::boolalpha; + os << "NarrowRange(" << params->narrow_range() << ") "; + os << std::noboolalpha; + os << std::endl; + } + } +}; + class FullyConnectedPrinter : public OpPrinter { public: @@ -720,6 +759,8 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[circle::BuiltinOperator_ARG_MIN] = make_unique<ArgMinPrinter>(); _op_map[circle::BuiltinOperator_AVERAGE_POOL_2D] = make_unique<Pool2DPrinter>(); _op_map[circle::BuiltinOperator_BATCH_MATMUL] = make_unique<BatchMatMulPrinter>(); + _op_map[circle::BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM] = + make_unique<BidirectionalSequenceLSTMPrinter>(); _op_map[circle::BuiltinOperator_CAST] = make_unique<CastPrinter>(); // There is no Option for CEIL _op_map[circle::BuiltinOperator_CONCATENATION] = make_unique<ConcatenationPrinter>(); @@ -728,6 +769,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[circle::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique<DepthwiseConv2DPrinter>(); // There is no Option for DEQUANTIZE _op_map[circle::BuiltinOperator_DIV] = make_unique<DivPrinter>(); + _op_map[circle::BuiltinOperator_FAKE_QUANT] = make_unique<FakeQuantPrinter>(); // There is no Option for FLOOR // There is no Option for FLOOR_MOD _op_map[circle::BuiltinOperator_FULLY_CONNECTED] = make_unique<FullyConnectedPrinter>(); @@ -737,7 +779,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[circle::BuiltinOperator_L2_POOL_2D] = make_unique<Pool2DPrinter>(); _op_map[circle::BuiltinOperator_LEAKY_RELU] = make_unique<LeakyReluPrinter>(); _op_map[circle::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION] = - make_unique<LocalResponseNormalizationPrinter>(); + make_unique<LocalResponseNormalizationPrinter>(); // There is no Option for LOG // There is no Option for LOGISTIC // There is no Option for LOG_SOFTMAX @@ -761,7 +803,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[circle::BuiltinOperator_RESHAPE] = make_unique<ReshapePrinter>(); _op_map[circle::BuiltinOperator_RESIZE_BILINEAR] = make_unique<ResizeBilinearPrinter>(); _op_map[circle::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = - make_unique<ResizeNearestNeighborPrinter>(); + make_unique<ResizeNearestNeighborPrinter>(); _op_map[circle::BuiltinOperator_REVERSE_SEQUENCE] = make_unique<ReverseSequencePrinter>(); // There is no Option for ROUND // There is no Option for SELECT @@ -782,7 +824,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[circle::BuiltinOperator_TRANSPOSE_CONV] = make_unique<TransposeConvPrinter>(); // There is no Option for TOPK_V2 _op_map[circle::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM] = - make_unique<UnidirectionalSequenceLSTMPrinter>(); + make_unique<UnidirectionalSequenceLSTMPrinter>(); _op_map[circle::BuiltinOperator_UNIQUE] = make_unique<UniquePrinter>(); _op_map[circle::BuiltinOperator_WHILE] = make_unique<WhilePrinter>(); _op_map[circle::BuiltinOperator_CUSTOM] = make_unique<CustomOpPrinter>(); diff --git a/compiler/circledump/src/Read.cpp b/compiler/circledump/src/Read.cpp index 053225536..db8298585 100644 --- a/compiler/circledump/src/Read.cpp +++ b/compiler/circledump/src/Read.cpp @@ -81,6 +81,7 @@ Reader::Reader(const circle::Model *model) _version = model->version(); _subgraphs = model->subgraphs(); _buffers = model->buffers(); + _metadata = model->metadata(); auto opcodes = model->operator_codes(); for (const ::circle::OperatorCode *opcode : *opcodes) diff --git a/compiler/circledump/src/Read.h b/compiler/circledump/src/Read.h index dd1ef20b6..be0e15827 100644 --- a/compiler/circledump/src/Read.h +++ b/compiler/circledump/src/Read.h @@ -52,6 +52,7 @@ private: using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>; using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>; using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>; + using CircleMetadata_t = flatbuffers::Vector<flatbuffers::Offset<circle::Metadata>>; public: Reader(const circle::Model *model); @@ -68,6 +69,7 @@ public: const std::vector<int32_t> &inputs() const { return _inputs; } const std::vector<int32_t> &outputs() const { return _outputs; } const circle::DataFormat &data_format() const { return _data_format; } + const CircleMetadata_t *metadata() const { return _metadata; } uint32_t num_subgraph() const { return _subgraphs->Length(); } @@ -87,6 +89,7 @@ private: const CircleBuffers_t *_buffers{nullptr}; const CircleTensors_t *_tensors{nullptr}; const CircleOperators_t *_operators{nullptr}; + const CircleMetadata_t *_metadata{nullptr}; uint32_t _subgraph_index; std::string _subgraph_name; diff --git a/compiler/cli/CMakeLists.txt b/compiler/cli/CMakeLists.txt index 22948fff9..2ab8c0529 100644 --- a/compiler/cli/CMakeLists.txt +++ b/compiler/cli/CMakeLists.txt @@ -12,4 +12,3 @@ endif(NOT GTest_FOUND) GTest_AddTEst(cli_test ${TESTS}) target_link_libraries(cli_test cli) -target_link_libraries(cli_test stdex) diff --git a/compiler/cli/src/App.test.cpp b/compiler/cli/src/App.test.cpp index fe2d44179..59e5da3bd 100644 --- a/compiler/cli/src/App.test.cpp +++ b/compiler/cli/src/App.test.cpp @@ -16,7 +16,7 @@ #include "cli/App.h" -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> @@ -52,7 +52,7 @@ TEST(APP, run) cli::App app("test"); std::string args; - app.insert("record", stdex::make_unique<RecordCommand>(3, args)); + app.insert("record", std::make_unique<RecordCommand>(3, args)); const char *argv[] = {"record", "hello", "world"}; diff --git a/compiler/coco/core/CMakeLists.txt b/compiler/coco/core/CMakeLists.txt index 8c6844733..a81d366c9 100644 --- a/compiler/coco/core/CMakeLists.txt +++ b/compiler/coco/core/CMakeLists.txt @@ -7,7 +7,6 @@ target_include_directories(coco_core PUBLIC include) # NOTE Some coco_core PUBLIC headers include angkor headers target_link_libraries(coco_core PUBLIC angkor) target_link_libraries(coco_core PRIVATE pepper_assert) -target_link_libraries(coco_core PRIVATE stdex) # Let's apply nncc common compile options # NOTE This will enable strict compilation (warnings as error). # Please refer to top-level CMakeLists.txt for details @@ -22,4 +21,3 @@ nnas_find_package(GTest REQUIRED) GTest_AddTest(coco_core_test ${TESTS}) target_link_libraries(coco_core_test coco_core) -target_link_libraries(coco_core_test stdex) diff --git a/compiler/coco/core/include/coco/IR/FeatureShape.h b/compiler/coco/core/include/coco/IR/FeatureShape.h index 015fc709d..3c8e9accd 100644 --- a/compiler/coco/core/include/coco/IR/FeatureShape.h +++ b/compiler/coco/core/include/coco/IR/FeatureShape.h @@ -31,13 +31,13 @@ class FeatureShape : public nncc::core::ADT::feature::Shape { public: FeatureShape(uint32_t depth, uint32_t height, uint32_t width) - : Shape{depth, height, width}, _batch{1} + : Shape{depth, height, width}, _batch{1} { // DO NOTHING } FeatureShape(uint32_t batch, uint32_t depth, uint32_t height, uint32_t width) - : Shape{depth, height, width}, _batch{batch} + : Shape{depth, height, width}, _batch{batch} { // DO NOTHING } diff --git a/compiler/coco/core/include/coco/IR/Locatable.h b/compiler/coco/core/include/coco/IR/Locatable.h index b80a4a360..549802776 100644 --- a/compiler/coco/core/include/coco/IR/Locatable.h +++ b/compiler/coco/core/include/coco/IR/Locatable.h @@ -24,7 +24,7 @@ namespace coco /** * @brief Return the associated instruction if exists. - */ + */ struct Locatable { virtual ~Locatable() = default; diff --git a/compiler/coco/core/include/coco/IR/Ops.h b/compiler/coco/core/include/coco/IR/Ops.h index 01ac92b7f..39dce5272 100644 --- a/compiler/coco/core/include/coco/IR/Ops.h +++ b/compiler/coco/core/include/coco/IR/Ops.h @@ -407,6 +407,6 @@ public: const Sqrt *asSqrt(void) const override { return this; } }; -} // namesapce coco +} // namespace coco #endif // __COCO_IR_OPS_H__ diff --git a/compiler/coco/core/include/coco/IR/Padding2D.h b/compiler/coco/core/include/coco/IR/Padding2D.h index b764656cc..68a3481f1 100644 --- a/compiler/coco/core/include/coco/IR/Padding2D.h +++ b/compiler/coco/core/include/coco/IR/Padding2D.h @@ -32,7 +32,7 @@ public: public: Padding2D(uint32_t top, uint32_t bottom, uint32_t left, uint32_t right) - : _top{top}, _bottom{bottom}, _left{left}, _right{right} + : _top{top}, _bottom{bottom}, _left{left}, _right{right} { // DO NOTHING } diff --git a/compiler/coco/core/src/ADT/PtrList.test.cpp b/compiler/coco/core/src/ADT/PtrList.test.cpp index dcbad8b90..904dd6e1d 100644 --- a/compiler/coco/core/src/ADT/PtrList.test.cpp +++ b/compiler/coco/core/src/ADT/PtrList.test.cpp @@ -25,7 +25,7 @@ namespace struct Object { }; -} +} // namespace TEST(ADT_PTR_LIST, ctor) { diff --git a/compiler/coco/core/src/ADT/PtrManager.test.cpp b/compiler/coco/core/src/ADT/PtrManager.test.cpp index bb9056f29..5a9f09d4e 100644 --- a/compiler/coco/core/src/ADT/PtrManager.test.cpp +++ b/compiler/coco/core/src/ADT/PtrManager.test.cpp @@ -61,7 +61,7 @@ struct ObjectManager final : public coco::PtrManager<Object> void free(Object *o) { release(o); } }; -} +} // namespace TEST(ADT_PTR_MANAGER, usecase) { diff --git a/compiler/coco/core/src/IR/BagManager.cpp b/compiler/coco/core/src/IR/BagManager.cpp index 10fe69d57..8cfb0c09c 100644 --- a/compiler/coco/core/src/IR/BagManager.cpp +++ b/compiler/coco/core/src/IR/BagManager.cpp @@ -16,14 +16,14 @@ #include "coco/IR/BagManager.h" -#include <stdex/Memory.h> +#include <memory> namespace coco { Bag *BagManager::create(uint32_t size) { - auto bag = stdex::make_unique<Bag>(size); + auto bag = std::make_unique<Bag>(size); modulize(bag.get()); return take(std::move(bag)); } diff --git a/compiler/coco/core/src/IR/BlockManager.cpp b/compiler/coco/core/src/IR/BlockManager.cpp index 5e3b88173..d1bcacb32 100644 --- a/compiler/coco/core/src/IR/BlockManager.cpp +++ b/compiler/coco/core/src/IR/BlockManager.cpp @@ -16,8 +16,7 @@ #include "coco/IR/BlockManager.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace coco @@ -25,7 +24,7 @@ namespace coco Block *BlockManager::create(void) { - auto blk = stdex::make_unique<Block>(); + auto blk = std::make_unique<Block>(); modulize(blk.get()); return take(std::move(blk)); } diff --git a/compiler/coco/core/src/IR/Conv2D.test.cpp b/compiler/coco/core/src/IR/Conv2D.test.cpp index df0a2470b..5bf06ca9f 100644 --- a/compiler/coco/core/src/IR/Conv2D.test.cpp +++ b/compiler/coco/core/src/IR/Conv2D.test.cpp @@ -20,11 +20,9 @@ #include <vector> #include <memory> -#include <stdex/Memory.h> - #include <gtest/gtest.h> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/coco/core/src/IR/Def.test.cpp b/compiler/coco/core/src/IR/Def.test.cpp index 98455c09e..443fdcb95 100644 --- a/compiler/coco/core/src/IR/Def.test.cpp +++ b/compiler/coco/core/src/IR/Def.test.cpp @@ -19,13 +19,13 @@ #include "coco/IR/FeatureObject.h" -#include <stdex/Memory.h> +#include <memory> #include "Producer.mock.h" #include <gtest/gtest.h> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/coco/core/src/IR/InputManager.cpp b/compiler/coco/core/src/IR/InputManager.cpp index 6d5b9470b..0530deeda 100644 --- a/compiler/coco/core/src/IR/InputManager.cpp +++ b/compiler/coco/core/src/IR/InputManager.cpp @@ -16,14 +16,14 @@ #include "coco/IR/InputManager.h" -#include <stdex/Memory.h> +#include <memory> namespace coco { Input *InputManager::create(const nncc::core::ADT::tensor::Shape &shape) { - auto input = stdex::make_unique<Input>(shape); + auto input = std::make_unique<Input>(shape); modulize(input.get()); return take(std::move(input)); } diff --git a/compiler/coco/core/src/IR/Module.cpp b/compiler/coco/core/src/IR/Module.cpp index 0b65ceedc..420cf6f0c 100644 --- a/compiler/coco/core/src/IR/Module.cpp +++ b/compiler/coco/core/src/IR/Module.cpp @@ -16,9 +16,9 @@ #include "coco/IR/Module.h" -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/coco/core/src/IR/ObjectManager.cpp b/compiler/coco/core/src/IR/ObjectManager.cpp index 1b7215a04..38c3a9bcc 100644 --- a/compiler/coco/core/src/IR/ObjectManager.cpp +++ b/compiler/coco/core/src/IR/ObjectManager.cpp @@ -19,11 +19,10 @@ #include "coco/IR/FeatureObject.h" #include "coco/IR/KernelObject.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> -using stdex::make_unique; +using std::make_unique; namespace coco { diff --git a/compiler/coco/core/src/IR/OpManager.cpp b/compiler/coco/core/src/IR/OpManager.cpp index c87b704fe..911f999c7 100644 --- a/compiler/coco/core/src/IR/OpManager.cpp +++ b/compiler/coco/core/src/IR/OpManager.cpp @@ -16,13 +16,12 @@ #include "coco/IR/OpManager.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> #include <queue> #include <set> -using stdex::make_unique; +using std::make_unique; namespace coco { diff --git a/compiler/coco/core/src/IR/Ops.test.cpp b/compiler/coco/core/src/IR/Ops.test.cpp index ae979b2bf..cfbd3ca70 100644 --- a/compiler/coco/core/src/IR/Ops.test.cpp +++ b/compiler/coco/core/src/IR/Ops.test.cpp @@ -21,11 +21,9 @@ #include <vector> #include <memory> -#include <stdex/Memory.h> - #include <gtest/gtest.h> -using stdex::make_unique; +using std::make_unique; /** * Section: Add Op diff --git a/compiler/coco/core/src/IR/OutputManager.cpp b/compiler/coco/core/src/IR/OutputManager.cpp index 86b9580ac..5dd51c378 100644 --- a/compiler/coco/core/src/IR/OutputManager.cpp +++ b/compiler/coco/core/src/IR/OutputManager.cpp @@ -16,14 +16,14 @@ #include "coco/IR/OutputManager.h" -#include <stdex/Memory.h> +#include <memory> namespace coco { Output *OutputManager::create(const nncc::core::ADT::tensor::Shape &shape) { - auto output = stdex::make_unique<Output>(shape); + auto output = std::make_unique<Output>(shape); modulize(output.get()); return take(std::move(output)); } diff --git a/compiler/coco/core/src/IR/Part.test.cpp b/compiler/coco/core/src/IR/Part.test.cpp index 87e0e1516..4348d4db2 100644 --- a/compiler/coco/core/src/IR/Part.test.cpp +++ b/compiler/coco/core/src/IR/Part.test.cpp @@ -17,11 +17,11 @@ #include "coco/IR/Part.h" #include "coco/IR/Op.h" -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/coco/core/src/IR/Use.test.cpp b/compiler/coco/core/src/IR/Use.test.cpp index 3191e9852..b7026385f 100644 --- a/compiler/coco/core/src/IR/Use.test.cpp +++ b/compiler/coco/core/src/IR/Use.test.cpp @@ -21,11 +21,11 @@ #include "Consumer.mock.h" -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/coco/generic/CMakeLists.txt b/compiler/coco/generic/CMakeLists.txt index 02fbf67f5..c65c84c06 100644 --- a/compiler/coco/generic/CMakeLists.txt +++ b/compiler/coco/generic/CMakeLists.txt @@ -5,7 +5,6 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_library(coco_generic SHARED ${SOURCES}) target_include_directories(coco_generic PUBLIC include) target_link_libraries(coco_generic PUBLIC coco_core) -target_link_libraries(coco_generic PRIVATE stdex) target_link_libraries(coco_generic PRIVATE nncc_common) if(NOT ENABLE_TEST) @@ -17,6 +16,3 @@ nnas_find_package(GTest REQUIRED) GTest_AddTest(coco_generic_test ${TESTS}) target_link_libraries(coco_generic_test coco_generic) -# stdex is a PRIVATE dependency of coco_generic, and thus is not linked to coco_generic_test -# even though coco_generic_test is linked to coco_generic -target_link_libraries(coco_generic_test stdex) diff --git a/compiler/coco/generic/src/IR/Data.cpp b/compiler/coco/generic/src/IR/Data.cpp index b71947253..5ab7069ee 100644 --- a/compiler/coco/generic/src/IR/Data.cpp +++ b/compiler/coco/generic/src/IR/Data.cpp @@ -19,13 +19,12 @@ #include <nncc/core/ADT/kernel/NCHWLayout.h> #include <nncc/core/ADT/kernel/Overlay.h> -#include <stdex/Memory.h> - +#include <memory> #include <map> using namespace nncc::core::ADT; -using stdex::make_unique; +using std::make_unique; namespace { @@ -71,7 +70,7 @@ public: private: std::map<const coco::Bag *, std::unique_ptr<std::vector<uint8_t>>> _data; }; -} +} // namespace namespace { diff --git a/compiler/common-artifacts/CMakeLists.txt b/compiler/common-artifacts/CMakeLists.txt index ec9e3cf85..e93a66ef0 100644 --- a/compiler/common-artifacts/CMakeLists.txt +++ b/compiler/common-artifacts/CMakeLists.txt @@ -171,9 +171,9 @@ foreach(RECIPE IN ITEMS ${RECIPES}) if(DEFINED RULE_SOURCE_PATH) # Copy .rule add_custom_command(OUTPUT ${RULE_BINARY_PATH} - COMMAND ${CMAKE_COMMAND} -E copy "${RULE_SOURCE_PATH}" "${RULE_BINARY_PATH}" - DEPENDS ${RULE_SOURCE_PATH} - COMMENT "Generate ${RULE_FILE}" + COMMAND ${CMAKE_COMMAND} -E copy "${RULE_SOURCE_PATH}" "${RULE_BINARY_PATH}" + DEPENDS ${RULE_SOURCE_PATH} + COMMENT "Generate ${RULE_FILE}" ) list(APPEND TEST_DEPS ${RULE_BINARY_PATH}) endif() @@ -188,21 +188,21 @@ foreach(RECIPE IN ITEMS ${RECIPES}) list(APPEND TEST_DEPS ${TFLITE_OUTPUT_PATH}) if(NOT DEFINED NO_CIRCLIZE_${RECIPE}) - # Generate .circle - add_custom_command(OUTPUT ${CIRCLE_OUTPUT_PATH} - COMMAND $<TARGET_FILE:tflite2circle> ${TFLITE_OUTPUT_PATH} ${CIRCLE_OUTPUT_PATH} - DEPENDS $<TARGET_FILE:tflite2circle> ${TFLITE_OUTPUT_PATH} - COMMENT "Generate ${CIRCLE_FILE}" - ) - set(MODEL_FORMAT "circle") - list(APPEND TEST_DEPS ${CIRCLE_OUTPUT_PATH}) + # Generate .circle + add_custom_command(OUTPUT ${CIRCLE_OUTPUT_PATH} + COMMAND $<TARGET_FILE:tflite2circle> ${TFLITE_OUTPUT_PATH} ${CIRCLE_OUTPUT_PATH} + DEPENDS $<TARGET_FILE:tflite2circle> ${TFLITE_OUTPUT_PATH} + COMMENT "Generate ${CIRCLE_FILE}" + ) + set(MODEL_FORMAT "circle") + list(APPEND TEST_DEPS ${CIRCLE_OUTPUT_PATH}) endif() else() # Generate .circle add_custom_command(OUTPUT ${CIRCLE_OUTPUT_PATH} - COMMAND $<TARGET_FILE:circlechef-file> ${RECIPE_BINARY_PATH} ${CIRCLE_OUTPUT_PATH} - DEPENDS $<TARGET_FILE:circlechef-file> ${RECIPE_BINARY_PATH} - COMMENT "Generate ${CIRCLE_FILE}" + COMMAND $<TARGET_FILE:circlechef-file> ${RECIPE_BINARY_PATH} ${CIRCLE_OUTPUT_PATH} + DEPENDS $<TARGET_FILE:circlechef-file> ${RECIPE_BINARY_PATH} + COMMENT "Generate ${CIRCLE_FILE}" ) list(APPEND TEST_DEPS ${CIRCLE_OUTPUT_PATH}) endif() @@ -213,7 +213,7 @@ foreach(RECIPE IN ITEMS ${RECIPES}) if(NOT DEFINED NO_OPTIMIZE_${RECIPE}) # Generate optimized .circle add_custom_command(OUTPUT ${OPT_CIRCLE_OUTPUT_PATH} - COMMAND $<TARGET_FILE:circle2circle> --all ${CIRCLE_OUTPUT_PATH} ${OPT_CIRCLE_OUTPUT_PATH} + COMMAND $<TARGET_FILE:circle2circle> --O1 ${CIRCLE_OUTPUT_PATH} ${OPT_CIRCLE_OUTPUT_PATH} DEPENDS $<TARGET_FILE:circle2circle> ${CIRCLE_OUTPUT_PATH} COMMENT "Generate ${OPT_CIRCLE_FILE}" ) @@ -224,54 +224,43 @@ foreach(RECIPE IN ITEMS ${RECIPES}) set(MODEL_FILE "${RECIPE}${OPT_FORMAT}.${MODEL_FORMAT}") set(MODEL_PATH "${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE}") set(NNPKG_FILE "${RECIPE}${OPT_FORMAT}") - set(NNPKG_PATH "${CMAKE_CURRENT_BINARY_DIR}/${NNPKG_FILE}") + set(NNPKG_DIR "${CMAKE_CURRENT_BINARY_DIR}/${NNPKG_FILE}") + set(NNPKG_MODEL "${NNPKG_DIR}/${MODEL_FILE}") + + # Generate nnpackage directory + add_custom_command(OUTPUT ${NNPKG_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${NNPKG_DIR} + DEPENDS ${MODEL_PATH} + COMMENT "Generate ${RECIPE} nnpackage directory" + ) + list(APPEND TEST_DEPS ${NNPKG_DIR}) - add_custom_command(OUTPUT ${NNPKG_PATH} + add_custom_command(OUTPUT ${NNPKG_MODEL} COMMAND ${MODEL2NNPKG} ${MODEL_PATH} - DEPENDS ${MODEL2NNPKG} ${MODEL_PATH} + DEPENDS ${MODEL2NNPKG} ${MODEL_PATH} ${NNPKG_DIR} COMMENT "Generate ${RECIPE} nnpackage" ) - list(APPEND TEST_DEPS ${NNPKG_PATH}) - - set(INPUT_HDF5_FILE "${RECIPE}${OPT_FORMAT}.input.h5") - set(INPUT_BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/${INPUT_HDF5_FILE}") - - set(EXPECTED_HDF5_FILE "${RECIPE}${OPT_FORMAT}.expected.h5") - set(EXPECTED_BIN_PATH "${CMAKE_CURRENT_BINARY_DIR}/${EXPECTED_HDF5_FILE}") + list(APPEND TEST_DEPS ${NNPKG_MODEL}) if(NOT DEFINED NO_TCGEN_${RECIPE}) - # Generate input.h5, expected.h5 - add_custom_command(OUTPUT ${INPUT_BIN_PATH} ${EXPECTED_BIN_PATH} - COMMAND $<TARGET_FILE:testDataGenerator> ${MODEL_FILE} - DEPENDS $<TARGET_FILE:testDataGenerator> ${MODEL_FILE} - COMMENT "Generate ${INPUT_BIN_PATH} and ${EXPECTED_BIN_PATH}" - ) - # Generate test directory - set(TC_DIRECTORY "${NNPKG_PATH}/metadata/tc") + set(TC_DIRECTORY "${NNPKG_DIR}/metadata/tc") add_custom_command(OUTPUT ${TC_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E make_directory ${TC_DIRECTORY} - DEPENDS ${NNPKG_PATH} + DEPENDS ${NNPKG_DIR} COMMENT "Generate ${RECIPE} nnpackage test directory" ) + list(APPEND TEST_DEPS ${TC_DIRECTORY}) - # Move input hdf5 file to test directory - set(INPUT_NNPKG_PATH "${TC_DIRECTORY}/input.h5") - add_custom_command(OUTPUT ${INPUT_NNPKG_PATH} - COMMAND ${CMAKE_COMMAND} -E rename ${INPUT_BIN_PATH} ${INPUT_NNPKG_PATH} - DEPENDS ${INPUT_BIN_PATH} ${TC_DIRECTORY} - COMMENT "Move ${INPUT_HDF5_FILE} to nnpackage" - ) - - # Move expected hdf5 file to test directory - set(EXPECTED_NNPKG_PATH "${TC_DIRECTORY}/expected.h5") - add_custom_command(OUTPUT ${EXPECTED_NNPKG_PATH} - COMMAND ${CMAKE_COMMAND} -E rename ${EXPECTED_BIN_PATH} ${EXPECTED_NNPKG_PATH} - DEPENDS ${EXPECTED_BIN_PATH} ${TC_DIRECTORY} - COMMENT "Move ${EXPECTED_HDF5_FILE} to nnpackage" + # Generate input.h5, expected.h5 + set(INPUT_HDF5_FILE "${TC_DIRECTORY}/input.h5") + set(EXPECTED_HDF5_FILE "${TC_DIRECTORY}/expected.h5") + add_custom_command(OUTPUT ${INPUT_HDF5_FILE} ${EXPECTED_HDF5_FILE} + COMMAND $<TARGET_FILE:testDataGenerator> --input_data ${INPUT_HDF5_FILE} --expected_data ${EXPECTED_HDF5_FILE} ${MODEL_FILE} + DEPENDS $<TARGET_FILE:testDataGenerator> ${MODEL_FILE} ${TC_DIRECTORY} + COMMENT "Generate ${INPUT_HDF5_FILE} and ${EXPECTED_HDF5_FILE}" ) - list(APPEND TEST_DEPS ${TC_DIRECTORY} ${INPUT_BIN_PATH} ${EXPECTED_BIN_PATH} - ${INPUT_NNPKG_PATH} ${EXPECTED_NNPKG_PATH}) + list(APPEND TEST_DEPS ${INPUT_HDF5_FILE} ${EXPECTED_HDF5_FILE}) endif() endforeach() diff --git a/compiler/common-artifacts/exclude.lst b/compiler/common-artifacts/exclude.lst index 34a4d2c6a..b9b758fe7 100644 --- a/compiler/common-artifacts/exclude.lst +++ b/compiler/common-artifacts/exclude.lst @@ -28,6 +28,7 @@ tcgenerate(BatchMatMul_000) tcgenerate(BatchMatMulV2_000) tcgenerate(BatchMatMulV2_001) tcgenerate(BatchToSpaceND_000) +tcgenerate(BroadcastTo_000) # luci-interpreter doesn't support custom operator tcgenerate(Cast_000) tcgenerate(Cast_001) tcgenerate(Ceil_000) @@ -41,6 +42,8 @@ tcgenerate(ExpandDims_000) tcgenerate(ExpandDims_001) tcgenerate(ExpandDims_002) tcgenerate(ExpandDims_003) +tcgenerate(ExpandDims_004) +tcgenerate(FakeQuant_000) # runtime and luci-interpreter doesn't support yet tcgenerate(Fill_000) tcgenerate(Fill_001) tcgenerate(FloorMod_000) @@ -60,6 +63,9 @@ tcgenerate(MatrixSetDiag_000) tcgenerate(MaxPoolWithArgMax_000) tcgenerate(MaxPoolWithArgMax_001) tcgenerate(MaxPoolWithArgMax_002) +tcgenerate(Mean_dynamic_000) # TestDataGenerator does not support unknown dimension +tcgenerate(Mean_dynamic_001) # TestDataGenerator does not support unknown dimension +tcgenerate(Mean_U8_dynamic_000) # TestDataGenerator does not support unknown dimension tcgenerate(NonMaxSuppressionV4_000) tcgenerate(NonMaxSuppressionV4_001) tcgenerate(NonMaxSuppressionV5_000) @@ -67,10 +73,8 @@ tcgenerate(NonMaxSuppressionV5_001) tcgenerate(MirrorPad_000) tcgenerate(Mul_U8_000) tcgenerate(Neg_000) +tcgenerate(Net_BroadcastTo_AddV2_001) # luci-interpreter doesn't support custom operator tcgenerate(Net_Dangle_001) -tcgenerate(Net_InstanceNorm_001) -tcgenerate(Net_InstanceNorm_002) -tcgenerate(Net_InstanceNorm_003) tcgenerate(Net_ZeroDim_001) # luci-interpreter doesn't support zero dim tcgenerate(OneHot_000) tcgenerate(OneHot_001) @@ -85,24 +89,26 @@ tcgenerate(ReduceAny_000) tcgenerate(ReduceAny_001) tcgenerate(ReduceAny_002) tcgenerate(ReduceAny_003) -tcgenerate(ReduceAny_dynamic_000) -tcgenerate(ReduceAny_dynamic_001) -tcgenerate(ReduceAny_dynamic_002) -tcgenerate(ReduceAny_dynamic_003) +tcgenerate(ReduceAny_dynamic_000) # TestDataGenerator does not support unknown dimension +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(ReduceMax_000) -tcgenerate(ReduceMax_dynamic_000) +tcgenerate(ReduceMax_dynamic_000) # TestDataGenerator does not support unknown dimension tcgenerate(ReduceMin_000) -tcgenerate(ReduceMin_dynamic_000) +tcgenerate(ReduceMin_dynamic_000) # TestDataGenerator does not support unknown dimension tcgenerate(ReduceProd_000) tcgenerate(ReduceProd_001) tcgenerate(ReduceProd_002) tcgenerate(ReduceProd_003) -tcgenerate(ReduceProd_dynamic_000) -tcgenerate(ReduceProd_dynamic_001) -tcgenerate(ReduceProd_dynamic_002) -tcgenerate(ReduceProd_dynamic_003) +tcgenerate(ReduceProd_dynamic_000) # TestDataGenerator does not support unknown dimension +tcgenerate(ReduceProd_dynamic_001) # TestDataGenerator does not support unknown dimension +tcgenerate(ReduceProd_dynamic_002) # TestDataGenerator does not support unknown dimension +tcgenerate(ReduceProd_dynamic_003) # TestDataGenerator does not support unknown dimension +tcgenerate(ReLU_dynamic_000) # TestDataGenerator does not support unknown dimension +tcgenerate(ReLU6_dynamic_000) # TestDataGenerator does not support unknown dimension tcgenerate(ReLUN1To1_000) -tcgenerate(ReLUN1To1_dynamic_000) +tcgenerate(ReLUN1To1_dynamic_000) # TestDataGenerator does not support unknown dimension tcgenerate(Reshape_003) # luci-interpreter doesn't support reshape without built-in option tcgenerate(ReverseSequence_000) tcgenerate(ReverseV2_000) @@ -117,6 +123,7 @@ tcgenerate(SelectV2_001) tcgenerate(SelectV2_002) tcgenerate(Shape_000) tcgenerate(Sin_000) +tcgenerate(Slice_001) # luci-interpreter doesn't support Slice with -1 tcgenerate(SpaceToBatchND_000) tcgenerate(SpaceToBatchND_001) tcgenerate(SpaceToBatchND_002) @@ -124,11 +131,10 @@ tcgenerate(SpaceToBatchND_003) tcgenerate(SparseToDense_000) tcgenerate(SplitV_000) tcgenerate(Square_000) -tcgenerate(SquaredDifference_000) tcgenerate(Sum_000) tcgenerate(Sum_001) -tcgenerate(Sum_dynamic_000) -tcgenerate(Sum_dynamic_001) +tcgenerate(Sum_dynamic_000) # TestDataGenerator does not support unknown dimension +tcgenerate(Sum_dynamic_001) # TestDataGenerator does not support unknown dimension tcgenerate(Tile_000) tcgenerate(Tile_U8_000) tcgenerate(TopKV2_000) diff --git a/compiler/common-artifacts/src/TestDataGenerator.cpp b/compiler/common-artifacts/src/TestDataGenerator.cpp index f8f014442..be6bb5ba9 100644 --- a/compiler/common-artifacts/src/TestDataGenerator.cpp +++ b/compiler/common-artifacts/src/TestDataGenerator.cpp @@ -34,7 +34,7 @@ namespace uint32_t element_num(std::vector<hsize_t> &vec) { return static_cast<uint32_t>( - std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<uint32_t>())); + std::accumulate(std::begin(vec), std::end(vec), 1, std::multiplies<uint32_t>())); } H5::PredType hdf5_dtype_cast(const loco::DataType loco_dtype) @@ -94,10 +94,20 @@ int entry(int argc, char **argv) { arser::Arser arser; arser.add_argument("circle").type(arser::DataType::STR).help("Circle file you want to test"); + arser.add_argument("--input_data") + .required(true) + .nargs(1) + .type(arser::DataType::STR) + .help("Path to generate input data h5 file"); + arser.add_argument("--expected_data") + .required(true) + .nargs(1) + .type(arser::DataType::STR) + .help("Path to generate expected data h5 file"); arser.add_argument("--fixed_seed") - .required(false) - .nargs(0) - .help("Put a fixed seed into the random number generator"); + .required(false) + .nargs(0) + .help("Put a fixed seed into the random number generator"); try { @@ -111,8 +121,6 @@ int entry(int argc, char **argv) } std::string circle_file = arser.get<std::string>("circle"); - size_t last_dot_index = circle_file.find_last_of("."); - std::string prefix = circle_file.substr(0, last_dot_index); // load circle file foder::FileLoader file_loader{circle_file}; @@ -144,17 +152,17 @@ int entry(int argc, char **argv) * ã„´DATA ... */ // create random data and dump into hdf5 file - H5::H5File input_file{prefix + ".input.h5", H5F_ACC_TRUNC}; + H5::H5File input_file{arser.get<std::string>("--input_data"), H5F_ACC_TRUNC}; std::unique_ptr<H5::Group> input_name_group = - std::make_unique<H5::Group>(input_file.createGroup("name")); + std::make_unique<H5::Group>(input_file.createGroup("name")); std::unique_ptr<H5::Group> input_value_group = - std::make_unique<H5::Group>(input_file.createGroup("value")); + std::make_unique<H5::Group>(input_file.createGroup("value")); - H5::H5File output_file{prefix + ".expected.h5", H5F_ACC_TRUNC}; + H5::H5File output_file{arser.get<std::string>("--expected_data"), H5F_ACC_TRUNC}; std::unique_ptr<H5::Group> output_name_group = - std::make_unique<H5::Group>(output_file.createGroup("name")); + std::make_unique<H5::Group>(output_file.createGroup("name")); std::unique_ptr<H5::Group> output_value_group = - std::make_unique<H5::Group>(output_file.createGroup("value")); + std::make_unique<H5::Group>(output_file.createGroup("value")); std::random_device rd; // used to obtain a seed for the random number engine uint32_t input_index = 0; @@ -187,7 +195,7 @@ int entry(int argc, char **argv) auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data()); auto dtype = hdf5_dtype_cast(input_node->dtype()); auto dataset = std::make_unique<H5::DataSet>( - input_file.createDataSet("value/" + std::to_string(input_index), dtype, *dataspace)); + input_file.createDataSet("value/" + std::to_string(input_index), dtype, *dataspace)); auto data_size = ::element_num(dims); auto dtype_size = loco::size(input_node->dtype()); @@ -241,7 +249,7 @@ int entry(int argc, char **argv) auto dataspace = std::make_unique<H5::DataSpace>(dims.size(), dims.data()); auto dtype = hdf5_dtype_cast(output_node->dtype()); auto dataset = std::make_unique<H5::DataSet>( - output_file.createDataSet("value/" + std::to_string(output_index), dtype, *dataspace)); + output_file.createDataSet("value/" + std::to_string(output_index), dtype, *dataspace)); uint32_t tensor_bytesize = loco::size(output_node->dtype()); tensor_bytesize *= ::element_num(dims); diff --git a/compiler/crew/CMakeLists.txt b/compiler/crew/CMakeLists.txt new file mode 100644 index 000000000..1824d86ab --- /dev/null +++ b/compiler/crew/CMakeLists.txt @@ -0,0 +1,20 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(crew STATIC ${SOURCES}) +target_include_directories(crew PRIVATE src) +target_include_directories(crew PUBLIC include) +target_link_libraries(crew PRIVATE foder) +target_link_libraries(crew PRIVATE nncc_common) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(crew_test ${TESTS}) +target_include_directories(crew_test PRIVATE src) +target_link_libraries(crew_test nncc_common) +target_link_libraries(crew_test crew) diff --git a/compiler/crew/README.md b/compiler/crew/README.md new file mode 100644 index 000000000..29691929d --- /dev/null +++ b/compiler/crew/README.md @@ -0,0 +1,13 @@ +# crew + +_crew_ is circle partitioning Configuration REader and Writer library. + +### Support formats + +Current _crew_ supports below format and functionalities. +- INI read +- INI write +- JSON write + +_crew_ supports limited portion of JSON and INI formats just enough to access +circle partition configuration files. diff --git a/compiler/crew/include/crew/PConfig.h b/compiler/crew/include/crew/PConfig.h new file mode 100644 index 000000000..9ff875574 --- /dev/null +++ b/compiler/crew/include/crew/PConfig.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 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 __CREW_PCONFIG_H__ +#define __CREW_PCONFIG_H__ + +#include <iostream> +#include <string> +#include <vector> + +namespace crew +{ + +struct Part +{ + std::string model_file; + std::vector<std::string> inputs; + std::vector<std::string> outputs; +}; + +using Parts = std::vector<Part>; +using Source = Part; + +struct PConfig +{ + Source source; + Parts parts; +}; + +/** + * @brief Read config as ini file, return false if failed + */ +bool read_ini(const std::string &path, PConfig &config); + +/** + * @brief Write config as ini file, return false if failed + */ +bool write_ini(std::ostream &os, const PConfig &config); + +/** + * @brief Write config as json file, return false if failed + */ +bool write_json(std::ostream &os, const PConfig &config); + +} // namespace crew + +#endif // __CREW_PCONFIG_H__ diff --git a/compiler/crew/include/crew/PConfigIni.h b/compiler/crew/include/crew/PConfigIni.h new file mode 100644 index 000000000..45a54e115 --- /dev/null +++ b/compiler/crew/include/crew/PConfigIni.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 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 __CREW_PCONFIG_INI_H__ +#define __CREW_PCONFIG_INI_H__ + +#include <iostream> +#include <string> +#include <unordered_map> +#include <vector> + +namespace crew +{ + +using KeyValues = std::unordered_map<std::string, std::string>; + +struct Section +{ + std::string name; + KeyValues items; +}; + +using Sections = std::vector<Section>; + +/** + * @brief Reads Config INI from null terminated string and return Sections + */ +Sections read_ini(const char *data, size_t length); +/** + * @brief Reads Config INI from file and return Sections + */ +Sections read_ini(const std::string &path); + +/** + * @brief Write Config INI with Sections to ostream + */ +void write_ini(std::ostream &os, const Sections §ions); +/** + * @brief Write Config INI with Sections to file, throw if failed + */ +void write_ini(const std::string &path, const Sections §ions); + +/** + * @brief Find a section with name, empty section if not found + */ +Section find(const Sections §ions, const std::string &name); + +/** + * @brief Find a key-value pair from key and return value, empty string if not found + */ +std::string find(const Section §ion, const std::string &key); + +} // namespace crew + +#endif // __CREW_PCONFIG_INI_H__ diff --git a/compiler/crew/include/crew/PConfigIniDump.h b/compiler/crew/include/crew/PConfigIniDump.h new file mode 100644 index 000000000..0755c6b20 --- /dev/null +++ b/compiler/crew/include/crew/PConfigIniDump.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 __CREW_PCONFIG_INI_DUMP_H__ +#define __CREW_PCONFIG_INI_DUMP_H__ + +#include "PConfigIni.h" + +#include <iostream> + +namespace crew +{ + +void dump(std::ostream &os, const Sections §ions); + +} // namespace crew + +std::ostream &operator<<(std::ostream &os, const crew::Sections §ions); + +#endif // __CREW_PCONFIG_INI_DUMP_H__ diff --git a/compiler/crew/requires.cmake b/compiler/crew/requires.cmake new file mode 100644 index 000000000..27406d465 --- /dev/null +++ b/compiler/crew/requires.cmake @@ -0,0 +1 @@ +require("foder") diff --git a/compiler/crew/src/PConfig.cpp b/compiler/crew/src/PConfig.cpp new file mode 100644 index 000000000..b8e7c3e44 --- /dev/null +++ b/compiler/crew/src/PConfig.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2021 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 "crew/PConfig.h" +#include "crew/PConfigIni.h" + +#include "PConfigJson.h" + +#include <utility> + +namespace +{ + +bool read_part(const crew::Section §ion, crew::Part &part) +{ + // construct Source from section_source + part.model_file = crew::find(section, "file"); + if (part.model_file.empty()) + return false; + + // read inputs for Source + for (int32_t i = 1;; ++i) + { + std::string item = "i" + std::to_string(i); + std::string input = crew::find(section, item); + if (input.empty()) + break; + + part.inputs.push_back(input); + } + // read outputs for Source + for (int32_t i = 1;; ++i) + { + std::string item = "o" + std::to_string(i); + std::string output = crew::find(section, item); + if (output.empty()) + break; + + part.outputs.push_back(output); + } + return true; +} + +} // namespace + +namespace +{ + +void write_part(crew::JsonExport &je, const crew::Part &part) +{ + std::vector<std::string> graph_inputs; + std::vector<std::string> graph_outputs; + + for (auto &input : part.inputs) + { + graph_inputs.push_back(input); + } + for (auto &output : part.outputs) + { + graph_outputs.push_back(output); + } + + je.key_val("file", part.model_file.c_str(), true); + je.key_val("inputs", graph_inputs, true); + je.key_val("outputs", graph_outputs, false); +} + +void write_parts(crew::JsonExport &je, const crew::Parts &parts) +{ + uint32_t idx = 1; + uint32_t size = parts.size(); + for (auto &part : parts) + { + je.open_brace(); + write_part(je, part); + je.close_brace(idx < size); + idx++; + } +} + +} // namespace + +namespace +{ + +void part_to_section_io(const crew::Part &part, crew::Section §ion) +{ + uint32_t idx = 1; + for (auto &input : part.inputs) + { + std::string key = "i" + std::to_string(idx); + section.items.emplace(key, input); + idx++; + } + idx = 1; + for (auto &output : part.outputs) + { + std::string key = "o" + std::to_string(idx); + section.items.emplace(key, output); + idx++; + } +} + +} // namespace + +namespace crew +{ + +bool read_ini(const std::string &path, PConfig &pconfig) +{ + auto sections = crew::read_ini(path); + + auto section_source = crew::find(sections, "source"); + auto section_models = crew::find(sections, "models"); + if (section_source.name != "source" || section_models.name != "models") + { + return false; + } + + if (!read_part(section_source, pconfig.source)) + { + return false; + } + + // get models list + std::vector<std::string> models; + for (int32_t i = 1;; ++i) + { + std::string item = "m" + std::to_string(i); + std::string model = crew::find(section_models, item); + if (model.empty()) + break; + + models.push_back(model); + } + + for (auto &model : models) + { + auto section_model = crew::find(sections, model); + + Part part; + if (!read_part(section_model, part)) + { + return false; + } + pconfig.parts.push_back(part); + } + + return true; +} + +bool write_ini(std::ostream &os, const PConfig &pconfig) +{ + crew::Sections sections; + + // make [source] + crew::Section section_source; + section_source.name = "source"; + section_source.items["file"] = pconfig.source.model_file; + part_to_section_io(pconfig.source, section_source); + sections.push_back(section_source); + + // make [models] + crew::Section section_models; + section_models.name = "models"; + uint32_t idx = 1; + for (auto &part : pconfig.parts) + { + std::string key = "m" + std::to_string(idx); + section_models.items[key] = part.model_file; + idx++; + } + sections.push_back(section_models); + + for (auto &part : pconfig.parts) + { + // make circle model section + crew::Section section_model; + section_model.name = part.model_file; + section_model.items["file"] = part.model_file; + part_to_section_io(part, section_model); + sections.push_back(section_model); + } + + write_ini(os, sections); + + return true; +} + +bool write_json(std::ostream &os, const PConfig &pconfig) +{ + crew::JsonExport je(os); + + je.open_brace(); + { + je.open_brace("source"); + write_part(je, pconfig.source); + je.close_brace(true); + } + { + je.open_bracket("parts"); + write_parts(je, pconfig.parts); + je.close_bracket(false); + } + je.close_brace(false); + + return true; +} + +} // namespace crew diff --git a/compiler/crew/src/PConfigIni.cpp b/compiler/crew/src/PConfigIni.cpp new file mode 100644 index 000000000..f0e3e8e01 --- /dev/null +++ b/compiler/crew/src/PConfigIni.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 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 "crew/PConfigIni.h" +#include "crew/PConfigIniDump.h" + +#include <foder/FileLoader.h> + +#include <cassert> +#include <cstring> +#include <fstream> +#include <memory> +#include <sstream> +#include <stdexcept> +#include <string> + +namespace crew +{ + +Sections read_ini(const char *data, size_t length) +{ + assert(data != nullptr); + assert(length > 0); + + auto buffer = std::vector<char>(); + buffer.reserve(length + 1); + char *pbuffer = buffer.data(); + memcpy(pbuffer, data, length); + // add null at end to be sure + *(pbuffer + length) = 0; + + Sections sections; + Section section; + + std::string string_line; + + const char *delim = "\r\n"; + const char *one_line = std::strtok(pbuffer, delim); + while (one_line != nullptr) + { + if (*one_line == '[') + { + if (!section.name.empty()) + { + sections.push_back(section); + } + section.name.clear(); + section.items.clear(); + + string_line = one_line + 1; + auto pos = string_line.find(']'); + assert(pos != std::string::npos); + if (pos != std::string::npos) + { + section.name = string_line.substr(0, pos); + } + } + else if (*one_line == '#' || *one_line == ';') + { + // Comment line, do nothing + } + else if (*one_line) // string legnth is not 0 + { + if (section.name.empty()) + throw std::runtime_error("Invalid INI file"); + + string_line = one_line; + auto pos = string_line.find('='); + assert(pos != std::string::npos); + if (pos != std::string::npos) + { + auto key = string_line.substr(0, pos); + auto val = string_line.substr(pos + 1); + section.items.emplace(key, val); + } + } + + one_line = std::strtok(nullptr, delim); + } + if (!section.name.empty()) + { + sections.push_back(section); + } + + return sections; +} + +Sections read_ini(const std::string &path) +{ + foder::FileLoader file_loader{path}; + // load will throw if error while opening + auto ini_data = file_loader.load(); + + return read_ini(ini_data.data(), ini_data.size()); +} + +void write_ini(std::ostream &os, const Sections §ions) +{ + std::stringstream ss; + + ss << sections; + + std::string strss = ss.str(); + + os.write(strss.c_str(), strss.length()); +} + +void write_ini(const std::string &filepath, const Sections §ions) +{ + std::ofstream fs(filepath.c_str(), std::ofstream::binary | std::ofstream::trunc); + if (not fs.good()) + { + std::string msg = "Failed to create file: " + filepath; + throw std::runtime_error(msg); + } + + write_ini(fs, sections); + + fs.close(); +} + +Section find(const Sections §ions, const std::string &name) +{ + for (auto §ion : sections) + { + if (section.name == name) + return section; + } + Section not_found; + return not_found; +} + +std::string find(const Section §ion, const std::string &key) +{ + for (auto &item : section.items) + { + if (item.first == key) + return item.second; + } + return ""; +} + +} // namespace crew diff --git a/compiler/crew/src/PConfigIni.test.cpp b/compiler/crew/src/PConfigIni.test.cpp new file mode 100644 index 000000000..bdd2ccc1f --- /dev/null +++ b/compiler/crew/src/PConfigIni.test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 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 "crew/PConfigIni.h" +#include "crew/PConfigIniDump.h" + +#include <gtest/gtest.h> + +#include <sstream> +#include <stdexcept> + +TEST(ConfigIniTest, read_ini_non_exist_file) +{ + EXPECT_THROW(crew::read_ini("/hello/world/not_a_file"), std::runtime_error); +} + +TEST(ConfigIniTest, read_ini_simple) +{ + std::stringstream ss; + + ss << "[hello]\nkey=world\n"; + + auto str = ss.str(); + auto sections = crew::read_ini(str.c_str(), str.length()); + ASSERT_EQ(1UL, sections.size()); + + auto its = sections.begin(); + ASSERT_NE(sections.end(), its); + EXPECT_TRUE("hello" == its->name); + ASSERT_EQ(1UL, its->items.size()); + + auto it = its->items.begin(); + ASSERT_NE(its->items.end(), it); + EXPECT_TRUE("key" == it->first); + EXPECT_TRUE("world" == it->second); +} + +TEST(ConfigIniTest, read_ini_simple_NEG) +{ + std::stringstream ss; + + ss << "key=value\nhello=world\n"; + + auto str = ss.str(); + + EXPECT_THROW(crew::read_ini(str.c_str(), str.length()), std::runtime_error); +} + +TEST(ConfigIniTest, read_ini_comment) +{ + std::stringstream ss; + + ss << "[hello]\n;comment=skip\n#comment=skip\nkey=world\n"; + + auto str = ss.str(); + auto sections = crew::read_ini(str.c_str(), str.length()); + ASSERT_EQ(1UL, sections.size()); + + auto its = sections.begin(); + ASSERT_NE(sections.end(), its); + EXPECT_TRUE("hello" == its->name); + ASSERT_EQ(1UL, its->items.size()); + + auto it = its->items.begin(); + ASSERT_NE(its->items.end(), it); + EXPECT_TRUE("key" == it->first); + EXPECT_TRUE("world" == it->second); +} + +TEST(ConfigIniTest, write_ini_file_error_NEG) +{ + crew::Sections sections; + EXPECT_THROW(crew::write_ini("/abc/def/cannot_access", sections), std::runtime_error); +} diff --git a/compiler/crew/src/PConfigIniDump.cpp b/compiler/crew/src/PConfigIniDump.cpp new file mode 100644 index 000000000..5b7a1cb6d --- /dev/null +++ b/compiler/crew/src/PConfigIniDump.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 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 "crew/PConfigIniDump.h" + +namespace crew +{ + +/** + * @brief Dump content of sections + */ +void dump(std::ostream &os, const Sections §ions) +{ + for (auto §ion : sections) + { + os << "[" << section.name << "]" << std::endl; + for (auto &item : section.items) + { + os << item.first << "=" << item.second << std::endl; + } + os << std::endl; + } +} + +} // namespace crew + +std::ostream &operator<<(std::ostream &os, const crew::Sections §ions) +{ + crew::dump(os, sections); + return os; +} diff --git a/compiler/crew/src/PConfigIniDump.test.cpp b/compiler/crew/src/PConfigIniDump.test.cpp new file mode 100644 index 000000000..25cf4736b --- /dev/null +++ b/compiler/crew/src/PConfigIniDump.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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 "crew/PConfigIni.h" +#include "crew/PConfigIniDump.h" + +#include <gtest/gtest.h> + +#include <sstream> +#include <stdexcept> + +TEST(ConfigIniDumpTest, dump_sections) +{ + crew::Sections sections; + crew::Section section; + + section.name = "hello"; + section.items["key"] = "value"; + + sections.push_back(section); + + std::stringstream ss; + + ss << sections; + + // there's extra \n at end of each section + ASSERT_TRUE(ss.str() == "[hello]\nkey=value\n\n"); +} diff --git a/compiler/crew/src/PConfigJson.cpp b/compiler/crew/src/PConfigJson.cpp new file mode 100644 index 000000000..5af0ebddd --- /dev/null +++ b/compiler/crew/src/PConfigJson.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 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 "PConfigJson.h" + +#include <iostream> +#include <string> +#include <vector> + +namespace +{ + +const char _CLF = '\n'; // Control Line Feed +const char _DQU = '\"'; // Double QUotation + +} // namespace + +namespace crew +{ + +void JsonExport::indent(void) +{ + for (uint32_t i = 0; i < _indent; ++i) + _os << " "; +} + +void JsonExport::open_brace(void) +{ + indent(); + + _os << "{" << _CLF; + _indent++; +} + +void JsonExport::open_brace(const std::string &key) +{ + indent(); + + _os << _DQU << key << _DQU << " : {" << _CLF; + _indent++; +} + +void JsonExport::open_bracket(const std::string &key) +{ + indent(); + + _os << _DQU << key << _DQU << " : [" << _CLF; + _indent++; +} + +void JsonExport::close_bracket(bool cont) +{ + _indent--; + indent(); + + _os << "]"; + if (cont) + _os << ","; + _os << _CLF; +} + +void JsonExport::close_brace(bool cont) +{ + _indent--; + indent(); + + _os << "}"; + if (cont) + _os << ","; + _os << _CLF; +} + +void JsonExport::key_val(const std::string &key, const std::string &value, bool cont) +{ + indent(); + + _os << _DQU << key << _DQU << " : " << _DQU << value << _DQU; + if (cont) + _os << ","; + _os << _CLF; +} + +void JsonExport::key_val(const std::string &key, const std::vector<std::string> &l, bool cont) +{ + indent(); + + _os << _DQU << key << _DQU << " : [ "; + bool comma = false; + for (auto &v : l) + { + if (comma) + _os << ", "; + else + comma = true; + _os << _DQU << v << _DQU; + } + _os << " ]"; + if (cont) + _os << ","; + _os << _CLF; +} + +} // namespace crew diff --git a/compiler/crew/src/PConfigJson.h b/compiler/crew/src/PConfigJson.h new file mode 100644 index 000000000..c5c49d096 --- /dev/null +++ b/compiler/crew/src/PConfigJson.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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 __CREW_PCONFIG_JSON_H__ +#define __CREW_PCONFIG_JSON_H__ + +#include <ostream> +#include <string> +#include <vector> + +namespace crew +{ + +class JsonExport +{ +public: + JsonExport(std::ostream &os) : _os(os) {} + +private: + void indent(void); + +public: + void open_brace(void); + void open_brace(const std::string &key); + void open_bracket(const std::string &key); + void close_bracket(bool cont); + void close_brace(bool cont); + void key_val(const std::string &key, const std::string &value, bool cont); + void key_val(const std::string &key, const std::vector<std::string> &l, bool cont); + +private: + std::ostream &_os; + uint32_t _indent = 0; +}; + +} // namespace crew + +#endif // __CREW_PCONFIG_JSON_H__ diff --git a/compiler/crew/src/PConfigJson.test.cpp b/compiler/crew/src/PConfigJson.test.cpp new file mode 100644 index 000000000..f8afabc3d --- /dev/null +++ b/compiler/crew/src/PConfigJson.test.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 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 "PConfigJson.h" + +#include <gtest/gtest.h> + +#include <sstream> + +TEST(ConfigJsonTest, empty) +{ + std::stringstream ss; + crew::JsonExport je(ss); + + je.open_brace(); + je.close_brace(true); + + ASSERT_TRUE(ss.str() == "{\n},\n"); +} + +TEST(ConfigJsonTest, keyvalue) +{ + std::stringstream ss; + crew::JsonExport je(ss); + + je.open_brace("hello"); + je.key_val("key", "value", true); + je.close_brace(true); + + ASSERT_TRUE(ss.str() == "\"hello\" : {\n \"key\" : \"value\",\n},\n"); +} + +TEST(ConfigJsonTest, keyvaluearray) +{ + std::stringstream ss; + crew::JsonExport je(ss); + std::vector<std::string> vs = {"1", "2"}; + + je.open_brace("hello"); + je.key_val("key", vs, true); + je.close_brace(true); + + ASSERT_TRUE(ss.str() == "\"hello\" : {\n \"key\" : [ \"1\", \"2\" ],\n},\n"); +} + +TEST(ConfigJsonTest, bracket) +{ + std::stringstream ss; + crew::JsonExport je(ss); + + je.open_bracket("hello"); + je.close_bracket(true); + + ASSERT_TRUE(ss.str() == "\"hello\" : [\n],\n"); +} diff --git a/compiler/cwrap/src/Fildes.test.cpp b/compiler/cwrap/src/Fildes.test.cpp index f9fa20f9e..c487f064a 100644 --- a/compiler/cwrap/src/Fildes.test.cpp +++ b/compiler/cwrap/src/Fildes.test.cpp @@ -44,7 +44,7 @@ int make_temp(char *name_template) return fd; } -} // namespace make_temp +} // namespace TEST(FildesTest, default_constructor) { diff --git a/compiler/enco/cli/CMakeLists.txt b/compiler/enco/cli/CMakeLists.txt index 5a43ab655..6777f329b 100644 --- a/compiler/enco/cli/CMakeLists.txt +++ b/compiler/enco/cli/CMakeLists.txt @@ -5,7 +5,6 @@ target_include_directories(enco-cli PRIVATE src) target_link_libraries(enco-cli enco_intf_cmdline) target_link_libraries(enco-cli enco_intf_frontend) target_link_libraries(enco-cli enco_core) -target_link_libraries(enco-cli stdex) target_link_libraries(enco-cli dl) # Let's use project-wide compile options target_link_libraries(enco-cli nncc_common) diff --git a/compiler/enco/cli/src/Driver.cpp b/compiler/enco/cli/src/Driver.cpp index 185bb13b9..fe6cefb8c 100644 --- a/compiler/enco/cli/src/Driver.cpp +++ b/compiler/enco/cli/src/Driver.cpp @@ -135,8 +135,7 @@ private: } // namespace -#include <stdex/Memory.h> - +#include <memory> #include <map> #include <iostream> @@ -153,7 +152,7 @@ static int entry(int argc, char **argv) std::map<std::string, std::function<void(const std::string &arg)>> argparse; argparse["--frontend"] = [&](const std::string &path) { - frontend_zone = stdex::make_unique<FrontendZone>(path); + frontend_zone = std::make_unique<FrontendZone>(path); }; argparse["--frontend-arg"] = [&](const std::string &arg) { frontend_zone->append(arg); }; diff --git a/compiler/enco/core/CMakeLists.txt b/compiler/enco/core/CMakeLists.txt index f437e687a..25dad2bc6 100644 --- a/compiler/enco/core/CMakeLists.txt +++ b/compiler/enco/core/CMakeLists.txt @@ -17,7 +17,6 @@ target_link_libraries(enco_core PUBLIC coco_generic) # These libraries are linked for internal use, and thus does not appear in public headers. target_link_libraries(enco_core PRIVATE pp) target_link_libraries(enco_core PRIVATE morph) -target_link_libraries(enco_core PRIVATE stdex) # Let's use nncc project-wide build options target_link_libraries(enco_core PRIVATE nncc_common) diff --git a/compiler/enco/core/src/ANN/Binder.h b/compiler/enco/core/src/ANN/Binder.h index 71b95676b..be9f705c7 100644 --- a/compiler/enco/core/src/ANN/Binder.h +++ b/compiler/enco/core/src/ANN/Binder.h @@ -32,7 +32,7 @@ class ANNBinder { public: ANNBinder(coco::Block *block, std::unique_ptr<ann::Module> &&module) - : _block{block}, _module{std::move(module)} + : _block{block}, _module{std::move(module)} { // DO NOTHING } diff --git a/compiler/enco/core/src/ANN/Context.cpp b/compiler/enco/core/src/ANN/Context.cpp index d4d1882fa..b6d2a3d42 100644 --- a/compiler/enco/core/src/ANN/Context.cpp +++ b/compiler/enco/core/src/ANN/Context.cpp @@ -16,12 +16,12 @@ #include "ANN/Context.h" -#include <stdex/Memory.h> +#include <memory> ANNBinder *ANNContext::create(coco::Block *blk) { - auto mod = stdex::make_unique<ann::Module>(); - auto obj = stdex::make_unique<ANNBinder>(blk, std::move(mod)); + auto mod = std::make_unique<ann::Module>(); + auto obj = std::make_unique<ANNBinder>(blk, std::move(mod)); auto ptr = obj.get(); _binders.emplace_back(std::move(obj)); diff --git a/compiler/enco/core/src/ANN/Context.test.cpp b/compiler/enco/core/src/ANN/Context.test.cpp index 7fd26f30c..252d92290 100644 --- a/compiler/enco/core/src/ANN/Context.test.cpp +++ b/compiler/enco/core/src/ANN/Context.test.cpp @@ -33,7 +33,7 @@ public: protected: std::unique_ptr<coco::Module> m; }; -} +} // namespace TEST_F(ANNContextTest, constructor) { diff --git a/compiler/enco/core/src/ANN/IR/OperandInventory.cpp b/compiler/enco/core/src/ANN/IR/OperandInventory.cpp index c7ad38811..4399c3900 100644 --- a/compiler/enco/core/src/ANN/IR/OperandInventory.cpp +++ b/compiler/enco/core/src/ANN/IR/OperandInventory.cpp @@ -16,9 +16,9 @@ #include "ANN/IR/OperandInventory.h" -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; namespace ann { diff --git a/compiler/enco/core/src/ANN/IR/Operation.h b/compiler/enco/core/src/ANN/IR/Operation.h index cacc2b794..a1f1d46e2 100644 --- a/compiler/enco/core/src/ANN/IR/Operation.h +++ b/compiler/enco/core/src/ANN/IR/Operation.h @@ -38,7 +38,7 @@ public: public: Operation(const Code &code, std::initializer_list<OperandID> inputs, std::initializer_list<OperandID> outputs) - : _code{code}, _inputs{inputs}, _outputs{outputs} + : _code{code}, _inputs{inputs}, _outputs{outputs} { // DO NOTHING } diff --git a/compiler/enco/core/src/ANN/IR/OperationInventory.cpp b/compiler/enco/core/src/ANN/IR/OperationInventory.cpp index 37d48c170..93108dfb7 100644 --- a/compiler/enco/core/src/ANN/IR/OperationInventory.cpp +++ b/compiler/enco/core/src/ANN/IR/OperationInventory.cpp @@ -16,9 +16,9 @@ #include "OperationInventory.h" -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; namespace ann { diff --git a/compiler/enco/core/src/ANN/IR/WeightInventory.cpp b/compiler/enco/core/src/ANN/IR/WeightInventory.cpp index d8809ac08..edcb16aed 100644 --- a/compiler/enco/core/src/ANN/IR/WeightInventory.cpp +++ b/compiler/enco/core/src/ANN/IR/WeightInventory.cpp @@ -16,9 +16,9 @@ #include "WeightInventory.h" -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; namespace ann { diff --git a/compiler/enco/core/src/AsmCode.h b/compiler/enco/core/src/AsmCode.h index c43892888..6d57f1851 100644 --- a/compiler/enco/core/src/AsmCode.h +++ b/compiler/enco/core/src/AsmCode.h @@ -27,7 +27,7 @@ class AsmCode { public: AsmCode(const std::string &filename, const std::string &varname) - : _filename{filename}, _varname{varname} + : _filename{filename}, _varname{varname} { // DO NOTHING } diff --git a/compiler/enco/core/src/Backend.cpp b/compiler/enco/core/src/Backend.cpp index d4bec7447..77374fecd 100644 --- a/compiler/enco/core/src/Backend.cpp +++ b/compiler/enco/core/src/Backend.cpp @@ -44,13 +44,12 @@ #include "Transforms/Split.h" #include "Transforms/GlobalDataGeneration.h" -#include <stdex/Memory.h> - +#include <memory> #include <stdexcept> #include <iostream> #include <fstream> -using stdex::make_unique; +using std::make_unique; using namespace enco; namespace @@ -168,7 +167,7 @@ void BackendImpl::compile(coco::Module *m, coco::Data *d) ofs << CppCode{data_var, code(sess)} << std::endl; } -} // namespace enco +} // namespace #include <iostream> diff --git a/compiler/enco/core/src/CodeIndex.h b/compiler/enco/core/src/CodeIndex.h index 7f2da6463..ed8f24109 100644 --- a/compiler/enco/core/src/CodeIndex.h +++ b/compiler/enco/core/src/CodeIndex.h @@ -30,7 +30,7 @@ public: public: CodeIndex(const coco::BlockIndex &blk_ind, const coco::InstrIndex &ins_ind) - : _blk_ind{blk_ind}, _ins_ind{ins_ind} + : _blk_ind{blk_ind}, _ins_ind{ins_ind} { } diff --git a/compiler/enco/core/src/CppGen/Host.cpp b/compiler/enco/core/src/CppGen/Host.cpp index 37e0583d7..7f9456239 100644 --- a/compiler/enco/core/src/CppGen/Host.cpp +++ b/compiler/enco/core/src/CppGen/Host.cpp @@ -18,8 +18,7 @@ #include <pp/EnclosedDocument.h> -#include <stdex/Memory.h> - +#include <memory> #include <map> #include <string> @@ -157,7 +156,7 @@ public: public: TransferLoop(uint32_t count, uint32_t src_step, uint32_t dst_step) - : _count{count}, _step{src_step, dst_step} + : _count{count}, _step{src_step, dst_step} { // DO NOTHING } @@ -293,7 +292,7 @@ std::unique_ptr<pp::MultiLineText> HostBlockCompiler::compile(const coco::Block { InstrPrinter prn{_mem}; - auto res = stdex::make_unique<pp::LinearDocument>(); + auto res = std::make_unique<pp::LinearDocument>(); for (auto ins = blk->instr()->head(); ins; ins = ins->next()) { diff --git a/compiler/enco/core/src/CppGen/Subnet.cpp b/compiler/enco/core/src/CppGen/Subnet.cpp index 9a636c6ae..599b0794e 100644 --- a/compiler/enco/core/src/CppGen/Subnet.cpp +++ b/compiler/enco/core/src/CppGen/Subnet.cpp @@ -21,11 +21,10 @@ #include <pp/LinearDocument.h> -#include <stdex/Memory.h> - +#include <memory> #include <sstream> -using stdex::make_unique; +using std::make_unique; using enco::concat; #define S(content) #content @@ -117,7 +116,7 @@ class ScalarOperandDecl final : public CodeFragment { public: ScalarOperandDecl(const std::string &model, const ann::DType &dtype) - : _model{model}, _dtype{dtype} + : _model{model}, _dtype{dtype} { // DO NOTHING } @@ -150,7 +149,7 @@ class TensorOperandDecl final : public CodeFragment public: TensorOperandDecl(const std::string &model, const ann::DType &dtype, const nncc::core::ADT::tensor::Shape &shape) - : _model{model}, _dtype{dtype}, _shape{shape} + : _model{model}, _dtype{dtype}, _shape{shape} { // DO NOTHING } @@ -194,7 +193,7 @@ class WeightDecl final : public CodeFragment public: WeightDecl(const std::string &model, const ann::OperandID &id, const std::string &base, const std::string &size) - : _model{model}, _id{id}, _base{base}, _size{size} + : _model{model}, _id{id}, _base{base}, _size{size} { // DO NOTHING } diff --git a/compiler/enco/core/src/Session.cpp b/compiler/enco/core/src/Session.cpp index 034f23892..18af87ace 100644 --- a/compiler/enco/core/src/Session.cpp +++ b/compiler/enco/core/src/Session.cpp @@ -16,12 +16,10 @@ #include "Session.h" -#include <stdex/Memory.h> - #include <map> #include <memory> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/enco/core/src/Support/Debugging.cpp b/compiler/enco/core/src/Support/Debugging.cpp index bd65a27d8..9a9a7745e 100644 --- a/compiler/enco/core/src/Support/Debugging.cpp +++ b/compiler/enco/core/src/Support/Debugging.cpp @@ -77,7 +77,7 @@ pp::LinearDocument operator<<(const SectionBuilder &builder, Callback cb) } SectionBuilder section(const std::string &tag) { return SectionBuilder{tag}; } -} +} // namespace /** * SECTION: Bag diff --git a/compiler/enco/core/src/Transforms/FeatureUnification.cpp b/compiler/enco/core/src/Transforms/FeatureUnification.cpp index 1a7a0a8a4..9e4a8e19f 100644 --- a/compiler/enco/core/src/Transforms/FeatureUnification.cpp +++ b/compiler/enco/core/src/Transforms/FeatureUnification.cpp @@ -17,14 +17,13 @@ #include "FeatureUnification.h" #include "IRUtils.h" -#include <stdex/Memory.h> - +#include <memory> #include <set> #include <vector> #include <cassert> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp b/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp index 152477a51..cb5a0a9a9 100644 --- a/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp +++ b/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp @@ -18,11 +18,10 @@ #include "Split.h" #include "Dims.h" -#include <stdex/Memory.h> - +#include <memory> #include <map> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/enco/core/src/Transforms/Split.cpp b/compiler/enco/core/src/Transforms/Split.cpp index b57b8f882..714c27a72 100644 --- a/compiler/enco/core/src/Transforms/Split.cpp +++ b/compiler/enco/core/src/Transforms/Split.cpp @@ -22,13 +22,13 @@ #include <coco/IR.h> #include <nncc/core/ADT/kernel/NHWCLayout.h> -#include <stdex/Memory.h> +#include <memory> #include <map> #include <stdexcept> #include <functional> -using stdex::make_unique; +using std::make_unique; namespace { @@ -337,8 +337,8 @@ public: auto ofm = binder->addOperand<float>(_ofm); binder->addOperation( - ann::Operation::Code::DEPTHWISE_CONV_2D, - {ifm, ker, bias, left, right, top, bottom, hstride, vstride, multiplier, fuse}, {ofm}); + ann::Operation::Code::DEPTHWISE_CONV_2D, + {ifm, ker, bias, left, right, top, bottom, hstride, vstride, multiplier, fuse}, {ofm}); } private: diff --git a/compiler/enco/core/src/Transforms/Split.h b/compiler/enco/core/src/Transforms/Split.h index b4e1d7baf..85ad2684f 100644 --- a/compiler/enco/core/src/Transforms/Split.h +++ b/compiler/enco/core/src/Transforms/Split.h @@ -43,6 +43,6 @@ struct PhaseConstructionPass final : public Pass void run(const SessionID &sess) const override { split_into_phases(code(sess)); } }; -} // namespace enco; +} // namespace enco #endif // __SPLIT_H__ diff --git a/compiler/enco/frontend/caffe/CMakeLists.txt b/compiler/enco/frontend/caffe/CMakeLists.txt index ce43a41d3..9722392a1 100644 --- a/compiler/enco/frontend/caffe/CMakeLists.txt +++ b/compiler/enco/frontend/caffe/CMakeLists.txt @@ -16,7 +16,6 @@ target_link_libraries(enco_caffe_frontend enco_intf_frontend) target_link_libraries(enco_caffe_frontend enco_intf_cmdline) target_link_libraries(enco_caffe_frontend morph) target_link_libraries(enco_caffe_frontend caffeproto) -target_link_libraries(enco_caffe_frontend stdex) nnas_find_package(GTest QUIET) diff --git a/compiler/enco/frontend/caffe/src/Context.h b/compiler/enco/frontend/caffe/src/Context.h index aca57ce6f..7cf27ead4 100644 --- a/compiler/enco/frontend/caffe/src/Context.h +++ b/compiler/enco/frontend/caffe/src/Context.h @@ -81,8 +81,8 @@ public: explicit GraphBuilderContext(coco::Module *module, coco::Data *data, coco::Block *block, ShapeContext &shape_ctx, StoreContext &bag_ctx, WeightContext &weight_ctx) - : _module(module), _data(data), _block(block), _shape_ctx(shape_ctx), _bag_ctx(bag_ctx), - _weight_ctx(weight_ctx) + : _module(module), _data(data), _block(block), _shape_ctx(shape_ctx), _bag_ctx(bag_ctx), + _weight_ctx(weight_ctx) { // DO NOTHING } diff --git a/compiler/enco/frontend/caffe/src/Entry.cpp b/compiler/enco/frontend/caffe/src/Entry.cpp index 2bdb73eac..41e174bc4 100644 --- a/compiler/enco/frontend/caffe/src/Entry.cpp +++ b/compiler/enco/frontend/caffe/src/Entry.cpp @@ -19,8 +19,7 @@ #include <cmdline/View.h> -#include <stdex/Memory.h> - +#include <memory> #include <fstream> #include <cassert> @@ -28,7 +27,7 @@ extern "C" std::unique_ptr<enco::Frontend> make_frontend(const cmdline::View &cm { assert(cmdline.size() == 2); - auto frontend = stdex::make_unique<Frontend>(); + auto frontend = std::make_unique<Frontend>(); // Fill prototxt { diff --git a/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp b/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp index e9db31177..d9a1c9617 100644 --- a/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp +++ b/compiler/enco/frontend/caffe/src/GraphBuilderRegistry.cpp @@ -25,9 +25,9 @@ #include "Layer/Scale.h" #include "Layer/BatchNorm.h" -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; namespace caffeimport { diff --git a/compiler/enco/frontend/caffe/src/Layer/Convolution.cpp b/compiler/enco/frontend/caffe/src/Layer/Convolution.cpp index 9fb096d49..807cce44d 100644 --- a/compiler/enco/frontend/caffe/src/Layer/Convolution.cpp +++ b/compiler/enco/frontend/caffe/src/Layer/Convolution.cpp @@ -101,7 +101,7 @@ void ConvolutionBuilder::build(const ::caffe::LayerParameter &layer, auto ker_dst = data->f32()->access(ker_obj); auto ker_src = kernel::OverlayFactory<float, kernel::NCHWLayout>::make( - ker_obj->shape(), ker_blob->mutable_data()->begin()); + ker_obj->shape(), ker_blob->mutable_data()->begin()); for (uint32_t n = 0; n < ker_obj->shape().count(); ++n) { diff --git a/compiler/enco/frontend/tflite/CMakeLists.txt b/compiler/enco/frontend/tflite/CMakeLists.txt index 77159879e..ea10fbc4b 100644 --- a/compiler/enco/frontend/tflite/CMakeLists.txt +++ b/compiler/enco/frontend/tflite/CMakeLists.txt @@ -19,7 +19,6 @@ target_link_libraries(enco_tflite_frontend enco_intf_frontend) target_link_libraries(enco_tflite_frontend enco_intf_cmdline) target_link_libraries(enco_tflite_frontend flatbuffers) target_link_libraries(enco_tflite_frontend enco_tflite_schema) -target_link_libraries(enco_tflite_frontend stdex) target_link_libraries(enco_tflite_frontend morph) target_link_libraries(enco_tflite_frontend cwrap) diff --git a/compiler/enco/frontend/tflite/src/Context.cpp b/compiler/enco/frontend/tflite/src/Context.cpp index ef030dc5d..588c3c44b 100644 --- a/compiler/enco/frontend/tflite/src/Context.cpp +++ b/compiler/enco/frontend/tflite/src/Context.cpp @@ -48,7 +48,7 @@ void TensorContext::prepare(const tflite::SubGraph *graph) } TflOpCodeContext::TflOpCodeContext( - const flatbuffers::Vector<flatbuffers::Offset<tflite::OperatorCode>> *opcodes) + const flatbuffers::Vector<flatbuffers::Offset<tflite::OperatorCode>> *opcodes) { for (const tflite::OperatorCode *opcode : *opcodes) { diff --git a/compiler/enco/frontend/tflite/src/Context.h b/compiler/enco/frontend/tflite/src/Context.h index f72385f9a..caeac4ab5 100644 --- a/compiler/enco/frontend/tflite/src/Context.h +++ b/compiler/enco/frontend/tflite/src/Context.h @@ -135,8 +135,8 @@ public: explicit GraphBuilderContext(coco::Module *m, coco::Data *d, coco::Block *block, TensorBags &tensor_bags, TensorContext &tensor_context, TflBufferContext &buffer_context, const tflite::SubGraph *graph) - : _m(m), _d(d), _block(block), _tensor_bags(tensor_bags), _tensor_context(tensor_context), - _buffer_context(buffer_context), _graph(graph) + : _m(m), _d(d), _block(block), _tensor_bags(tensor_bags), _tensor_context(tensor_context), + _buffer_context(buffer_context), _graph(graph) { // DO NOTHING } diff --git a/compiler/enco/frontend/tflite/src/Entry.cpp b/compiler/enco/frontend/tflite/src/Entry.cpp index c69e18074..74d3096ab 100644 --- a/compiler/enco/frontend/tflite/src/Entry.cpp +++ b/compiler/enco/frontend/tflite/src/Entry.cpp @@ -19,12 +19,11 @@ #include <cmdline/View.h> -#include <stdex/Memory.h> - +#include <memory> #include <fstream> #include <cassert> -using stdex::make_unique; +using std::make_unique; extern "C" std::unique_ptr<enco::Frontend> make_frontend(const cmdline::View &cmdline) { diff --git a/compiler/enco/frontend/tflite/src/Frontend.test.cpp b/compiler/enco/frontend/tflite/src/Frontend.test.cpp index aee6099e7..1bc774629 100644 --- a/compiler/enco/frontend/tflite/src/Frontend.test.cpp +++ b/compiler/enco/frontend/tflite/src/Frontend.test.cpp @@ -16,11 +16,11 @@ #include "Frontend.h" -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> -using stdex::make_unique; +using std::make_unique; namespace { diff --git a/compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h b/compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h index 1ae882e89..ca4f74fc5 100644 --- a/compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h +++ b/compiler/enco/frontend/tflite/src/GraphBuilderRegistry.h @@ -29,11 +29,11 @@ #include "Op/Div.h" #include <schema_generated.h> -#include <stdex/Memory.h> +#include <memory> #include <map> -using stdex::make_unique; +using std::make_unique; namespace tflimport { @@ -68,7 +68,7 @@ private: // add GraphBuilder for each tflite operation. _builder_map[tflite::BuiltinOperator_CONV_2D] = make_unique<Conv2DGraphBuilder>(); _builder_map[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = - make_unique<DepthwiseConv2DGraphBuilder>(); + make_unique<DepthwiseConv2DGraphBuilder>(); _builder_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique<AvgPool2DGraphBuilder>(); _builder_map[tflite::BuiltinOperator_MAX_POOL_2D] = make_unique<MaxPool2DGraphBuilder>(); _builder_map[tflite::BuiltinOperator_CONCATENATION] = make_unique<ConcatenationGraphBuilder>(); diff --git a/compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp b/compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp index 16f68fcdb..6f8223f10 100644 --- a/compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp +++ b/compiler/enco/frontend/tflite/src/Op/AveragePool2D.cpp @@ -102,7 +102,7 @@ void AvgPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContex coco_avgpool2d->stride()->horizontal(params->stride_w()); coco::Padding2D padding = - pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); + pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); coco_avgpool2d->pad()->top(padding.top()); coco_avgpool2d->pad()->bottom(padding.bottom()); diff --git a/compiler/enco/frontend/tflite/src/Op/Conv2D.cpp b/compiler/enco/frontend/tflite/src/Op/Conv2D.cpp index e9516c0e9..d1f97597f 100644 --- a/compiler/enco/frontend/tflite/src/Op/Conv2D.cpp +++ b/compiler/enco/frontend/tflite/src/Op/Conv2D.cpp @@ -171,7 +171,7 @@ void Conv2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContext * // fused activation coco::FeatureObject *act_output = - build_activation(conv_params->fused_activation_function(), blk, last_obj); + build_activation(conv_params->fused_activation_function(), blk, last_obj); // Create Copy Instr of last_obj to Output Object auto copy_ins = instr_builder(m).copy(ofm_obj, act_output); diff --git a/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp b/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp index e3d7b263e..bc903c380 100644 --- a/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp +++ b/compiler/enco/frontend/tflite/src/Op/DepthwiseConv2D.cpp @@ -138,8 +138,8 @@ void DepthwiseConv2DGraphBuilder::build(const tflite::Operator *op, auto wc = new_shape.width() * new_shape.depth(); ker_spn[n * hwc + h * wc + w * new_shape.depth() + c] = - buffer.ptr[tfl_n * hw * new_shape.count() + /* new_shape.count() is old c */ - h * new_shape.width() * new_shape.count() + w * new_shape.count() + tfl_c]; + buffer.ptr[tfl_n * hw * new_shape.count() + /* new_shape.count() is old c */ + h * new_shape.width() * new_shape.count() + w * new_shape.count() + tfl_c]; } } } @@ -220,7 +220,7 @@ void DepthwiseConv2DGraphBuilder::build(const tflite::Operator *op, // fused activation coco::FeatureObject *act_output = - build_activation(dconv_params->fused_activation_function(), blk, last_obj); + build_activation(dconv_params->fused_activation_function(), blk, last_obj); // Create Copy Instr of last_obj to Output Object auto copy_ins = instr_builder(m).copy(ofm_obj, act_output); diff --git a/compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp b/compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp index ee4406425..41e0cde17 100644 --- a/compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp +++ b/compiler/enco/frontend/tflite/src/Op/MaxPool2D.cpp @@ -99,7 +99,7 @@ void MaxPool2DGraphBuilder::build(const tflite::Operator *op, GraphBuilderContex coco_maxpool2d->stride()->horizontal(params->stride_w()); coco::Padding2D padding = - pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); + pool2D_padding(params, ifm_shape, params->filter_width(), params->filter_height()); coco_maxpool2d->pad()->top(padding.top()); coco_maxpool2d->pad()->bottom(padding.bottom()); diff --git a/compiler/enco/test/basic/000/CMakeLists.txt b/compiler/enco/test/basic/000/CMakeLists.txt index 20ba3c571..95e9cb0b0 100644 --- a/compiler/enco/test/basic/000/CMakeLists.txt +++ b/compiler/enco/test/basic/000/CMakeLists.txt @@ -11,7 +11,6 @@ set(LIB_TARGET ${PREFIX}-lib) add_library(${PREFIX}-frontend SHARED enco.test.cpp) target_link_libraries(${PREFIX}-frontend enco_intf_cmdline) target_link_libraries(${PREFIX}-frontend enco_intf_frontend) -target_link_libraries(${PREFIX}-frontend stdex) # NOTE BYPRODUCTS are not specified in order to enforce source code generation add_custom_command(OUTPUT ${GENERATED_CPP} ${GENERATED_ASM} ${GENERATED_BIN} diff --git a/compiler/enco/test/basic/000/enco.test.cpp b/compiler/enco/test/basic/000/enco.test.cpp index 3dbf96613..84c28d0f7 100644 --- a/compiler/enco/test/basic/000/enco.test.cpp +++ b/compiler/enco/test/basic/000/enco.test.cpp @@ -19,7 +19,7 @@ #include <nncc/core/ADT/tensor/LexicalLayout.h> -#include <stdex/Memory.h> +#include <memory> using namespace nncc::core::ADT; @@ -77,5 +77,5 @@ struct Frontend final : public enco::Frontend extern "C" std::unique_ptr<enco::Frontend> make_frontend(const cmdline::View &cmdline) { - return stdex::make_unique<Frontend>(); + return std::make_unique<Frontend>(); } diff --git a/compiler/enco/test/binder.cpp b/compiler/enco/test/binder.cpp index c8c72fc8b..f04cfa4f6 100644 --- a/compiler/enco/test/binder.cpp +++ b/compiler/enco/test/binder.cpp @@ -46,9 +46,9 @@ void Network_invoke(Network *net); #include <nncc/core/ADT/tensor/LexicalLayout.h> #include <nncc/core/ADT/tensor/Overlay.h> -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; using namespace nncc::core::ADT; namespace diff --git a/compiler/enco/test/caffe/CMakeLists.txt b/compiler/enco/test/caffe/CMakeLists.txt index ee49b6b28..d552d6ec8 100644 --- a/compiler/enco/test/caffe/CMakeLists.txt +++ b/compiler/enco/test/caffe/CMakeLists.txt @@ -123,7 +123,6 @@ foreach(PREFIX IN ITEMS ${CANDIDATES}) target_link_libraries(${BINDER_TARGET} nnkit_intf_backend) target_link_libraries(${BINDER_TARGET} ann_api) target_link_libraries(${BINDER_TARGET} ann_ref_static) - target_link_libraries(${BINDER_TARGET} stdex) set_target_properties(${BINDER_TARGET} PROPERTIES OUTPUT_NAME ${PREFIX}) list(APPEND TESTS ${PREFIX}) diff --git a/compiler/enco/test/tflite/CMakeLists.txt b/compiler/enco/test/tflite/CMakeLists.txt index d5a96a6da..81d5ed2a2 100644 --- a/compiler/enco/test/tflite/CMakeLists.txt +++ b/compiler/enco/test/tflite/CMakeLists.txt @@ -90,7 +90,6 @@ foreach(PREFIX IN ITEMS ${CANDIDATES}) target_link_libraries(${BINDER_TARGET} nnkit_intf_backend) target_link_libraries(${BINDER_TARGET} ann_api) target_link_libraries(${BINDER_TARGET} ann_ref_static) - target_link_libraries(${BINDER_TARGET} stdex) set_target_properties(${BINDER_TARGET} PROPERTIES OUTPUT_NAME ${PREFIX}) list(APPEND TESTS ${PREFIX}) diff --git a/compiler/encodump/CMakeLists.txt b/compiler/encodump/CMakeLists.txt index 58fe17a51..a4ad441b2 100644 --- a/compiler/encodump/CMakeLists.txt +++ b/compiler/encodump/CMakeLists.txt @@ -13,5 +13,4 @@ target_include_directories(encodump PRIVATE src) target_link_libraries(encodump enco_intf_frontend) target_link_libraries(encodump enco_core) target_link_libraries(encodump safemain) -target_link_libraries(encodump stdex) target_link_libraries(encodump dl) diff --git a/compiler/encodump/src/Driver.cpp b/compiler/encodump/src/Driver.cpp index f27cbe904..2928d1d25 100644 --- a/compiler/encodump/src/Driver.cpp +++ b/compiler/encodump/src/Driver.cpp @@ -137,8 +137,7 @@ private: } // namespace -#include <stdex/Memory.h> - +#include <memory> #include <map> #include <iostream> @@ -163,7 +162,7 @@ int entry(int argc, char **argv) std::map<std::string, std::function<void(const std::string &arg)>> argparse; argparse["--frontend"] = [&](const std::string &path) { - frontend_zone = stdex::make_unique<FrontendZone>(path); + frontend_zone = std::make_unique<FrontendZone>(path); }; argparse["--frontend-arg"] = [&](const std::string &arg) { frontend_zone->append(arg); }; diff --git a/compiler/exo/CMakeLists.txt b/compiler/exo/CMakeLists.txt index 79c75ef2e..e686cbb83 100644 --- a/compiler/exo/CMakeLists.txt +++ b/compiler/exo/CMakeLists.txt @@ -39,7 +39,6 @@ target_include_directories(exo PRIVATE src) target_link_libraries(exo PUBLIC exo_tflite_fbs) target_link_libraries(exo PUBLIC exo_circle_fbs) target_link_libraries(exo PUBLIC loco) -target_link_libraries(exo PRIVATE stdex) target_link_libraries(exo PRIVATE pepper_str) target_link_libraries(exo PRIVATE pepper_strcast) target_link_libraries(exo PRIVATE locoex_customop) @@ -64,7 +63,6 @@ nnas_find_package(GTest REQUIRED) GTest_AddTest(exo_test ${TESTS}) target_include_directories(exo_test PRIVATE src) -target_link_libraries(exo_test stdex) target_link_libraries(exo_test pepper_str) target_link_libraries(exo_test exo) target_link_libraries(exo_test hermes_std) diff --git a/compiler/exo/requires.cmake b/compiler/exo/requires.cmake index 6378b942d..3116c5757 100644 --- a/compiler/exo/requires.cmake +++ b/compiler/exo/requires.cmake @@ -1,4 +1,3 @@ -require("stdex") require("loco") require("locoex-customop") require("logo") diff --git a/compiler/exo/src/Circle/CircleExporter.cpp b/compiler/exo/src/Circle/CircleExporter.cpp index 797749090..cfcb9a258 100644 --- a/compiler/exo/src/Circle/CircleExporter.cpp +++ b/compiler/exo/src/Circle/CircleExporter.cpp @@ -18,16 +18,15 @@ #include "CircleExporterImpl.h" -#include <stdex/Memory.h> - #include <oops/InternalExn.h> +#include <memory> #include <fstream> namespace exo { -CircleExporter::CircleExporter(loco::Graph *graph) : _impl(stdex::make_unique<Impl>(graph)) +CircleExporter::CircleExporter(loco::Graph *graph) : _impl(std::make_unique<Impl>(graph)) { // NOTHING TO DO } diff --git a/compiler/exo/src/Circle/CircleExporterImpl.cpp b/compiler/exo/src/Circle/CircleExporterImpl.cpp index 4cba33da1..a93931597 100644 --- a/compiler/exo/src/Circle/CircleExporterImpl.cpp +++ b/compiler/exo/src/Circle/CircleExporterImpl.cpp @@ -88,7 +88,7 @@ encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<OpCode, uint3 INTERNAL_EXN("Cannot find code for customop even though opcode is BuiltinOperator_CUSTOM"); operator_codes_vec[idx] = - CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second)); + CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second)); } } return builder.CreateVector(operator_codes_vec); @@ -148,7 +148,7 @@ void CircleExporter::Impl::exportGraph(loco::Graph *graph) // encode operator codes auto operator_codes = - encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes); + encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes); // Subgraphs Offset<SubGraph> subgraph = exportSubgraph(gd); diff --git a/compiler/exo/src/Circle/CircleExporterUtils.cpp b/compiler/exo/src/Circle/CircleExporterUtils.cpp index 12b204ce7..079f115f6 100644 --- a/compiler/exo/src/Circle/CircleExporterUtils.cpp +++ b/compiler/exo/src/Circle/CircleExporterUtils.cpp @@ -78,13 +78,13 @@ circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> * // // NOTE input and output 'feature' map are shape of NHWC bool same_padding_criterion_1 = - (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && - (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); + (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); // For same padding, rear padding is same or bigger than front padding by at most 1 bool same_padding_criterion_2 = - (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && - (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); + (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && + (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); if (same_padding_criterion_1 && same_padding_criterion_2) return circle::Padding_SAME; @@ -123,8 +123,7 @@ void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd) gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST; } -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace @@ -150,7 +149,7 @@ private: void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id) { assert(node->annot<TFLTensorIndexAnnotation>() == nullptr); - node->annot(stdex::make_unique<TFLTensorIndexAnnotation>(tensor_id)); + node->annot(std::make_unique<TFLTensorIndexAnnotation>(tensor_id)); } TFLTensorIndex get_tensor_index(loco::Node *node) diff --git a/compiler/exo/src/Circle/CircleOperationExporter.cpp b/compiler/exo/src/Circle/CircleOperationExporter.cpp index 390e2ec99..8b7337011 100644 --- a/compiler/exo/src/Circle/CircleOperationExporter.cpp +++ b/compiler/exo/src/Circle/CircleOperationExporter.cpp @@ -89,13 +89,19 @@ public: void visit(loco::ReLU *) final; void visit(loco::ReLU6 *) final; void visit(loco::Tanh *) final; - void visit(loco::Push *) final { /* DO NOTHING */} - void visit(loco::Pull *) final { /* DO NOTHING */} + void visit(loco::Push *) final + { /* DO NOTHING */ + } + void visit(loco::Pull *) final + { /* DO NOTHING */ + } void visit(loco::FeatureEncode *) final; void visit(loco::FeatureDecode *) final; void visit(loco::FilterEncode *) final; void visit(loco::DepthwiseFilterEncode *) final; - void visit(loco::ConstGen *) final { /* skip, everything is done in exportOpDefinedTensors */} + void visit(loco::ConstGen *) final + { /* skip, everything is done in exportOpDefinedTensors */ + } void visit(loco::MaxPool2D *) final; void visit(loco::AvgPool2D *) final; void visit(loco::Conv2D *) final; @@ -235,7 +241,7 @@ void OperationExporter::visit(locoex::TFLFullyConnected *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); auto options = - CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); + CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction())); // Make FULLY_CONNECTED operator auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, @@ -375,8 +381,8 @@ void OperationExporter::visit(locoex::TFLTranspose *node) auto options = CreateTransposeOptions(builder); auto op_offset = - CreateOperator(builder, op_idx, inputs, outputs, - circle::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union()); + CreateOperator(builder, op_idx, inputs, outputs, + circle::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union()); gd._operators.push_back(op_offset); } @@ -393,7 +399,7 @@ void OperationExporter::visit(locoex::TFLTransposeConv *node) auto outputs = builder.CreateVector(outputs_vec); circle::Padding padding = getOpPadding(node->padding()); auto options = - CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h()); + CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h()); // Make TRANSPOSE_CONV operator auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, @@ -405,7 +411,7 @@ template <class TFLPool2D> void OperationExporter::export_pool_2d(TFLPool2D *node, circle::BuiltinOperator builtin_op) { EXO_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D || - builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D, + builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D, "should be maxpool or avgpool"); EXO_ASSERT(node->padding() != locoex::Padding::UNDEFINED, "Padding is not set"); @@ -481,10 +487,10 @@ void OperationExporter::visit(loco::MaxPool2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); circle::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); - auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical(), node->window()->horizontal(), - node->window()->vertical()); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = + CreatePool2DOptions(builder, padding, node->stride()->horizontal(), node->stride()->vertical(), + node->window()->horizontal(), node->window()->vertical()); auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, circle::BuiltinOptions_Pool2DOptions, options.Union()); gd._operators.push_back(op_offset); @@ -501,10 +507,10 @@ void OperationExporter::visit(loco::AvgPool2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); circle::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); - auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical(), node->window()->horizontal(), - node->window()->vertical()); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = + CreatePool2DOptions(builder, padding, node->stride()->horizontal(), node->stride()->vertical(), + node->window()->horizontal(), node->window()->vertical()); auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, circle::BuiltinOptions_Pool2DOptions, options.Union()); gd._operators.push_back(op_offset); @@ -527,7 +533,7 @@ void OperationExporter::visit(loco::Conv2D *node) std::vector<float> bias_vec_data(bias_vec_size); // initialized as zero vector auto bias_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); @@ -539,7 +545,7 @@ void OperationExporter::visit(loco::Conv2D *node) auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); auto bias_tensor_offset = - CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); gd._tensors.push_back(bias_tensor_offset); // Make input, output and options for operator @@ -549,9 +555,9 @@ void OperationExporter::visit(loco::Conv2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); circle::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); - auto options = CreateConv2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical()); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = + CreateConv2DOptions(builder, padding, node->stride()->horizontal(), node->stride()->vertical()); // Make CONV_2D operator auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, @@ -581,7 +587,7 @@ void OperationExporter::visit(loco::TransposedConv2D *node) } auto outshape_vec_offset = builder.CreateVector( - reinterpret_cast<uint8_t *>(outshape_vec_data.data()), raw_outshape_vec_size); + reinterpret_cast<uint8_t *>(outshape_vec_data.data()), raw_outshape_vec_size); auto outshape_buffer_offset = CreateBuffer(builder, outshape_vec_offset); @@ -630,7 +636,7 @@ void OperationExporter::visit(loco::DepthwiseConv2D *node) size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t); std::vector<float> bias_vec_data(bias_vec_size); auto bias_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); @@ -642,7 +648,7 @@ void OperationExporter::visit(loco::DepthwiseConv2D *node) auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); auto bias_tensor_offset = - CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); gd._tensors.push_back(bias_tensor_offset); std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()), @@ -651,13 +657,13 @@ void OperationExporter::visit(loco::DepthwiseConv2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); circle::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); int32_t ifm_channel_size = ShapeInference::get(node->ifm())._dims[3]; // multiplier = bias_vec_size(output_size)/ifm_channel_size auto options = - CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical(), bias_vec_size / ifm_channel_size); + CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), bias_vec_size / ifm_channel_size); auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, circle::BuiltinOptions_DepthwiseConv2DOptions, options.Union()); @@ -691,7 +697,7 @@ void OperationExporter::visit(loco::TensorReduce *node) size_t raw_axes_vec_size = axes_vec_size * sizeof(int32_t); auto axes_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(axes_vec.data()), raw_axes_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(axes_vec.data()), raw_axes_vec_size); auto axes_buffer_offset = CreateBuffer(builder, axes_vec_offset); @@ -703,7 +709,7 @@ void OperationExporter::visit(loco::TensorReduce *node) auto name_offset = builder.CreateString("t_" + std::to_string(axes_tensor_id)); auto axes_tensor_offset = - CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset); + CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset); gd._tensors.push_back(axes_tensor_offset); std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), axes_tensor_id}; @@ -766,7 +772,7 @@ void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, constexpr size_t raw_perm_vec_size = perm_vec_size * sizeof(int32_t); auto perm_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(perm_vec_data.data()), raw_perm_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(perm_vec_data.data()), raw_perm_vec_size); auto perm_buffer_offset = CreateBuffer(builder, perm_vec_offset); @@ -778,7 +784,7 @@ void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, auto name_offset = builder.CreateString("t_" + std::to_string(perm_tensor_id)); auto perm_tensor_offset = - CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset); + CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset); gd._tensors.push_back(perm_tensor_offset); // Create permutation node @@ -792,7 +798,7 @@ void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, constexpr auto options_type = circle::BuiltinOptions::BuiltinOptions_TransposeOptions; auto transpose_offset = - CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union()); + CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union()); gd._operators.push_back(transpose_offset); } @@ -878,11 +884,11 @@ void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder, // but also by input. auto input_shape_shape_vec_offset = - builder.CreateVector(std::vector<int32_t>{(int32_t)new_shape_vec.size()}); + builder.CreateVector(std::vector<int32_t>{(int32_t)new_shape_vec.size()}); size_t input_shape_vec_size = new_shape_vec.size() * sizeof(int32_t); auto input_shape_input_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(new_shape_vec.data()), input_shape_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(new_shape_vec.data()), input_shape_vec_size); auto input_shape_buffer_offset = CreateBuffer(builder, input_shape_input_vec_offset); const auto input_shape_buffer_id = static_cast<uint32_t>(gd._buffers.size()); @@ -891,7 +897,7 @@ void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder, auto input_shape_tensor_id = static_cast<int32_t>(gd._tensors.size()); auto name_offset = builder.CreateString("t_" + std::to_string(input_shape_tensor_id)); auto input_shape_tensor_offset = CreateTensor( - builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset); + builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset); gd._tensors.push_back(input_shape_tensor_offset); uint32_t op_idx = gd.registerBuiltinOpcode(circle::BuiltinOperator_RESHAPE); @@ -1093,7 +1099,7 @@ void OperationExporter::visit(loco::TensorConstantPad *node) auto padding_shape_vec_ptr = builder.CreateVector(std::vector<int32_t>{padding_vec_size, 2}); // create tensor auto padding_tensor_ptr = - CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id); + CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id); // get tensor id const auto padding_tensor_id = static_cast<int32_t>(gd._tensors.size()); diff --git a/compiler/exo/src/Circle/CircleTypeInference.cpp b/compiler/exo/src/Circle/CircleTypeInference.cpp index a1e92b884..d3d01b4af 100644 --- a/compiler/exo/src/Circle/CircleTypeInference.cpp +++ b/compiler/exo/src/Circle/CircleTypeInference.cpp @@ -31,8 +31,6 @@ #include <oops/InternalExn.h> -#include <stdex/Memory.h> - #include <stdexcept> #include <type_traits> diff --git a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp index e3884c3cc..aa2cad705 100644 --- a/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp +++ b/compiler/exo/src/Conversion/DepthwiseConv2DConverter.cpp @@ -75,9 +75,9 @@ bool DepthwiseConv2DConverter::convert(loco::DepthwiseConv2D *origin) reshape->tensor(filter_dec); int32_t new_shape[4] = { - 1, static_cast<int32_t>(filter_shape.height().value()), - static_cast<int32_t>(filter_shape.width().value()), - static_cast<int32_t>(filter_shape.depth().value() * filter_shape.multiplier().value())}; + 1, static_cast<int32_t>(filter_shape.height().value()), + static_cast<int32_t>(filter_shape.width().value()), + static_cast<int32_t>(filter_shape.depth().value() * filter_shape.multiplier().value())}; locoex::set_new_shape(reshape, new_shape, 4); tfl_dw_conv2d->filter(reshape); diff --git a/compiler/exo/src/Convert.cpp b/compiler/exo/src/Convert.cpp index 45f0481f4..3a578eee8 100644 --- a/compiler/exo/src/Convert.cpp +++ b/compiler/exo/src/Convert.cpp @@ -32,7 +32,7 @@ #include <logo/RemoveForwardNodePass.h> #include <logo/Phase.h> -#include <stdex/Memory.h> +#include <memory> namespace exo { @@ -49,40 +49,40 @@ void convert_to_TFLNodes(loco::Graph *graph) logo::Phase phase; { // prepare type and shape before conversion - phase.emplace_back(stdex::make_unique<TypeInferencePass>()); - phase.emplace_back(stdex::make_unique<ShapeInferencePass>()); + phase.emplace_back(std::make_unique<TypeInferencePass>()); + phase.emplace_back(std::make_unique<ShapeInferencePass>()); // Add converters for canonical nodes. Note: Not all loco canonical nodes are listed. - phase.emplace_back(stdex::make_unique<AvgPool2DConverter>()); - phase.emplace_back(stdex::make_unique<ConstGenConverter>()); - phase.emplace_back(stdex::make_unique<Conv2DConverter>()); - phase.emplace_back(stdex::make_unique<DepthwiseConv2DConverter>()); + phase.emplace_back(std::make_unique<AvgPool2DConverter>()); + phase.emplace_back(std::make_unique<ConstGenConverter>()); + phase.emplace_back(std::make_unique<Conv2DConverter>()); + phase.emplace_back(std::make_unique<DepthwiseConv2DConverter>()); // TODO loco::DepthwiseFilterEncode - phase.emplace_back(stdex::make_unique<EltwiseAddConverter>()); - phase.emplace_back(stdex::make_unique<EltwiseDivConverter>()); - phase.emplace_back(stdex::make_unique<EltwiseMaxConverter>()); - phase.emplace_back(stdex::make_unique<EltwiseMulConverter>()); - phase.emplace_back(stdex::make_unique<EltwiseSqrtConverter>()); - phase.emplace_back(stdex::make_unique<EltwiseSubConverter>()); - phase.emplace_back(stdex::make_unique<FeatureBiasAddConverter>()); + phase.emplace_back(std::make_unique<EltwiseAddConverter>()); + phase.emplace_back(std::make_unique<EltwiseDivConverter>()); + phase.emplace_back(std::make_unique<EltwiseMaxConverter>()); + phase.emplace_back(std::make_unique<EltwiseMulConverter>()); + phase.emplace_back(std::make_unique<EltwiseSqrtConverter>()); + phase.emplace_back(std::make_unique<EltwiseSubConverter>()); + phase.emplace_back(std::make_unique<FeatureBiasAddConverter>()); // TODO loco::FixedReshape - phase.emplace_back(stdex::make_unique<MatMulConverter>()); - phase.emplace_back(stdex::make_unique<MaxPool2DConverter>()); - phase.emplace_back(stdex::make_unique<ReluConverter>()); - phase.emplace_back(stdex::make_unique<Relu6Converter>()); + phase.emplace_back(std::make_unique<MatMulConverter>()); + phase.emplace_back(std::make_unique<MaxPool2DConverter>()); + phase.emplace_back(std::make_unique<ReluConverter>()); + phase.emplace_back(std::make_unique<Relu6Converter>()); // TODO loco::Tanh - phase.emplace_back(stdex::make_unique<TensorConcatConverter>()); + phase.emplace_back(std::make_unique<TensorConcatConverter>()); // TODO loco::TensorBiasAdd - phase.emplace_back(stdex::make_unique<TensorBroadcastConverter>()); - phase.emplace_back(stdex::make_unique<TensorReduceConverter>()); + phase.emplace_back(std::make_unique<TensorBroadcastConverter>()); + phase.emplace_back(std::make_unique<TensorReduceConverter>()); // TODO loco::TensorSoftmax - phase.emplace_back(stdex::make_unique<TensorTransposeConverter>()); - phase.emplace_back(stdex::make_unique<TransposedConv2DConverter>()); + phase.emplace_back(std::make_unique<TensorTransposeConverter>()); + phase.emplace_back(std::make_unique<TransposedConv2DConverter>()); // Add optimization below - phase.emplace_back(stdex::make_unique<logo::SimplifyDomainConversionPass>()); - phase.emplace_back(stdex::make_unique<logo::RemoveForwardNodePass>()); - phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>()); + phase.emplace_back(std::make_unique<logo::SimplifyDomainConversionPass>()); + phase.emplace_back(std::make_unique<logo::RemoveForwardNodePass>()); + phase.emplace_back(std::make_unique<logo::RemoveDeadNodePass>()); } logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{graph}; diff --git a/compiler/exo/src/Dialect/IR/CircleNodes.h b/compiler/exo/src/Dialect/IR/CircleNodes.h index 7be093103..c93bd1ab0 100644 --- a/compiler/exo/src/Dialect/IR/CircleNodes.h +++ b/compiler/exo/src/Dialect/IR/CircleNodes.h @@ -53,8 +53,8 @@ private: * @brief INSTANCE_NORM in circle */ class CircleInstanceNorm final - : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>, - public CircleNodeMixin<CircleNodeTrait::FusedActFunc> + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: /// @note Currently only support FLOAT32 as input node diff --git a/compiler/exo/src/Dialect/IR/TFLNodes.h b/compiler/exo/src/Dialect/IR/TFLNodes.h index 41a11e7c0..1642eb1f4 100644 --- a/compiler/exo/src/Dialect/IR/TFLNodes.h +++ b/compiler/exo/src/Dialect/IR/TFLNodes.h @@ -129,7 +129,9 @@ class TFLAveragePool2D final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::A public TFLNodeMixin<TFLNodeTrait::FusedActFunc> { public: - TFLAveragePool2D() : _padding(Padding::UNDEFINED) { /* empty */} + TFLAveragePool2D() : _padding(Padding::UNDEFINED) + { /* empty */ + } public: loco::Node *value(void) const { return at(0)->node(); } @@ -240,9 +242,9 @@ private: * @brief DEPTHWISE_CONV_2D in TensorFlow Lite */ class TFLDepthwiseConv2D final - : public FixedArityNode<3, TFLNodeImpl<TFLOpcode::DEPTHWISE_CONV_2D>>, - public TFLNodeMixin<TFLNodeTrait::FusedActFunc>, - public TFLNodeMixin<TFLNodeTrait::Bias> + : public FixedArityNode<3, TFLNodeImpl<TFLOpcode::DEPTHWISE_CONV_2D>>, + public TFLNodeMixin<TFLNodeTrait::FusedActFunc>, + public TFLNodeMixin<TFLNodeTrait::Bias> { public: loco::Node *input(void) const { return at(0)->node(); } @@ -325,7 +327,9 @@ class TFLMaxPool2D final : public FixedArityNode<1, TFLNodeImpl<TFLOpcode::MAX_P public TFLNodeMixin<TFLNodeTrait::FusedActFunc> { public: - TFLMaxPool2D() : _padding(Padding::UNDEFINED) { /* empty */} + TFLMaxPool2D() : _padding(Padding::UNDEFINED) + { /* empty */ + } public: loco::Node *value(void) const { return at(0)->node(); } @@ -463,7 +467,7 @@ public: }; class TFLSquaredDifference final - : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::SQUARED_DIFFERENCE>> + : public FixedArityNode<2, TFLNodeImpl<TFLOpcode::SQUARED_DIFFERENCE>> { public: TFLSquaredDifference() = default; diff --git a/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp index b68728b47..5a7e71dcf 100644 --- a/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp +++ b/compiler/exo/src/Dialect/Service/TFLShapeInferenceRule.test.cpp @@ -26,8 +26,6 @@ #include <loco/Service/CanonicalShapeInferenceRule.h> #include <loco/Service/MultiDialectShapeInferenceRule.h> -#include <stdex/Memory.h> - #include <gtest/gtest.h> TEST(TFLShapeInferenceRuleTest, minimal_with_TFLRelu) @@ -53,7 +51,7 @@ TEST(TFLShapeInferenceRuleTest, minimal_with_TFLRelu) loco::MultiDialectShapeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(locoex::TFLDialect::get(), &tfl_rule); + .bind(locoex::TFLDialect::get(), &tfl_rule); loco::apply(&rules).to(graph.g.get()); @@ -98,7 +96,7 @@ TEST(TFLShapeInferenceRuleTest, avgpool2d_valid) loco::MultiDialectShapeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(locoex::TFLDialect::get(), &tfl_rule); + .bind(locoex::TFLDialect::get(), &tfl_rule); loco::apply(&rules).to(graph.g.get()); @@ -145,7 +143,7 @@ TEST(TFLShapeInferenceRuleTest, avgpool2d_same) loco::MultiDialectShapeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(locoex::TFLDialect::get(), &tfl_rule); + .bind(locoex::TFLDialect::get(), &tfl_rule); loco::apply(&rules).to(graph.g.get()); diff --git a/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp index 9326e5e58..df7aee49c 100644 --- a/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp +++ b/compiler/exo/src/Dialect/Service/TFLTypeInferenceRule.test.cpp @@ -24,8 +24,6 @@ #include <loco/IR/CanonicalDialect.h> #include <loco/Service/TypeInference.h> -#include <stdex/Memory.h> - #include <gtest/gtest.h> TEST(TFLTypeInferenceRuleTest, minimal_with_TFLRelu) diff --git a/compiler/exo/src/ExoFormattedGraph.h b/compiler/exo/src/ExoFormattedGraph.h index 714e483b5..ec4173329 100644 --- a/compiler/exo/src/ExoFormattedGraph.h +++ b/compiler/exo/src/ExoFormattedGraph.h @@ -19,7 +19,7 @@ #include <locop/FormattedGraph.h> -#include <stdex/Memory.h> +#include <memory> namespace exo { @@ -47,7 +47,7 @@ public: public: std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final { - return stdex::make_unique<NodeSummaryBuilder>(tlb); + return std::make_unique<NodeSummaryBuilder>(tlb); } }; diff --git a/compiler/exo/src/ExoOptimize.cpp b/compiler/exo/src/ExoOptimize.cpp index d7278e900..752693f38 100644 --- a/compiler/exo/src/ExoOptimize.cpp +++ b/compiler/exo/src/ExoOptimize.cpp @@ -22,7 +22,7 @@ #include <logo/Phase.h> -#include <stdex/Memory.h> +#include <memory> namespace exo { @@ -32,36 +32,36 @@ void optimize(loco::Graph *g) logo::Phase phase; { // prepare type and shape before optimization - phase.emplace_back(stdex::make_unique<TypeInferencePass>()); - phase.emplace_back(stdex::make_unique<ShapeInferencePass>()); + phase.emplace_back(std::make_unique<TypeInferencePass>()); + phase.emplace_back(std::make_unique<ShapeInferencePass>()); - phase.emplace_back(stdex::make_unique<FoldReshapeOfConstPass>()); - phase.emplace_back(stdex::make_unique<FoldTransposeOfConstPass>()); + phase.emplace_back(std::make_unique<FoldReshapeOfConstPass>()); + phase.emplace_back(std::make_unique<FoldTransposeOfConstPass>()); if (get<Knob::UseFuseBiasAddPass>()) { - phase.emplace_back(stdex::make_unique<FuseBiasAddPass>()); + phase.emplace_back(std::make_unique<FuseBiasAddPass>()); } if (get<Knob::UseFuseInstanceNormPass>()) { - phase.emplace_back(stdex::make_unique<FuseInstanceNormPass>()); + phase.emplace_back(std::make_unique<FuseInstanceNormPass>()); } if (get<Knob::UseFuseReluPass>()) { - phase.emplace_back(stdex::make_unique<FuseReluPass>()); + phase.emplace_back(std::make_unique<FuseReluPass>()); } - phase.emplace_back(stdex::make_unique<FuseRsqrtPass>()); + phase.emplace_back(std::make_unique<FuseRsqrtPass>()); if (get<Knob::UseFuseSquaredDifferencePass>()) { - phase.emplace_back(stdex::make_unique<FuseSquaredDifferencePass>()); + phase.emplace_back(std::make_unique<FuseSquaredDifferencePass>()); } - phase.emplace_back(stdex::make_unique<MergeConcatNodesPass>()); + phase.emplace_back(std::make_unique<MergeConcatNodesPass>()); - phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>()); + phase.emplace_back(std::make_unique<logo::RemoveDeadNodePass>()); } logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g}; diff --git a/compiler/exo/src/GraphBlock.cpp b/compiler/exo/src/GraphBlock.cpp index 0a45ce8ad..b26f2e8b6 100644 --- a/compiler/exo/src/GraphBlock.cpp +++ b/compiler/exo/src/GraphBlock.cpp @@ -19,7 +19,7 @@ #include "Check.h" #include <loco.h> -#include <stdex/Memory.h> +#include <memory> namespace { @@ -114,7 +114,7 @@ template <FeatureLayout T> loco::FeatureEncode *make_feature_encode(loco::Node * EXO_ASSERT(input_for_encode != nullptr, "input should not be nullptr"); loco::Graph *g = input_for_encode->graph(); - auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); encoder->perm(perm<T>()); @@ -130,7 +130,7 @@ template <FeatureLayout T> loco::FeatureDecode *make_feature_decode(loco::Node * EXO_ASSERT(input_for_decode != nullptr, "input should not be nullptr"); loco::Graph *g = input_for_decode->graph(); - auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); decoder->perm(perm<T>()); @@ -146,7 +146,7 @@ template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *inp EXO_ASSERT(input_for_encode != nullptr, "filter should not be nullptr"); loco::Graph *g = input_for_encode->graph(); - auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); encoder->perm(perm<T>()); @@ -162,7 +162,7 @@ template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *inp EXO_ASSERT(input_for_decode != nullptr, "filter should not be nullptr"); loco::Graph *g = input_for_decode->graph(); - auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>(); + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>(); decoder->perm(perm<T>()); @@ -179,7 +179,7 @@ loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode) EXO_ASSERT(input_for_decode != nullptr, "filter should not be nullptr"); loco::Graph *g = input_for_decode->graph(); - auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::DepthwiseFilter>>(); + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::DepthwiseFilter>>(); decoder->perm(perm<T>()); @@ -195,7 +195,7 @@ template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *inp EXO_ASSERT(input_for_encode != nullptr, "input should not be nullptr"); loco::Graph *g = input_for_encode->graph(); - auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Matrix>>(); + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Matrix>>(); encoder->perm(perm<T>()); @@ -211,7 +211,7 @@ template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *inp EXO_ASSERT(input_for_decode != nullptr, "input should not be nullptr"); loco::Graph *g = input_for_decode->graph(); - auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Matrix>>(); + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Matrix>>(); decoder->perm(perm<T>()); diff --git a/compiler/exo/src/GraphBlock.h b/compiler/exo/src/GraphBlock.h index b771c821b..96e4b0831 100644 --- a/compiler/exo/src/GraphBlock.h +++ b/compiler/exo/src/GraphBlock.h @@ -72,7 +72,7 @@ template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *inp /// @brief Create a loco::MatrixDecode of given layout template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode); -} // exo +} // namespace exo // // DomainConverter diff --git a/compiler/exo/src/Log.cpp b/compiler/exo/src/Log.cpp index aa762968b..cbe9ecb73 100644 --- a/compiler/exo/src/Log.cpp +++ b/compiler/exo/src/Log.cpp @@ -17,7 +17,6 @@ #include "Log.h" #include <hermes/ConsoleReporter.h> -#include <stdex/Memory.h> #include <cstdlib> #include <iostream> diff --git a/compiler/exo/src/LogHelper.cpp b/compiler/exo/src/LogHelper.cpp index 7520b7ec8..153356632 100644 --- a/compiler/exo/src/LogHelper.cpp +++ b/compiler/exo/src/LogHelper.cpp @@ -72,7 +72,7 @@ namespace exo FormattedGraph fmt(loco::Graph *g) { - auto node_summary_builder = stdex::make_unique<NodeSummaryBuilderFactory>(); + auto node_summary_builder = std::make_unique<NodeSummaryBuilderFactory>(); return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder))); } diff --git a/compiler/exo/src/LoggingContext.cpp b/compiler/exo/src/LoggingContext.cpp index 1c14d97b9..120a50e7b 100644 --- a/compiler/exo/src/LoggingContext.cpp +++ b/compiler/exo/src/LoggingContext.cpp @@ -18,7 +18,8 @@ #include "Log.h" // To use LoggerConfig #include <hermes/ConsoleReporter.h> -#include <stdex/Memory.h> + +#include <memory> namespace exo { @@ -30,11 +31,11 @@ hermes::Context *LoggingContext::get(void) if (ctx == nullptr) { ctx = new hermes::Context; - ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>()); - ctx->config(stdex::make_unique<LoggerConfig>()); + ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>()); + ctx->config(std::make_unique<LoggerConfig>()); } return ctx; } -} // namespac exo +} // namespace exo diff --git a/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp b/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp index 005c42944..66c99121e 100644 --- a/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp +++ b/compiler/exo/src/Pass/FoldTransposeOfConstPass.cpp @@ -124,7 +124,7 @@ void fold_transpose_of_const(locoex::TFLTranspose *transpose) index_orig.at(perm->at<S32>(axis)) = index_new.at(axis); const_new->at<FLOAT32>(l.offset(shape_new, index_new)) = - const_orig->at<FLOAT32>(l.offset(shape_orig, index_orig)); + const_orig->at<FLOAT32>(l.offset(shape_orig, index_orig)); } // replace diff --git a/compiler/exo/src/Pass/FuseBiasAddPass.cpp b/compiler/exo/src/Pass/FuseBiasAddPass.cpp index 6338dff5d..0e797dc80 100644 --- a/compiler/exo/src/Pass/FuseBiasAddPass.cpp +++ b/compiler/exo/src/Pass/FuseBiasAddPass.cpp @@ -136,7 +136,7 @@ public: Fuser(LatterT *latter) { static_assert(std::is_same<LatterT, locoex::TFLAdd>::value || - std::is_same<LatterT, locoex::TFLSub>::value, + std::is_same<LatterT, locoex::TFLSub>::value, "wrong template type"); _latter = latter; @@ -185,7 +185,7 @@ template <class LatterT> locoex::TFLConst *Fuser<LatterT>::create_fused_bias_con for (uint32_t x = 0; x < bias->dim(0).value(); x++) new_bias->at<loco::DataType::FLOAT32>(x) = calc<LatterT>( - bias->at<loco::DataType::FLOAT32>(x), _const_node->at<loco::DataType::FLOAT32>(x)); + bias->at<loco::DataType::FLOAT32>(x), _const_node->at<loco::DataType::FLOAT32>(x)); } return new_bias; @@ -252,14 +252,14 @@ struct Collector final : public locoex::TFLNodeMutableVisitor<void> void setCandidate(FormerT *former, LatterT *latter, locoex::TFLConst *const_node) { static_assert(std::is_same<LatterT, locoex::TFLAdd>::value || - std::is_same<LatterT, locoex::TFLSub>::value, + std::is_same<LatterT, locoex::TFLSub>::value, "wrong template type"); if (!check_act_func(former)) return; auto depth = - loco::shape_get(as_loco_node(former)).template as<loco::TensorShape>().dim(3).value(); + loco::shape_get(as_loco_node(former)).template as<loco::TensorShape>().dim(3).value(); auto const_shape = loco::shape_get(const_node).template as<loco::TensorShape>(); if (const_shape.rank() == 1 and const_shape.dim(0) == depth) diff --git a/compiler/exo/src/Pass/FuseInstanceNormPass.cpp b/compiler/exo/src/Pass/FuseInstanceNormPass.cpp index 04d4a62cd..40aa9144f 100644 --- a/compiler/exo/src/Pass/FuseInstanceNormPass.cpp +++ b/compiler/exo/src/Pass/FuseInstanceNormPass.cpp @@ -291,7 +291,7 @@ bool InstanceNormPattern::matched() CHECK_OR_FALSE(add_as_variance); CHECK_OR_FALSE( - fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); + fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); // TODO Support regarding broadcast @@ -317,7 +317,7 @@ bool InstanceNormPattern::matched() locoex::TFLMul *mul_gamma_should_be = nullptr; locoex::TFLMean *mean_of_ifm_should_be = nullptr; CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_ifm_should_be) - .with_commutative_args_of(mul_as_scaled_mean)); + .with_commutative_args_of(mul_as_scaled_mean)); CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be); CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be); #undef CHECK_OR_FALSE diff --git a/compiler/exo/src/Pass/FuseReluPass.test.cpp b/compiler/exo/src/Pass/FuseReluPass.test.cpp index 6f83d4dd0..fd6f88d9c 100644 --- a/compiler/exo/src/Pass/FuseReluPass.test.cpp +++ b/compiler/exo/src/Pass/FuseReluPass.test.cpp @@ -73,8 +73,8 @@ template <class FusedTFLType, locoex::FusedActFunc FusedActFunc> void test() { static_assert((std::is_same<FusedTFLType, locoex::TFLRelu>::value && FusedActFunc == locoex::FusedActFunc::RELU) || - (std::is_same<FusedTFLType, locoex::TFLRelu6>::value && - FusedActFunc == locoex::FusedActFunc::RELU6), + (std::is_same<FusedTFLType, locoex::TFLRelu6>::value && + FusedActFunc == locoex::FusedActFunc::RELU6), "wrong template type"); exo::test::TestGraph g; diff --git a/compiler/exo/src/Pass/MergeConcatNodesPass.cpp b/compiler/exo/src/Pass/MergeConcatNodesPass.cpp index 8945fcfce..5885332a6 100644 --- a/compiler/exo/src/Pass/MergeConcatNodesPass.cpp +++ b/compiler/exo/src/Pass/MergeConcatNodesPass.cpp @@ -39,8 +39,8 @@ bool canMerge(locoex::TFLConcatenation *node1, locoex::TFLConcatenation *node2) case locoex::FusedActFunc::RELU6: return true; - // case locoex::FusedActFunc::TANH: - // return false; + // case locoex::FusedActFunc::TANH: + // return false; default: INTERNAL_EXN_V("Unknown FusedActFunc", oops::to_uint32(node1->fusedActivationFunction())); diff --git a/compiler/exo/src/Pass/ShapeInferencePass.cpp b/compiler/exo/src/Pass/ShapeInferencePass.cpp index bc60f91c4..367d7da91 100644 --- a/compiler/exo/src/Pass/ShapeInferencePass.cpp +++ b/compiler/exo/src/Pass/ShapeInferencePass.cpp @@ -49,9 +49,9 @@ bool ShapeInferencePass::run(loco::Graph *g) loco::MultiDialectShapeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(locoex::TFLDialect::get(), &tfl_rule) - .bind(locoex::CircleDialect::get(), &circle_rule) - .bind(locoex::COpDialect::get(), &cop_rule); + .bind(locoex::TFLDialect::get(), &tfl_rule) + .bind(locoex::CircleDialect::get(), &circle_rule) + .bind(locoex::COpDialect::get(), &cop_rule); return loco::apply(&rules).to(g); } diff --git a/compiler/exo/src/Pass/TypeInferencePass.cpp b/compiler/exo/src/Pass/TypeInferencePass.cpp index 31d4f13b6..52a9d0c33 100644 --- a/compiler/exo/src/Pass/TypeInferencePass.cpp +++ b/compiler/exo/src/Pass/TypeInferencePass.cpp @@ -47,9 +47,9 @@ bool TypeInferencePass::run(loco::Graph *g) loco::MultiDialectTypeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(locoex::TFLDialect::get(), &tfl_rule) - .bind(locoex::CircleDialect::get(), &circle_rule) - .bind(locoex::COpDialect::get(), &cop_rule); + .bind(locoex::TFLDialect::get(), &tfl_rule) + .bind(locoex::CircleDialect::get(), &circle_rule) + .bind(locoex::COpDialect::get(), &cop_rule); return loco::apply(&rules).to(g); } diff --git a/compiler/exo/src/ProgressReporter.h b/compiler/exo/src/ProgressReporter.h index b0f420df9..83f327309 100644 --- a/compiler/exo/src/ProgressReporter.h +++ b/compiler/exo/src/ProgressReporter.h @@ -28,7 +28,7 @@ class ProgressReporter : public logo::PhaseEventListener { public: ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) - : _graph{graph}, _strategy{strategy} + : _graph{graph}, _strategy{strategy} { // DO NOTHING } diff --git a/compiler/exo/src/TFLite/TFLExporter.cpp b/compiler/exo/src/TFLite/TFLExporter.cpp index cf002b3e1..71131b725 100644 --- a/compiler/exo/src/TFLite/TFLExporter.cpp +++ b/compiler/exo/src/TFLite/TFLExporter.cpp @@ -18,16 +18,15 @@ #include "TFLExporterImpl.h" -#include <stdex/Memory.h> - #include <oops/InternalExn.h> +#include <memory> #include <fstream> namespace exo { -TFLExporter::TFLExporter(loco::Graph *graph) : _impl(stdex::make_unique<Impl>(graph)) +TFLExporter::TFLExporter(loco::Graph *graph) : _impl(std::make_unique<Impl>(graph)) { // NOTHING TO DO } diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.cpp b/compiler/exo/src/TFLite/TFLExporterImpl.cpp index 07adbfb9d..1f6d1bd59 100644 --- a/compiler/exo/src/TFLite/TFLExporterImpl.cpp +++ b/compiler/exo/src/TFLite/TFLExporterImpl.cpp @@ -88,7 +88,7 @@ encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<OpCode, uint3 INTERNAL_EXN("Cannot find code for custom op"); operator_codes_vec[idx] = - CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second)); + CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second)); } } return builder.CreateVector(operator_codes_vec); @@ -146,7 +146,7 @@ void TFLExporter::Impl::exportGraph(loco::Graph *graph) // encode operator codes auto operator_codes = - encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes); + encodeOperatorCodes(_builder, gd._operator_codes, gd._custom_operator_codes); // Subgraphs Offset<SubGraph> subgraph = exportSubgraph(gd); diff --git a/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp b/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp index 866ede6a2..c337b38d3 100644 --- a/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp +++ b/compiler/exo/src/TFLite/TFLExporterImpl.test.cpp @@ -23,7 +23,8 @@ #include "Knob.h" #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> + +#include <memory> #include <gtest/gtest.h> @@ -56,7 +57,7 @@ template <> loco::FeatureEncode *TFLExporterImplTests::make_node(void) { loco::FeatureEncode *encode_layer = graph()->nodes()->create<loco::FeatureEncode>(); - auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); (*encoder->perm())[loco::FeatureAxis::Count] = 0; (*encoder->perm())[loco::FeatureAxis::Depth] = 1; (*encoder->perm())[loco::FeatureAxis::Height] = 2; @@ -70,7 +71,7 @@ template <> loco::FeatureDecode *TFLExporterImplTests::make_node(void) { loco::FeatureDecode *decode_layer = graph()->nodes()->create<loco::FeatureDecode>(); - auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); (*decoder->perm())[loco::FeatureAxis::Count] = 0; (*decoder->perm())[loco::FeatureAxis::Depth] = 1; (*decoder->perm())[loco::FeatureAxis::Height] = 2; @@ -227,7 +228,7 @@ TEST(TFLExporterImplTest, Transpose_simple) auto bufs = (model->buffers()); auto *perm_buf = - reinterpret_cast<const int32_t *>(bufs->Get(perm_tensor->buffer())->data()->data()); + reinterpret_cast<const int32_t *>(bufs->Get(perm_tensor->buffer())->data()->data()); ASSERT_EQ(1, perm_buf[0]); ASSERT_EQ(2, perm_buf[1]); @@ -285,7 +286,7 @@ TEST(TFLExporterImplTest, Transpose_from_FilterEncode_FilterDecode) auto bufs = (model->buffers()); auto *perm_buf = - reinterpret_cast<const int32_t *>(bufs->Get(perm_tensor->buffer())->data()->data()); + reinterpret_cast<const int32_t *>(bufs->Get(perm_tensor->buffer())->data()->data()); ASSERT_EQ(3, perm_buf[0]); ASSERT_EQ(0, perm_buf[1]); ASSERT_EQ(1, perm_buf[2]); diff --git a/compiler/exo/src/TFLite/TFLExporterUtils.cpp b/compiler/exo/src/TFLite/TFLExporterUtils.cpp index d35afc9aa..daec03c40 100644 --- a/compiler/exo/src/TFLite/TFLExporterUtils.cpp +++ b/compiler/exo/src/TFLite/TFLExporterUtils.cpp @@ -78,13 +78,13 @@ tflite::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> * // // NOTE input and output 'feature' map are shape of NHWC bool same_padding_criterion_1 = - (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && - (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); + (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); // For same padding, rear padding is same or bigger than front padding by at most 1 bool same_padding_criterion_2 = - (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && - (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); + (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && + (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); if (same_padding_criterion_1 && same_padding_criterion_2) return tflite::Padding_SAME; @@ -120,8 +120,7 @@ void registerGraphIOName(loco::Graph *graph, SerializedModelData &gd) } } -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace @@ -147,7 +146,7 @@ private: void set_tensor_index(loco::Node *node, const TFLTensorIndex &tensor_id) { assert(node->annot<TFLTensorIndexAnnotation>() == nullptr); - node->annot(stdex::make_unique<TFLTensorIndexAnnotation>(tensor_id)); + node->annot(std::make_unique<TFLTensorIndexAnnotation>(tensor_id)); } TFLTensorIndex get_tensor_index(loco::Node *node) diff --git a/compiler/exo/src/TFLite/TFLOperationExporter.cpp b/compiler/exo/src/TFLite/TFLOperationExporter.cpp index 79b5b6287..b7a0ffea8 100644 --- a/compiler/exo/src/TFLite/TFLOperationExporter.cpp +++ b/compiler/exo/src/TFLite/TFLOperationExporter.cpp @@ -81,13 +81,19 @@ public: void visit(loco::ReLU *) final; void visit(loco::ReLU6 *) final; void visit(loco::Tanh *) final; - void visit(loco::Push *) final { /* DO NOTHING */} - void visit(loco::Pull *) final { /* DO NOTHING */} + void visit(loco::Push *) final + { /* DO NOTHING */ + } + void visit(loco::Pull *) final + { /* DO NOTHING */ + } void visit(loco::FeatureEncode *) final; void visit(loco::FeatureDecode *) final; void visit(loco::FilterEncode *) final; void visit(loco::DepthwiseFilterEncode *) final; - void visit(loco::ConstGen *) final { /* skip, everything is done in exportOpDefinedTensors */} + void visit(loco::ConstGen *) final + { /* skip, everything is done in exportOpDefinedTensors */ + } void visit(loco::MaxPool2D *) final; void visit(loco::AvgPool2D *) final; void visit(loco::Conv2D *) final; @@ -227,7 +233,7 @@ void OperationExporter::visit(locoex::TFLFullyConnected *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); auto options = - CreateFullyConnectedOptions(builder, to_tflite_actfunc(node->fusedActivationFunction())); + CreateFullyConnectedOptions(builder, to_tflite_actfunc(node->fusedActivationFunction())); // Make FULLY_CONNECTED operator auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, @@ -367,8 +373,8 @@ void OperationExporter::visit(locoex::TFLTranspose *node) auto options = CreateTransposeOptions(builder); auto op_offset = - CreateOperator(builder, op_idx, inputs, outputs, - tflite::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union()); + CreateOperator(builder, op_idx, inputs, outputs, + tflite::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union()); gd._operators.push_back(op_offset); } @@ -385,7 +391,7 @@ void OperationExporter::visit(locoex::TFLTransposeConv *node) auto outputs = builder.CreateVector(outputs_vec); tflite::Padding padding = getOpPadding(node->padding()); auto options = - CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h()); + CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h()); // Make TRANSPOSE_CONV operator auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, @@ -397,7 +403,7 @@ template <class TFLPool2D> void OperationExporter::export_pool_2d(TFLPool2D *node, tflite::BuiltinOperator builtin_op) { EXO_ASSERT(builtin_op == tflite::BuiltinOperator_MAX_POOL_2D || - builtin_op == tflite::BuiltinOperator_AVERAGE_POOL_2D, + builtin_op == tflite::BuiltinOperator_AVERAGE_POOL_2D, "should be maxpool or avgpool"); EXO_ASSERT(node->padding() != locoex::Padding::UNDEFINED, "Padding is not set"); @@ -458,10 +464,10 @@ void OperationExporter::visit(loco::MaxPool2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); tflite::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); - auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical(), node->window()->horizontal(), - node->window()->vertical()); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = + CreatePool2DOptions(builder, padding, node->stride()->horizontal(), node->stride()->vertical(), + node->window()->horizontal(), node->window()->vertical()); auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, tflite::BuiltinOptions_Pool2DOptions, options.Union()); gd._operators.push_back(op_offset); @@ -478,10 +484,10 @@ void OperationExporter::visit(loco::AvgPool2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); tflite::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); - auto options = CreatePool2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical(), node->window()->horizontal(), - node->window()->vertical()); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = + CreatePool2DOptions(builder, padding, node->stride()->horizontal(), node->stride()->vertical(), + node->window()->horizontal(), node->window()->vertical()); auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, tflite::BuiltinOptions_Pool2DOptions, options.Union()); gd._operators.push_back(op_offset); @@ -504,7 +510,7 @@ void OperationExporter::visit(loco::Conv2D *node) std::vector<float> bias_vec_data(bias_vec_size); // initialized as zero vector auto bias_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); @@ -516,7 +522,7 @@ void OperationExporter::visit(loco::Conv2D *node) auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); auto bias_tensor_offset = - CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); gd._tensors.push_back(bias_tensor_offset); // Make input, output and options for operator @@ -526,9 +532,9 @@ void OperationExporter::visit(loco::Conv2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); tflite::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); - auto options = CreateConv2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical()); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + auto options = + CreateConv2DOptions(builder, padding, node->stride()->horizontal(), node->stride()->vertical()); // Make CONV_2D operator auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, @@ -558,7 +564,7 @@ void OperationExporter::visit(loco::TransposedConv2D *node) } auto outshape_vec_offset = builder.CreateVector( - reinterpret_cast<uint8_t *>(outshape_vec_data.data()), raw_outshape_vec_size); + reinterpret_cast<uint8_t *>(outshape_vec_data.data()), raw_outshape_vec_size); auto outshape_buffer_offset = CreateBuffer(builder, outshape_vec_offset); @@ -607,7 +613,7 @@ void OperationExporter::visit(loco::DepthwiseConv2D *node) size_t raw_bias_vec_size = bias_vec_size * sizeof(int32_t); std::vector<float> bias_vec_data(bias_vec_size); auto bias_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(bias_vec_data.data()), raw_bias_vec_size); auto bias_buffer_offset = CreateBuffer(builder, bias_vec_offset); @@ -619,7 +625,7 @@ void OperationExporter::visit(loco::DepthwiseConv2D *node) auto name_offset = builder.CreateString("t_" + std::to_string(bias_tensor_id)); auto bias_tensor_offset = - CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); + CreateTensor(builder, bias_vec_shape_offset, TensorType_FLOAT32, bias_buffer_id, name_offset); gd._tensors.push_back(bias_tensor_offset); std::vector<int32_t> inputs_vec{get_tensor_index(node->ifm()), get_tensor_index(node->ker()), @@ -628,13 +634,13 @@ void OperationExporter::visit(loco::DepthwiseConv2D *node) auto inputs = builder.CreateVector(inputs_vec); auto outputs = builder.CreateVector(outputs_vec); tflite::Padding padding = getOpPadding( - node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); + node->pad(), node->stride(), ShapeInference::get(node->ifm()), ShapeInference::get(node)); int32_t ifm_channel_size = ShapeInference::get(node->ifm())._dims[3]; // multiplier = bias_vec_size(output_size)/ifm_channel_size auto options = - CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(), - node->stride()->vertical(), bias_vec_size / ifm_channel_size); + CreateDepthwiseConv2DOptions(builder, padding, node->stride()->horizontal(), + node->stride()->vertical(), bias_vec_size / ifm_channel_size); auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, tflite::BuiltinOptions_DepthwiseConv2DOptions, options.Union()); @@ -668,7 +674,7 @@ void OperationExporter::visit(loco::TensorReduce *node) size_t raw_axes_vec_size = axes_vec_size * sizeof(int32_t); auto axes_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(axes_vec.data()), raw_axes_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(axes_vec.data()), raw_axes_vec_size); auto axes_buffer_offset = CreateBuffer(builder, axes_vec_offset); @@ -680,7 +686,7 @@ void OperationExporter::visit(loco::TensorReduce *node) auto name_offset = builder.CreateString("t_" + std::to_string(axes_tensor_id)); auto axes_tensor_offset = - CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset); + CreateTensor(builder, axes_vec_shape_offset, TensorType_INT32, axes_buffer_id, name_offset); gd._tensors.push_back(axes_tensor_offset); std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), axes_tensor_id}; @@ -743,7 +749,7 @@ void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, constexpr size_t raw_perm_vec_size = perm_vec_size * sizeof(int32_t); auto perm_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(perm_vec_data.data()), raw_perm_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(perm_vec_data.data()), raw_perm_vec_size); auto perm_buffer_offset = CreateBuffer(builder, perm_vec_offset); @@ -755,7 +761,7 @@ void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, auto name_offset = builder.CreateString("t_" + std::to_string(perm_tensor_id)); auto perm_tensor_offset = - CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset); + CreateTensor(builder, perm_vec_shape_offset, TensorType_INT32, perm_buffer_id, name_offset); gd._tensors.push_back(perm_tensor_offset); // Create permutation node @@ -769,7 +775,7 @@ void exportAsTranspose(loco::Node *node, FlatBufferBuilder &builder, constexpr auto options_type = tflite::BuiltinOptions::BuiltinOptions_TransposeOptions; auto transpose_offset = - CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union()); + CreateOperator(builder, op_idx, inputs, outputs, options_type, options.Union()); gd._operators.push_back(transpose_offset); } @@ -854,11 +860,11 @@ void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder, // but also by input. auto input_shape_shape_vec_offset = - builder.CreateVector(std::vector<int32_t>{(int32_t)new_shape_vec.size()}); + builder.CreateVector(std::vector<int32_t>{(int32_t)new_shape_vec.size()}); size_t input_shape_vec_size = new_shape_vec.size() * sizeof(int32_t); auto input_shape_input_vec_offset = - builder.CreateVector(reinterpret_cast<uint8_t *>(new_shape_vec.data()), input_shape_vec_size); + builder.CreateVector(reinterpret_cast<uint8_t *>(new_shape_vec.data()), input_shape_vec_size); auto input_shape_buffer_offset = CreateBuffer(builder, input_shape_input_vec_offset); const auto input_shape_buffer_id = static_cast<uint32_t>(gd._buffers.size()); @@ -867,7 +873,7 @@ void exportAsReshape(loco::Node *node, FlatBufferBuilder &builder, auto input_shape_tensor_id = static_cast<int32_t>(gd._tensors.size()); auto name_offset = builder.CreateString("t_" + std::to_string(input_shape_tensor_id)); auto input_shape_tensor_offset = CreateTensor( - builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset); + builder, input_shape_shape_vec_offset, TensorType_INT32, input_shape_buffer_id, name_offset); gd._tensors.push_back(input_shape_tensor_offset); uint32_t op_idx = gd.registerBuiltinOpcode(tflite::BuiltinOperator_RESHAPE); @@ -1069,7 +1075,7 @@ void OperationExporter::visit(loco::TensorConstantPad *node) auto padding_shape_vec_ptr = builder.CreateVector(std::vector<int32_t>{padding_vec_size, 2}); // create tensor auto padding_tensor_ptr = - CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id); + CreateTensor(builder, padding_shape_vec_ptr, TensorType_INT32, padding_buffer_id); // get tensor id const auto padding_tensor_id = static_cast<int32_t>(gd._tensors.size()); diff --git a/compiler/exo/src/TFLite/TFLTensorExporter.cpp b/compiler/exo/src/TFLite/TFLTensorExporter.cpp index 23c810ed5..2fb6f0c13 100644 --- a/compiler/exo/src/TFLite/TFLTensorExporter.cpp +++ b/compiler/exo/src/TFLite/TFLTensorExporter.cpp @@ -89,7 +89,7 @@ struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor<bool> bool visit(loco::FeatureEncode *node) final { auto encoder = - loco::must_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder()); + loco::must_cast<loco::PermutingEncoder<loco::Domain::Feature> *>(node->encoder()); auto perm = encoder->perm(); return isNHWC(perm); } @@ -97,7 +97,7 @@ struct NoOpDetector final : public loco::CanonicalNodeMutableVisitor<bool> bool visit(loco::FeatureDecode *node) final { auto decoder = - loco::must_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder()); + loco::must_cast<loco::PermutingDecoder<loco::Domain::Feature> *>(node->decoder()); auto perm = decoder->perm(); return isNHWC(perm); } diff --git a/compiler/exo/src/TFLite/TFLTypeInference.cpp b/compiler/exo/src/TFLite/TFLTypeInference.cpp index 8d6bb8d8c..56817ee3b 100644 --- a/compiler/exo/src/TFLite/TFLTypeInference.cpp +++ b/compiler/exo/src/TFLite/TFLTypeInference.cpp @@ -31,8 +31,6 @@ #include <oops/InternalExn.h> -#include <stdex/Memory.h> - #include <stdexcept> #include <type_traits> diff --git a/compiler/exo/src/TFLite/TFLTypeInference.test.cpp b/compiler/exo/src/TFLite/TFLTypeInference.test.cpp index 8a3a08da9..054dad1f1 100644 --- a/compiler/exo/src/TFLite/TFLTypeInference.test.cpp +++ b/compiler/exo/src/TFLite/TFLTypeInference.test.cpp @@ -18,12 +18,9 @@ #include "Pass/TypeInferencePass.h" #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> #include <gtest/gtest.h> -using stdex::make_unique; - namespace { diff --git a/compiler/exo/src/TestGraph.h b/compiler/exo/src/TestGraph.h index f919cc9ae..46c2264ab 100644 --- a/compiler/exo/src/TestGraph.h +++ b/compiler/exo/src/TestGraph.h @@ -23,8 +23,6 @@ #include <loco.h> -#include <stdex/Memory.h> - #include <cassert> namespace exo @@ -284,7 +282,7 @@ public: { filterEncode = exo::make_filter_encode<exo::FilterLayout::HWIO>(pull); // from Tensorflow filterDecode = - exo::make_filter_decode<exo::FilterLayout::OHWI>(filterEncode); // to Tensorflow Lite + exo::make_filter_decode<exo::FilterLayout::OHWI>(filterEncode); // to Tensorflow Lite complete(filterDecode); } }; diff --git a/compiler/exo/src/TestHelper.h b/compiler/exo/src/TestHelper.h index 1a3de50f5..bacaa3e5e 100644 --- a/compiler/exo/src/TestHelper.h +++ b/compiler/exo/src/TestHelper.h @@ -26,7 +26,7 @@ #include <loco.h> -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> @@ -54,11 +54,11 @@ public: TypeShapeReadyPhase() { // Type and Shape inference is prerequisite for run other test - _phase.emplace_back(stdex::make_unique<::exo::TypeInferencePass>()); - _phase.emplace_back(stdex::make_unique<::exo::ShapeInferencePass>()); + _phase.emplace_back(std::make_unique<::exo::TypeInferencePass>()); + _phase.emplace_back(std::make_unique<::exo::ShapeInferencePass>()); } - template <typename PassT> void add_pass() { _phase.emplace_back(stdex::make_unique<PassT>()); } + template <typename PassT> void add_pass() { _phase.emplace_back(std::make_unique<PassT>()); } void run(loco::Graph *g) { diff --git a/compiler/foder/CMakeLists.txt b/compiler/foder/CMakeLists.txt index 6a413c61e..2e44eefa6 100644 --- a/compiler/foder/CMakeLists.txt +++ b/compiler/foder/CMakeLists.txt @@ -1,2 +1,3 @@ add_library(foder INTERFACE) target_include_directories(foder INTERFACE include) +target_link_libraries(foder INTERFACE nncc_coverage) diff --git a/compiler/foder/include/foder/FileLoader.h b/compiler/foder/include/foder/FileLoader.h index e2143ecf6..f0b052a63 100644 --- a/compiler/foder/include/foder/FileLoader.h +++ b/compiler/foder/include/foder/FileLoader.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef __FODER_FILE_LOADER_H__ +#define __FODER_FILE_LOADER_H__ + #include <fstream> #include <vector> @@ -67,3 +70,5 @@ private: }; } // namespace foder + +#endif // __FODER_FILE_LOADER_H__ diff --git a/compiler/hermes-std/CMakeLists.txt b/compiler/hermes-std/CMakeLists.txt index c7b02e14c..8fce31953 100644 --- a/compiler/hermes-std/CMakeLists.txt +++ b/compiler/hermes-std/CMakeLists.txt @@ -6,7 +6,6 @@ add_library(hermes_std STATIC ${SOURCES}) set_target_properties(hermes_std PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(hermes_std PUBLIC include) target_link_libraries(hermes_std PUBLIC hermes) -target_link_libraries(hermes_std PRIVATE stdex) target_link_libraries(hermes_std PRIVATE pepper_strcast) # Let's apply nncc common compile options # @@ -23,5 +22,4 @@ endif(NOT ENABLE_TEST) nnas_find_package(GTest REQUIRED) GTest_AddTest(hermes_std_test ${TESTS}) -target_link_libraries(hermes_std_test stdex) target_link_libraries(hermes_std_test hermes_std) diff --git a/compiler/hermes-std/src/ConsoleReporter.test.cpp b/compiler/hermes-std/src/ConsoleReporter.test.cpp index c2e1f1c85..a65585a6a 100644 --- a/compiler/hermes-std/src/ConsoleReporter.test.cpp +++ b/compiler/hermes-std/src/ConsoleReporter.test.cpp @@ -16,8 +16,7 @@ #include "hermes/ConsoleReporter.h" -#include <stdex/Memory.h> - +#include <memory> #include <sstream> #include <gtest/gtest.h> @@ -37,7 +36,7 @@ TEST(ConsoleReporterTest, notify) ss << "Hello" << std::endl; - m.text(stdex::make_unique<hermes::MessageText>(ss)); + m.text(std::make_unique<hermes::MessageText>(ss)); } hermes::ConsoleReporter r; diff --git a/compiler/hermes-std/src/EnvConfig.test.cpp b/compiler/hermes-std/src/EnvConfig.test.cpp new file mode 100644 index 000000000..e4b39c167 --- /dev/null +++ b/compiler/hermes-std/src/EnvConfig.test.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 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 "hermes/EnvConfig.h" + +#include <hermes/core/SourceSetting.h> + +#include <gtest/gtest.h> + +#include <stdlib.h> + +namespace +{ + +class Logger final : public hermes::Source +{ +public: + Logger() = default; + ~Logger() = default; +}; + +std::string env_name("TEST_CONFIG"); + +} // namespace + +TEST(EnvConfigTest, constructor) +{ + hermes::EnvConfig<hermes::EnvFormat::BooleanNumber> ec(env_name); + + SUCCEED(); +} + +TEST(EnvConfigTest, configure) +{ + Logger logger; + hermes::SourceSetting ss; + hermes::EnvConfig<hermes::EnvFormat::BooleanNumber> ec(env_name); + + ec.configure(&logger, ss); + + SUCCEED(); +} + +TEST(EnvConfigTest, configure_enabled) +{ + setenv(env_name.c_str(), "1", 0); + + Logger logger; + hermes::SourceSetting ss; + hermes::EnvConfig<hermes::EnvFormat::BooleanNumber> ec(env_name); + + ec.configure(&logger, ss); + + SUCCEED(); +} diff --git a/compiler/hermes/CMakeLists.txt b/compiler/hermes/CMakeLists.txt index 5debfbca0..e1a71c2b4 100644 --- a/compiler/hermes/CMakeLists.txt +++ b/compiler/hermes/CMakeLists.txt @@ -5,7 +5,6 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_library(hermes STATIC ${SOURCES}) set_target_properties(hermes PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(hermes PUBLIC include) -target_link_libraries(hermes PRIVATE stdex) # Let's apply nncc common compile options # # NOTE This will enable strict compilation (warnings as error). @@ -22,7 +21,6 @@ nnas_find_package(GTest REQUIRED) add_executable(hermes_test ${TESTS}) target_link_libraries(hermes_test gtest_main) -target_link_libraries(hermes_test stdex) target_link_libraries(hermes_test hermes) add_test(hermes_test hermes_test) diff --git a/compiler/hermes/requires.cmake b/compiler/hermes/requires.cmake index a4855289c..e69de29bb 100644 --- a/compiler/hermes/requires.cmake +++ b/compiler/hermes/requires.cmake @@ -1 +0,0 @@ -require("stdex") diff --git a/compiler/hermes/src/core/MessageBuffer.cpp b/compiler/hermes/src/core/MessageBuffer.cpp index 175a45d3f..a4ff4eeff 100644 --- a/compiler/hermes/src/core/MessageBuffer.cpp +++ b/compiler/hermes/src/core/MessageBuffer.cpp @@ -16,7 +16,7 @@ #include "hermes/core/MessageBuffer.h" -#include <stdex/Memory.h> +#include <memory> namespace hermes { @@ -30,9 +30,9 @@ MessageBuffer::~MessageBuffer() { // NOTE The current implementation is unsafe as it may throw an excpetion. // TODO Find a better safe implementation. - auto msg = stdex::make_unique<Message>(); + auto msg = std::make_unique<Message>(); - msg->text(stdex::make_unique<MessageText>(_ss)); + msg->text(std::make_unique<MessageText>(_ss)); _bus->post(std::move(msg)); } diff --git a/compiler/hermes/src/core/Source.cpp b/compiler/hermes/src/core/Source.cpp index 33f8b0570..d124f4430 100644 --- a/compiler/hermes/src/core/Source.cpp +++ b/compiler/hermes/src/core/Source.cpp @@ -16,8 +16,7 @@ #include "hermes/core/Source.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace hermes @@ -64,7 +63,7 @@ void Source::reload(const Config *c) { c->configure(this, _setting); } std::unique_ptr<MessageBuffer> Source::buffer(const Severity &) const { // TODO Pass Severity - return stdex::make_unique<MessageBuffer>(_bus); + return std::make_unique<MessageBuffer>(_bus); } } // namespace hermes diff --git a/compiler/loco/CMakeLists.txt b/compiler/loco/CMakeLists.txt index f94052840..b1f61ade0 100644 --- a/compiler/loco/CMakeLists.txt +++ b/compiler/loco/CMakeLists.txt @@ -6,7 +6,6 @@ add_library(loco SHARED ${SOURCES}) target_include_directories(loco PUBLIC include) # TODO Remove dependencies on angkor library target_link_libraries(loco PUBLIC angkor) -target_link_libraries(loco PRIVATE stdex) # Let's apply nncc common compile options # # NOTE This will enable strict compilation (warnings as error). @@ -24,5 +23,4 @@ endif(NOT ENABLE_TEST) nnas_find_package(GTest REQUIRED) GTest_AddTest(loco_test ${TESTS}) -target_link_libraries(loco_test stdex) target_link_libraries(loco_test loco) diff --git a/compiler/loco/include/loco/IR/DataTypeTraits.h b/compiler/loco/include/loco/IR/DataTypeTraits.h index c186300de..3713ac992 100644 --- a/compiler/loco/include/loco/IR/DataTypeTraits.h +++ b/compiler/loco/include/loco/IR/DataTypeTraits.h @@ -52,6 +52,12 @@ template <> struct DataTypeImpl<DataType::S16> using Type = int16_t; }; +template <> struct DataTypeImpl<DataType::U16> +{ + // Use C++ uint16_t type for unsigned 16bit integer + using Type = uint16_t; +}; + template <> struct DataTypeImpl<DataType::S32> { // Use C++ int32_t type for 32bit integer @@ -70,12 +76,24 @@ template <> struct DataTypeImpl<DataType::S64> using Type = int64_t; }; +template <> struct DataTypeImpl<DataType::U64> +{ + // Use C++ uint64_t type for unsigned 64bit integer + using Type = uint64_t; +}; + template <> struct DataTypeImpl<DataType::FLOAT32> { // Use C++ float type for IEEE 32-bit floating-point numbers using Type = float; }; +template <> struct DataTypeImpl<DataType::FLOAT64> +{ + // Use C++ double type for IEEE 64-bit floating-point numbers + using Type = double; +}; + // NOTE DataTypeImpl for BOOL is subject to change template <> struct DataTypeImpl<DataType::BOOL> { @@ -97,14 +115,20 @@ inline uint32_t size(DataType data_type) return sizeof(DataTypeImpl<DataType::U8>::Type); case DataType::S16: return sizeof(DataTypeImpl<DataType::S16>::Type); + case DataType::U16: + return sizeof(DataTypeImpl<DataType::U16>::Type); case DataType::S32: return sizeof(DataTypeImpl<DataType::S32>::Type); case DataType::U32: return sizeof(DataTypeImpl<DataType::U32>::Type); case DataType::S64: return sizeof(DataTypeImpl<DataType::S64>::Type); + case DataType::U64: + return sizeof(DataTypeImpl<DataType::U64>::Type); case DataType::FLOAT32: return sizeof(DataTypeImpl<DataType::FLOAT32>::Type); + case DataType::FLOAT64: + return sizeof(DataTypeImpl<DataType::FLOAT64>::Type); case DataType::BOOL: return sizeof(DataTypeImpl<DataType::BOOL>::Type); default: diff --git a/compiler/loco/include/loco/IR/Nodes.h b/compiler/loco/include/loco/IR/Nodes.h index fecfad28d..63b1181bb 100644 --- a/compiler/loco/include/loco/IR/Nodes.h +++ b/compiler/loco/include/loco/IR/Nodes.h @@ -49,7 +49,7 @@ class GraphOutput; * @brief Make a value visible to user */ class Push /* to user */ final - : public CanonicalNodeDef<CanonicalOpcode::Push, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::Push, FixedArity<1>::Mixin> { public: Push() = default; @@ -91,8 +91,8 @@ Push *push_node(Graph *g, const GraphOutputIndex &index); * @brief Create a value from user data */ class Pull /* from user */ final - : public CanonicalNodeDef<CanonicalOpcode::Pull, FixedArity<0>::Mixin, - With<NodeTrait::TensorShape>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::Pull, FixedArity<0>::Mixin, + With<NodeTrait::TensorShape>::Mixin> { public: Pull() = default; @@ -213,8 +213,8 @@ public: * } */ class ConstGen final - : public CanonicalNodeDef<CanonicalOpcode::ConstGen, FixedArity<0>::Mixin, - With<NodeTrait::DataType>::Mixin, With<NodeTrait::TensorShape>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::ConstGen, FixedArity<0>::Mixin, + With<NodeTrait::DataType>::Mixin, With<NodeTrait::TensorShape>::Mixin> { public: ConstGen() = default; @@ -376,7 +376,7 @@ private: * @brief Create a feature map from a tensor */ class FeatureEncode final - : public CanonicalNodeDef<CanonicalOpcode::FeatureEncode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::FeatureEncode, FixedArity<1>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -395,7 +395,7 @@ private: * @brief Create a tensor from a feature map */ class FeatureDecode final - : public CanonicalNodeDef<CanonicalOpcode::FeatureDecode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::FeatureDecode, FixedArity<1>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -414,7 +414,7 @@ private: * @brief Create a filter from a tensor */ class FilterEncode final - : public CanonicalNodeDef<CanonicalOpcode::FilterEncode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::FilterEncode, FixedArity<1>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -433,7 +433,7 @@ private: * @brief Create a tensor from a filter */ class FilterDecode final - : public CanonicalNodeDef<CanonicalOpcode::FilterDecode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::FilterDecode, FixedArity<1>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -452,7 +452,7 @@ private: * @brief Create a depthwise filter from a tensor */ class DepthwiseFilterEncode final - : public CanonicalNodeDef<CanonicalOpcode::DepthwiseFilterEncode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::DepthwiseFilterEncode, FixedArity<1>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -471,7 +471,7 @@ private: * @brief Create a tensor from a depthwise filter */ class DepthwiseFilterDecode final - : public CanonicalNodeDef<CanonicalOpcode::DepthwiseFilterDecode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::DepthwiseFilterDecode, FixedArity<1>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -512,8 +512,8 @@ template <ReshapeType RT> class Reshape; */ template <> class Reshape<ReshapeType::Fixed> final - : public CanonicalNodeDef<CanonicalOpcode::FixedReshape, FixedArity<1>::Mixin, - With<NodeTrait::TensorShape>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::FixedReshape, FixedArity<1>::Mixin, + With<NodeTrait::TensorShape>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -529,7 +529,7 @@ using FixedReshape = Reshape<ReshapeType::Fixed>; * concatenated along the given axis. */ class TensorConcat final - : public CanonicalNodeDef<CanonicalOpcode::TensorConcat, FixedArity<2>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TensorConcat, FixedArity<2>::Mixin> { public: Node *lhs(void) const { return at(0)->node(); } @@ -578,7 +578,7 @@ private: * @brief Depthwise 2D Convolution */ class DepthwiseConv2D final - : public CanonicalNodeDef<CanonicalOpcode::DepthwiseConv2D, FixedArity<2>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::DepthwiseConv2D, FixedArity<2>::Mixin> { public: Node *ifm(void) const { return at(0)->node(); } @@ -616,7 +616,7 @@ enum class ReduceFunc * @note All the reduce functions always keep dimensions */ class TensorReduce final - : public CanonicalNodeDef<CanonicalOpcode::TensorReduce, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TensorReduce, FixedArity<1>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -684,7 +684,7 @@ private: * With this, output shape is uniquely determined by all inputs and attributes. */ class TransposedConv2D final - : public CanonicalNodeDef<CanonicalOpcode::TransposedConv2D, FixedArity<2>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TransposedConv2D, FixedArity<2>::Mixin> { public: Node *ifm(void) const { return at(0)->node(); } @@ -714,11 +714,11 @@ private: template <Domain D> class Softmax; /** -* @brief Computes softmax activations for Tensor domain -*/ + * @brief Computes softmax activations for Tensor domain + */ template <> class Softmax<Domain::Tensor> final - : public CanonicalNodeDef<CanonicalOpcode::TensorSoftmax, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TensorSoftmax, FixedArity<1>::Mixin> { public: Softmax() = default; @@ -777,7 +777,7 @@ template <Domain D> class BiasAdd; */ template <> class BiasAdd<Domain::Tensor> final - : public CanonicalNodeDef<CanonicalOpcode::TensorBiasAdd, FixedArity<2>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TensorBiasAdd, FixedArity<2>::Mixin> { public: BiasAdd() = default; @@ -813,7 +813,7 @@ using TensorBiasAdd = BiasAdd<Domain::Tensor>; */ template <> class BiasAdd<Domain::Feature> final - : public CanonicalNodeDef<CanonicalOpcode::FeatureBiasAdd, FixedArity<2>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::FeatureBiasAdd, FixedArity<2>::Mixin> { public: BiasAdd() = default; @@ -848,7 +848,7 @@ using FeatureBiasAdd = BiasAdd<Domain::Feature>; * [padding.front(0) + 1 + padding.back(0), padding.front(1) + 2 + padding.back(1)] = [4,9]. */ class TensorConstantPad final - : public CanonicalNodeDef<CanonicalOpcode::TensorConstantPad, FixedArity<2>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TensorConstantPad, FixedArity<2>::Mixin> { public: Node *input(void) const { return at(0)->node(); } @@ -951,7 +951,7 @@ public: * @brief Elementwise Sqrt of input */ class EltwiseSqrt final - : public CanonicalNodeDef<CanonicalOpcode::EltwiseSqrt, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::EltwiseSqrt, FixedArity<1>::Mixin> { public: EltwiseSqrt() = default; @@ -976,7 +976,7 @@ public: * TODO Explain the operation semantics */ class TensorBroadcast final - : public CanonicalNodeDef<CanonicalOpcode::TensorBroadcast, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TensorBroadcast, FixedArity<1>::Mixin> { public: TensorBroadcast() = default; @@ -1014,7 +1014,7 @@ private: * MatrixEncode currently requires a rank-2 Tensor as its input. */ class MatrixEncode final - : public CanonicalNodeDef<CanonicalOpcode::MatrixEncode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::MatrixEncode, FixedArity<1>::Mixin> { public: MatrixEncode() = default; @@ -1038,7 +1038,7 @@ private: * MatrixDecode currently requires a Matrix as its input. */ class MatrixDecode final - : public CanonicalNodeDef<CanonicalOpcode::MatrixDecode, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::MatrixDecode, FixedArity<1>::Mixin> { public: MatrixDecode() = default; @@ -1086,7 +1086,7 @@ public: * Input and output belong to tensor domain. */ class TensorTranspose final - : public CanonicalNodeDef<CanonicalOpcode::TensorTranspose, FixedArity<1>::Mixin> + : public CanonicalNodeDef<CanonicalOpcode::TensorTranspose, FixedArity<1>::Mixin> { public: TensorTranspose() = default; diff --git a/compiler/loco/include/loco/IR/Padding2D.h b/compiler/loco/include/loco/IR/Padding2D.h index 30557a891..b50a8045f 100644 --- a/compiler/loco/include/loco/IR/Padding2D.h +++ b/compiler/loco/include/loco/IR/Padding2D.h @@ -32,7 +32,7 @@ public: public: Padding2D(uint32_t top, uint32_t bottom, uint32_t left, uint32_t right) - : _top{top}, _bottom{bottom}, _left{left}, _right{right} + : _top{top}, _bottom{bottom}, _left{left}, _right{right} { // DO NOTHING } diff --git a/compiler/loco/requires.cmake b/compiler/loco/requires.cmake new file mode 100644 index 000000000..654db88c3 --- /dev/null +++ b/compiler/loco/requires.cmake @@ -0,0 +1 @@ +require("angkor") diff --git a/compiler/loco/src/ADT/AnnotatedItem.test.cpp b/compiler/loco/src/ADT/AnnotatedItem.test.cpp index 45ca87d75..87e597f5c 100644 --- a/compiler/loco/src/ADT/AnnotatedItem.test.cpp +++ b/compiler/loco/src/ADT/AnnotatedItem.test.cpp @@ -17,7 +17,8 @@ #include "loco/ADT/AnnotatedItem.h" #include <gtest/gtest.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -31,7 +32,7 @@ template <int N> struct DerivedAnnotation final : public Annotation { static std::unique_ptr<DerivedAnnotation<N>> make(void) { - return stdex::make_unique<DerivedAnnotation<N>>(); + return std::make_unique<DerivedAnnotation<N>>(); } }; diff --git a/compiler/loco/src/IR/CanonicalDialect.cpp b/compiler/loco/src/IR/CanonicalDialect.cpp index ea956b80e..9438956f8 100644 --- a/compiler/loco/src/IR/CanonicalDialect.cpp +++ b/compiler/loco/src/IR/CanonicalDialect.cpp @@ -18,8 +18,7 @@ #include "loco/IR/Graph.h" #include "loco/IR/Nodes.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> #include <stdexcept> @@ -55,7 +54,7 @@ namespace loco CanonicalDialect::CanonicalDialect() { - service<GraphOutputIndexQueryService>(stdex::make_unique<GraphOutputIndexQueryServiceImpl>()); + service<GraphOutputIndexQueryService>(std::make_unique<GraphOutputIndexQueryServiceImpl>()); } Dialect *CanonicalDialect::get(void) diff --git a/compiler/loco/src/IR/Dialect.test.cpp b/compiler/loco/src/IR/Dialect.test.cpp index 3af303375..447f443f2 100644 --- a/compiler/loco/src/IR/Dialect.test.cpp +++ b/compiler/loco/src/IR/Dialect.test.cpp @@ -16,7 +16,7 @@ #include "loco/IR/Dialect.h" -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> @@ -31,7 +31,7 @@ TEST(DialectTest, service) struct MockDialect final : public loco::Dialect { - MockDialect() { service<S1>(stdex::make_unique<S1>()); } + MockDialect() { service<S1>(std::make_unique<S1>()); } }; MockDialect dialect; diff --git a/compiler/loco/src/IR/Graph.cpp b/compiler/loco/src/IR/Graph.cpp index 8073d4545..98b22c3b6 100644 --- a/compiler/loco/src/IR/Graph.cpp +++ b/compiler/loco/src/IR/Graph.cpp @@ -16,8 +16,7 @@ #include "loco/IR/Graph.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace @@ -25,7 +24,7 @@ namespace std::unique_ptr<loco::TensorShape> make_tensor_shape(std::initializer_list<loco::Dimension> dims) { - auto tensor_shape = stdex::make_unique<loco::TensorShape>(); + auto tensor_shape = std::make_unique<loco::TensorShape>(); tensor_shape->rank(dims.size()); { @@ -50,14 +49,11 @@ void Mixin<Trait::TensorShaped>::shape(std::initializer_list<Dimension> dims) shape(make_tensor_shape(dims)); } -GraphInput *Graph::InputContext::create(void) -{ - return take(stdex::make_unique<GraphInput>(size())); -} +GraphInput *Graph::InputContext::create(void) { return take(std::make_unique<GraphInput>(size())); } GraphOutput *Graph::OutputContext::create(void) { - return take(stdex::make_unique<GraphOutput>(size())); + return take(std::make_unique<GraphOutput>(size())); } std::set<loco::Node *> all_nodes(loco::Graph *g) diff --git a/compiler/loco/src/IR/Graph.test.cpp b/compiler/loco/src/IR/Graph.test.cpp index ad6894f30..837d29326 100644 --- a/compiler/loco/src/IR/Graph.test.cpp +++ b/compiler/loco/src/IR/Graph.test.cpp @@ -108,7 +108,7 @@ namespace { // temp node with multple params for ctor. loco::CanonicalOpcode::ReLU is used for simplicity class ParamCtorNode - : public loco::CanonicalNodeDef<loco::CanonicalOpcode::ReLU, loco::FixedArity<0>::Mixin> + : public loco::CanonicalNodeDef<loco::CanonicalOpcode::ReLU, loco::FixedArity<0>::Mixin> { public: ParamCtorNode(int i, float f) diff --git a/compiler/loco/src/IR/PermutingCodec.cpp b/compiler/loco/src/IR/PermutingCodec.cpp index 2857e5e28..e9fd1fb12 100644 --- a/compiler/loco/src/IR/PermutingCodec.cpp +++ b/compiler/loco/src/IR/PermutingCodec.cpp @@ -16,8 +16,7 @@ #include "loco/IR/PermutingCodec.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> #include <set> #include <stdexcept> @@ -139,7 +138,7 @@ TensorIndex PermutingEncoder<Domain::Feature>::value(const FeatureIndex &in) con std::unique_ptr<FeatureEncoder> PermutingEncoder<Domain::Feature>::clone(void) const { - return stdex::make_unique<PermutingEncoder<Domain::Feature>>(_perm); + return std::make_unique<PermutingEncoder<Domain::Feature>>(_perm); } bool PermutingEncoder<Domain::Feature>::valid(void) const { return ::valid(_perm); } @@ -179,7 +178,7 @@ FeatureIndex PermutingDecoder<Domain::Feature>::value(const TensorIndex &in) con std::unique_ptr<FeatureDecoder> PermutingDecoder<Domain::Feature>::clone(void) const { - return stdex::make_unique<PermutingDecoder<Domain::Feature>>(_perm); + return std::make_unique<PermutingDecoder<Domain::Feature>>(_perm); } bool PermutingDecoder<Domain::Feature>::valid(void) const { return ::valid(_perm); } diff --git a/compiler/loco/src/IR/Verifier.test.cpp b/compiler/loco/src/IR/Verifier.test.cpp index 8c40a5058..8a92a35f0 100644 --- a/compiler/loco/src/IR/Verifier.test.cpp +++ b/compiler/loco/src/IR/Verifier.test.cpp @@ -18,10 +18,10 @@ #include <gtest/gtest.h> -#include <stdex/Memory.h> +#include <memory> #include <vector> -using stdex::make_unique; +using std::make_unique; TEST(VerifierTest, valid_minimal) { diff --git a/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp b/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp index 6d5adc525..a0f0e892a 100644 --- a/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp +++ b/compiler/loco/src/Service/CanonicalShapeInferenceRule.cpp @@ -674,7 +674,7 @@ public: for (uint32_t axis = 0; axis < out_shape.rank(); ++axis) { out_shape.dim(axis) = - tensor_shape.dim(axis).value() + padding->front(axis) + padding->back(axis); + tensor_shape.dim(axis).value() + padding->front(axis) + padding->back(axis); } return loco::NodeShape{out_shape}; diff --git a/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp b/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp index e88872b5d..0e0dec1a5 100644 --- a/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp +++ b/compiler/loco/src/Service/CanonicalShapeInferenceRule.test.cpp @@ -122,7 +122,7 @@ TEST(CanonicalShapeInferenceRuleTest, avgpool2d) testcase.pull_node->shape({1, 8, 4, 3}); - testcase.encode_node->encoder(stdex::make_unique<PermutingEncoder<Domain::Feature>>(perm)); + testcase.encode_node->encoder(std::make_unique<PermutingEncoder<Domain::Feature>>(perm)); testcase.avgpool2d_node->window()->vertical(2); testcase.avgpool2d_node->window()->horizontal(2); @@ -130,7 +130,7 @@ TEST(CanonicalShapeInferenceRuleTest, avgpool2d) testcase.avgpool2d_node->stride()->vertical(2); testcase.avgpool2d_node->stride()->horizontal(2); - testcase.decode_node->decoder(stdex::make_unique<PermutingDecoder<Domain::Feature>>(perm)); + testcase.decode_node->decoder(std::make_unique<PermutingDecoder<Domain::Feature>>(perm)); // Run Inference loco::CanonicalShapeInferenceRule rule; @@ -224,7 +224,7 @@ TEST(CanonicalShapeInferenceRuleTest, maxpool2d) testcase.pull_node->shape({1, 8, 4, 3}); - testcase.encode_node->encoder(stdex::make_unique<PermutingEncoder<Domain::Feature>>(perm)); + testcase.encode_node->encoder(std::make_unique<PermutingEncoder<Domain::Feature>>(perm)); testcase.maxpool2d_node->window()->vertical(2); testcase.maxpool2d_node->window()->horizontal(2); @@ -232,7 +232,7 @@ TEST(CanonicalShapeInferenceRuleTest, maxpool2d) testcase.maxpool2d_node->stride()->vertical(2); testcase.maxpool2d_node->stride()->horizontal(2); - testcase.decode_node->decoder(stdex::make_unique<PermutingDecoder<Domain::Feature>>(perm)); + testcase.decode_node->decoder(std::make_unique<PermutingDecoder<Domain::Feature>>(perm)); // Run Inference loco::CanonicalShapeInferenceRule rule; diff --git a/compiler/loco/src/Service/GraphBuilder.h b/compiler/loco/src/Service/GraphBuilder.h index 71084673c..74eed2af8 100644 --- a/compiler/loco/src/Service/GraphBuilder.h +++ b/compiler/loco/src/Service/GraphBuilder.h @@ -20,10 +20,8 @@ // loco-internal headers #include "loco/IR/Graph.h" -// repo-internal headers -#include <stdex/Memory.h> - // C++ standard headers +#include <memory> #include <stack> // @@ -90,7 +88,7 @@ public: // "Layer" is in theory a subgraph builder. template <typename Layer, typename... Args> auto push(Args &&... args) - -> decltype(static_cast<Layer *>(nullptr)->operator()(static_cast<Context *>(nullptr))) + -> decltype(static_cast<Layer *>(nullptr)->operator()(static_cast<Context *>(nullptr))) { Layer layer{std::forward<Args>(args)...}; return layer(ctx()); @@ -108,7 +106,7 @@ private: static inline std::unique_ptr<GraphBuilder> make_graph_builder(loco::Graph *g) { - return stdex::make_unique<GraphBuilder>(g); + return std::make_unique<GraphBuilder>(g); } // "InputLayer" creates both GraphInput and Pull node at once @@ -159,7 +157,7 @@ struct InputLayer final ctx->stack()->push(pull_node); - return stdex::make_unique<Return>(graph_input, pull_node); + return std::make_unique<Return>(graph_input, pull_node); } }; @@ -205,7 +203,7 @@ struct OutputLayer final ctx->stack()->push(push_node); - return stdex::make_unique<Return>(graph_output, push_node); + return std::make_unique<Return>(graph_output, push_node); } }; @@ -236,7 +234,7 @@ struct ReLULayer final ctx->stack()->push(relu_node); - return stdex::make_unique<Return>(relu_node); + return std::make_unique<Return>(relu_node); } }; @@ -263,7 +261,7 @@ struct ConstGenLayer final ctx->stack()->push(const_node); - return stdex::make_unique<Return>(const_node); + return std::make_unique<Return>(const_node); } }; @@ -283,7 +281,7 @@ struct FeatureEncodeLayer final Return *perm(const loco::Permutation<loco::Domain::Feature> &perm) { using namespace loco; - _node->encoder(stdex::make_unique<PermutingEncoder<Domain::Feature>>(perm)); + _node->encoder(std::make_unique<PermutingEncoder<Domain::Feature>>(perm)); return this; } @@ -302,7 +300,7 @@ struct FeatureEncodeLayer final ctx->stack()->push(encode_node); - return stdex::make_unique<Return>(encode_node); + return std::make_unique<Return>(encode_node); } }; @@ -320,7 +318,7 @@ struct FeatureDecodeLayer final Return *perm(const loco::Permutation<loco::Domain::Feature> &perm) { using namespace loco; - _node->decoder(stdex::make_unique<PermutingDecoder<Domain::Feature>>(perm)); + _node->decoder(std::make_unique<PermutingDecoder<Domain::Feature>>(perm)); return this; } @@ -341,7 +339,7 @@ struct FeatureDecodeLayer final ctx->stack()->push(decode_node); - return stdex::make_unique<Return>(decode_node); + return std::make_unique<Return>(decode_node); } }; @@ -358,7 +356,7 @@ struct FilterEncodeLayer final public: Return *perm(const loco::Permutation<loco::Domain::Filter> &perm) { - auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); encoder->perm(perm); _node->encoder(std::move(encoder)); return this; @@ -379,7 +377,7 @@ struct FilterEncodeLayer final ctx->stack()->push(encode_node); - return stdex::make_unique<Return>(encode_node); + return std::make_unique<Return>(encode_node); } }; @@ -397,7 +395,7 @@ struct DepthwiseFilterEncodeLayer final Return *perm(const loco::Permutation<loco::Domain::DepthwiseFilter> &perm) { using namespace loco; - _node->encoder(stdex::make_unique<PermutingEncoder<Domain::DepthwiseFilter>>(perm)); + _node->encoder(std::make_unique<PermutingEncoder<Domain::DepthwiseFilter>>(perm)); return this; } @@ -416,7 +414,7 @@ struct DepthwiseFilterEncodeLayer final ctx->stack()->push(encode_node); - return stdex::make_unique<Return>(encode_node); + return std::make_unique<Return>(encode_node); } }; @@ -446,7 +444,7 @@ struct DepthwiseConv2DLayer final ctx->stack()->push(depthwiseconv2d_node); - return stdex::make_unique<Return>(depthwiseconv2d_node); + return std::make_unique<Return>(depthwiseconv2d_node); } }; @@ -476,7 +474,7 @@ struct TransposedConv2DLayer final ctx->stack()->push(tr_conv2d_node); - return stdex::make_unique<Return>(tr_conv2d_node); + return std::make_unique<Return>(tr_conv2d_node); } }; @@ -512,7 +510,7 @@ struct FixedReshapeLayer final ctx->stack()->push(reshape_node); - return stdex::make_unique<Return>(reshape_node); + return std::make_unique<Return>(reshape_node); } }; @@ -540,7 +538,7 @@ struct TensorBroadcastLayer final broadcast_node->input(ctx->stack()->pop()); ctx->stack()->push(broadcast_node); - return stdex::make_unique<Return>(broadcast_node); + return std::make_unique<Return>(broadcast_node); } }; diff --git a/compiler/loco/src/Service/GraphTestcase.h b/compiler/loco/src/Service/GraphTestcase.h index 27b011f8d..06801e0aa 100644 --- a/compiler/loco/src/Service/GraphTestcase.h +++ b/compiler/loco/src/Service/GraphTestcase.h @@ -22,8 +22,6 @@ #include "GraphBuilder.h" -#include <stdex/Memory.h> - enum class GraphCode { Identity, @@ -278,7 +276,7 @@ public: const_node = graph_builder->push<ConstGenLayer>()->node(); filter_encode_node = - graph_builder->push<DepthwiseFilterEncodeLayer>()->perm(filter_perm)->node(); + graph_builder->push<DepthwiseFilterEncodeLayer>()->perm(filter_perm)->node(); depthwiseconv2d_node = graph_builder->push<DepthwiseConv2DLayer>()->node(); diff --git a/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp index 3d5a11ae4..7be41f7ee 100644 --- a/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp +++ b/compiler/loco/src/Service/MultiDialectShapeInferenceRule.test.cpp @@ -112,8 +112,8 @@ TEST(MultiDialectShapeInferenceRuleTest, test1) loco::MultiDialectShapeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(TestDialect<2, 3>::get(), &t23_rule) - .bind(TestDialect<4, 5>::get(), &t45_rule); + .bind(TestDialect<2, 3>::get(), &t23_rule) + .bind(TestDialect<4, 5>::get(), &t45_rule); loco::apply(&rules).to(g.get()); diff --git a/compiler/loco/src/Service/ShapeInference.cpp b/compiler/loco/src/Service/ShapeInference.cpp index 84eb10963..d177a4869 100644 --- a/compiler/loco/src/Service/ShapeInference.cpp +++ b/compiler/loco/src/Service/ShapeInference.cpp @@ -18,8 +18,7 @@ #include "loco/IR/Algorithm.h" #include <cassert> - -#include <stdex/Memory.h> +#include <memory> namespace { @@ -82,7 +81,7 @@ bool ShapeInferenceSession::to(Graph *g) const { if (_rule->infer(node, shape)) { - node->annot(stdex::make_unique<ShapeAnnotation>(shape)); + node->annot(std::make_unique<ShapeAnnotation>(shape)); changed = true; } } diff --git a/compiler/loco/src/Service/TypeInference.cpp b/compiler/loco/src/Service/TypeInference.cpp index 27d7d9a29..df038efe7 100644 --- a/compiler/loco/src/Service/TypeInference.cpp +++ b/compiler/loco/src/Service/TypeInference.cpp @@ -19,8 +19,7 @@ #include "loco/IR/Algorithm.h" #include <cassert> - -#include <stdex/Memory.h> +#include <memory> namespace { @@ -73,7 +72,7 @@ bool TypeInferenceSession::to(Graph *g) const { if (_rule->infer(node, dtype)) { - node->annot(stdex::make_unique<DataTypeAnnotation>(dtype)); + node->annot(std::make_unique<DataTypeAnnotation>(dtype)); changed = true; } } diff --git a/compiler/loco/src/Service/TypeInference.test.cpp b/compiler/loco/src/Service/TypeInference.test.cpp index 13bcfa52b..0d2cc8864 100644 --- a/compiler/loco/src/Service/TypeInference.test.cpp +++ b/compiler/loco/src/Service/TypeInference.test.cpp @@ -268,8 +268,8 @@ TEST(MultiDialectTypeInferenceRuleTest, test1) loco::MultiDialectTypeInferenceRule rules; rules.bind(TestDialect<loco::DataType::S8>::get(), &s8_rule) - .bind(TestDialect<loco::DataType::U8>::get(), &u8_rule) - .bind(loco::CanonicalDialect::get(), &canon_rule); + .bind(TestDialect<loco::DataType::U8>::get(), &u8_rule) + .bind(loco::CanonicalDialect::get(), &canon_rule); loco::apply(&rules).to(g.get()); diff --git a/compiler/loco/src/tensorflow.test.cpp b/compiler/loco/src/tensorflow.test.cpp index f534aee7b..d905429f5 100644 --- a/compiler/loco/src/tensorflow.test.cpp +++ b/compiler/loco/src/tensorflow.test.cpp @@ -23,9 +23,9 @@ #include <gtest/gtest.h> -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; namespace { @@ -65,7 +65,7 @@ loco::Permutation<loco::Domain::Filter> make_HWIO_permutation(void) return HWIO; } -} // nemaspace +} // namespace #if 0 >>> MaxPool_Float_000 testcase diff --git a/compiler/locoex-customop/CMakeLists.txt b/compiler/locoex-customop/CMakeLists.txt index df1e01526..12356c81b 100644 --- a/compiler/locoex-customop/CMakeLists.txt +++ b/compiler/locoex-customop/CMakeLists.txt @@ -5,7 +5,7 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_library(locoex_customop SHARED ${SOURCES}) target_include_directories(locoex_customop PUBLIC include) target_link_libraries(locoex_customop PUBLIC loco) -target_link_libraries(locoex_customop PRIVATE stdex locop pepper_str) +target_link_libraries(locoex_customop PRIVATE locop pepper_str) install(TARGETS locoex_customop DESTINATION lib) if(NOT ENABLE_TEST) @@ -15,4 +15,4 @@ endif(NOT ENABLE_TEST) nnas_find_package(GTest REQUIRED) GTest_AddTest(locoex_customop_test ${TESTS}) -target_link_libraries(locoex_customop_test loco locoex_customop stdex) +target_link_libraries(locoex_customop_test loco locoex_customop) diff --git a/compiler/locoex-customop/requires.cmake b/compiler/locoex-customop/requires.cmake index 9127144f2..c4240bc09 100644 --- a/compiler/locoex-customop/requires.cmake +++ b/compiler/locoex-customop/requires.cmake @@ -1,4 +1,3 @@ require("loco") -require("stdex") require("locop") require("pepper-str") diff --git a/compiler/locoex-customop/src/COpCall.cpp b/compiler/locoex-customop/src/COpCall.cpp index 029914758..e86ad5c5b 100644 --- a/compiler/locoex-customop/src/COpCall.cpp +++ b/compiler/locoex-customop/src/COpCall.cpp @@ -57,7 +57,7 @@ std::vector<std::string> COpCall::attr_names() const #define INSTANTIATE(AT) \ template const typename AttrTypeTrait<AT>::Type *COpCall::attr<AT>(const std::string &attr_name) \ - const; + const; INSTANTIATE(COpAttrType::Float) INSTANTIATE(COpAttrType::Int) diff --git a/compiler/locoex-customop/src/COpCall.test.cpp b/compiler/locoex-customop/src/COpCall.test.cpp index d5f01d22d..7bc4186e5 100644 --- a/compiler/locoex-customop/src/COpCall.test.cpp +++ b/compiler/locoex-customop/src/COpCall.test.cpp @@ -20,7 +20,7 @@ #include <loco/IR/Graph.h> #include <loco/IR/Nodes.h> -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> @@ -51,8 +51,8 @@ TEST(CallTest, Test_01) custom->input(0, inp); custom->input(1, inp); - custom->attr(int_attr, stdex::make_unique<COpAttrInt>(int_val)); - custom->attr(float_attr, stdex::make_unique<COpAttrFloat>(float_val)); + custom->attr(int_attr, std::make_unique<COpAttrInt>(int_val)); + custom->attr(float_attr, std::make_unique<COpAttrFloat>(float_val)); } // access custom op input diff --git a/compiler/locoex-customop/src/VariadicArityNode.test.cpp b/compiler/locoex-customop/src/VariadicArityNode.test.cpp index a618824e5..86a9de5cd 100644 --- a/compiler/locoex-customop/src/VariadicArityNode.test.cpp +++ b/compiler/locoex-customop/src/VariadicArityNode.test.cpp @@ -47,7 +47,7 @@ class BinaryInputNode : public TestNode public: BinaryInputNode() : TestNode(2) {} }; -} +} // namespace TEST(CustomOpTest, VariadicArityNode_arity_0) { diff --git a/compiler/locomotiv/CMakeLists.txt b/compiler/locomotiv/CMakeLists.txt index 5c0156b78..308f48619 100644 --- a/compiler/locomotiv/CMakeLists.txt +++ b/compiler/locomotiv/CMakeLists.txt @@ -8,7 +8,6 @@ target_include_directories(locomotiv PUBLIC include) target_include_directories(locomotiv PRIVATE src) target_link_libraries(locomotiv PUBLIC loco) target_link_libraries(locomotiv PUBLIC angkor) -target_link_libraries(locomotiv PRIVATE stdex) # Let's apply nncc common compile options # # NOTE This will enable strict compilation (warnings as error). diff --git a/compiler/locomotiv/include/locomotiv/Session.h b/compiler/locomotiv/include/locomotiv/Session.h index 3268d60b3..85c26c09c 100644 --- a/compiler/locomotiv/include/locomotiv/Session.h +++ b/compiler/locomotiv/include/locomotiv/Session.h @@ -51,7 +51,7 @@ public: * @warn This approach may fail in case of graph with control flow */ Session(loco::Graph *g, const std::vector<loco::Node *> &custom_outputs) - : _graph(g), _outputs(custom_outputs) + : _graph(g), _outputs(custom_outputs) { // DO NOTHING } diff --git a/compiler/locomotiv/requires.cmake b/compiler/locomotiv/requires.cmake index 1c09aa13d..654db88c3 100644 --- a/compiler/locomotiv/requires.cmake +++ b/compiler/locomotiv/requires.cmake @@ -1,2 +1 @@ require("angkor") -require("stdex") diff --git a/compiler/locomotiv/src/Node/AvgPool2D.cpp b/compiler/locomotiv/src/Node/AvgPool2D.cpp index 5fdf1e725..0adabd49a 100644 --- a/compiler/locomotiv/src/Node/AvgPool2D.cpp +++ b/compiler/locomotiv/src/Node/AvgPool2D.cpp @@ -78,9 +78,9 @@ nncc::core::ADT::tensor::Buffer<T> avgPool2D(const loco::AvgPool2D *avgpool2d, const uint32_t pad_right = avgpool2d->pad()->right(); const uint32_t output_height = - compute_out_size(ifm_height, pad_top + pad_bottom, window_height, stride_height); + compute_out_size(ifm_height, pad_top + pad_bottom, window_height, stride_height); const uint32_t output_width = - compute_out_size(ifm_width, pad_left + pad_right, window_width, stride_width); + compute_out_size(ifm_width, pad_left + pad_right, window_width, stride_width); // prepare output buffer Shape output_shape{batches, output_height, output_width, depth}; diff --git a/compiler/locomotiv/src/Node/AvgPool2D.test.cpp b/compiler/locomotiv/src/Node/AvgPool2D.test.cpp index f9863b47d..ec5f3cd82 100644 --- a/compiler/locomotiv/src/Node/AvgPool2D.test.cpp +++ b/compiler/locomotiv/src/Node/AvgPool2D.test.cpp @@ -84,7 +84,7 @@ void run_test(const float *ifm, const float *expected_ofm, const Shape &ifm_shap ASSERT_TRUE(*(avgpool2d_data->shape()) == ofm_shape); auto ofm_overlay = - make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); + make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) { const auto &ind = e.current(); diff --git a/compiler/locomotiv/src/Node/BiasAdd.cpp b/compiler/locomotiv/src/Node/BiasAdd.cpp index b84fa7e3c..0c45cc12f 100644 --- a/compiler/locomotiv/src/Node/BiasAdd.cpp +++ b/compiler/locomotiv/src/Node/BiasAdd.cpp @@ -55,7 +55,7 @@ void execute_node(loco::BiasAdd<loco::Domain::Tensor> *bias_add) validate(input_data && bias_data, "Input not ready"); validate(locomotiv::annot_domain(bias_add->value()) == loco::Domain::Tensor && - locomotiv::annot_domain(bias_add->bias()) == loco::Domain::Bias, + locomotiv::annot_domain(bias_add->bias()) == loco::Domain::Bias, "Wrong input domain"); std::unique_ptr<NodeData> bias_add_data = calc(input_data, bias_data, bias_add->axis()); @@ -74,7 +74,7 @@ void execute_node(loco::BiasAdd<loco::Domain::Feature> *bias_add) validate(input_data && bias_data, "Input not ready"); validate(locomotiv::annot_domain(bias_add->value()) == loco::Domain::Feature && - locomotiv::annot_domain(bias_add->bias()) == loco::Domain::Bias, + locomotiv::annot_domain(bias_add->bias()) == loco::Domain::Bias, "Wrong input domain"); std::unique_ptr<NodeData> bias_add_data = calc(input_data, bias_data, 3); diff --git a/compiler/locomotiv/src/Node/Conv2D.cpp b/compiler/locomotiv/src/Node/Conv2D.cpp index cdf0dfd56..2f9ca5a7e 100644 --- a/compiler/locomotiv/src/Node/Conv2D.cpp +++ b/compiler/locomotiv/src/Node/Conv2D.cpp @@ -82,9 +82,9 @@ Buffer<RET_T> calc_conv2D(const loco::Conv2D *conv2d, const Buffer<IFM_T> *input const uint32_t pad_right = conv2d->pad()->right(); const uint32_t output_height = - compute_out_size(input_height + pad_top + pad_bottom, filter_height, stride_height); + compute_out_size(input_height + pad_top + pad_bottom, filter_height, stride_height); const uint32_t output_width = - compute_out_size(input_width + pad_left + pad_right, filter_width, stride_width); + compute_out_size(input_width + pad_left + pad_right, filter_width, stride_width); const uint32_t batches = input_shape.dim(0); const uint32_t input_depth = input_shape.dim(3); @@ -121,9 +121,9 @@ Buffer<RET_T> calc_conv2D(const loco::Conv2D *conv2d, const Buffer<IFM_T> *input ((unsigned)in_y < input_height)) { auto input_value = - input_buf->at(Index({batch, (unsigned)in_y, (unsigned)in_x, in_channel})); + input_buf->at(Index({batch, (unsigned)in_y, (unsigned)in_x, in_channel})); auto filter_value = - filter_buf->at(Index({out_channel, filter_y, filter_x, in_channel})); + filter_buf->at(Index({out_channel, filter_y, filter_x, in_channel})); total += (input_value * filter_value); } } diff --git a/compiler/locomotiv/src/Node/Conv2D.test.cpp b/compiler/locomotiv/src/Node/Conv2D.test.cpp index 66e947acc..93afa79b7 100644 --- a/compiler/locomotiv/src/Node/Conv2D.test.cpp +++ b/compiler/locomotiv/src/Node/Conv2D.test.cpp @@ -97,7 +97,7 @@ void run_test(const float *ifm, const float *ker, const float *expected_ofm, con ASSERT_TRUE(*(conv2d_result->shape()) == ofm_shape); auto ofm_overlay = - make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); + make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) { const auto &ind = e.current(); diff --git a/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp b/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp index f39cd177e..a1a8e506f 100644 --- a/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp +++ b/compiler/locomotiv/src/Node/DepthwiseConv2D.cpp @@ -89,9 +89,9 @@ Buffer<RET_T> calc_dw_conv2d(const loco::DepthwiseConv2D *dw_conv2d, const Buffe const uint32_t pad_right = dw_conv2d->pad()->right(); const uint32_t ofm_height = - compute_out_size(ifm_height, pad_top + pad_bottom, ker_height, stride_height); + compute_out_size(ifm_height, pad_top + pad_bottom, ker_height, stride_height); const uint32_t ofm_width = - compute_out_size(ifm_width, pad_left + pad_right, ker_width, stride_width); + compute_out_size(ifm_width, pad_left + pad_right, ker_width, stride_width); const uint32_t batches = ifm_shape.dim(0); const uint32_t ifm_depth = ifm_shape.dim(3); diff --git a/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp b/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp index 1ff333be0..8a435b6ab 100644 --- a/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp +++ b/compiler/locomotiv/src/Node/DepthwiseConv2D.test.cpp @@ -97,7 +97,7 @@ void run_test(const float *ifm, const float *ker, const float *expected_ofm, con ASSERT_TRUE(*(dw_conv2d_result->shape()) == ofm_shape); auto ofm_overlay = - make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); + make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) { const auto &ind = e.current(); diff --git a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp index 03f5bf833..e161287ea 100644 --- a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp +++ b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.cpp @@ -59,8 +59,8 @@ std::unique_ptr<locomotiv::NodeData> dw_filter_encode(const loco::DepthwiseFilte // Make HWCM (i.e. height, width, depth, multiplier) buffer from DepthwiseFilterShape Buffer<T> node_buf = make_buffer<T, LexicalLayout>( - Shape{node_shape.height().value(), node_shape.width().value(), node_shape.depth().value(), - node_shape.multiplier().value()}); + Shape{node_shape.height().value(), node_shape.width().value(), node_shape.depth().value(), + node_shape.multiplier().value()}); // Copy buffer in an order arranged by encoder for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) diff --git a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp index 5b2ec9326..44364723c 100644 --- a/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp +++ b/compiler/locomotiv/src/Node/DepthwiseFilterEncode.test.cpp @@ -62,7 +62,7 @@ TEST(NodeExecution_DepthwiseFilterEncode, f32) // Encoder to correctly read input tensor as MHWC auto encoder = std::unique_ptr<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>( - new loco::PermutingEncoder<loco::Domain::DepthwiseFilter>); + new loco::PermutingEncoder<loco::Domain::DepthwiseFilter>); encoder->perm()->axis(loco::DepthwiseFilterAxis::Multiplier) = 0; encoder->perm()->axis(loco::DepthwiseFilterAxis::Height) = 1; encoder->perm()->axis(loco::DepthwiseFilterAxis::Width) = 2; diff --git a/compiler/locomotiv/src/Node/FeatureCodec.test.cpp b/compiler/locomotiv/src/Node/FeatureCodec.test.cpp index 1b6b06c13..dacd0170c 100644 --- a/compiler/locomotiv/src/Node/FeatureCodec.test.cpp +++ b/compiler/locomotiv/src/Node/FeatureCodec.test.cpp @@ -64,7 +64,7 @@ protected: const loco::Permutation<loco::Domain::Feature> &perm) { auto encoder = std::unique_ptr<loco::PermutingEncoder<loco::Domain::Feature>>( - new loco::PermutingEncoder<loco::Domain::Feature>); + new loco::PermutingEncoder<loco::Domain::Feature>); encoder->perm(perm); @@ -80,7 +80,7 @@ protected: const loco::Permutation<loco::Domain::Feature> &perm) { auto decoder = std::unique_ptr<loco::PermutingDecoder<loco::Domain::Feature>>( - new loco::PermutingDecoder<loco::Domain::Feature>); + new loco::PermutingDecoder<loco::Domain::Feature>); decoder->perm(perm); diff --git a/compiler/locomotiv/src/Node/FeatureDecode.cpp b/compiler/locomotiv/src/Node/FeatureDecode.cpp index 8776e1b42..2877906f9 100644 --- a/compiler/locomotiv/src/Node/FeatureDecode.cpp +++ b/compiler/locomotiv/src/Node/FeatureDecode.cpp @@ -54,8 +54,8 @@ std::unique_ptr<locomotiv::NodeData> feature_decode(const loco::FeatureDecode *n // Make tensor buffer from TensorShape Buffer<T> node_buf = - make_buffer<T, LexicalLayout>(Shape{node_shape.dim(0).value(), node_shape.dim(1).value(), - node_shape.dim(2).value(), node_shape.dim(3).value()}); + make_buffer<T, LexicalLayout>(Shape{node_shape.dim(0).value(), node_shape.dim(1).value(), + node_shape.dim(2).value(), node_shape.dim(3).value()}); // Copy buffer in an order arranged by decoder for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) diff --git a/compiler/locomotiv/src/Node/FeatureEncode.cpp b/compiler/locomotiv/src/Node/FeatureEncode.cpp index 406de76ff..c3570b981 100644 --- a/compiler/locomotiv/src/Node/FeatureEncode.cpp +++ b/compiler/locomotiv/src/Node/FeatureEncode.cpp @@ -54,8 +54,8 @@ std::unique_ptr<locomotiv::NodeData> feature_encode(const loco::FeatureEncode *n // Make NHWC buffer from FeatureShape Buffer<T> node_buf = - make_buffer<T, LexicalLayout>(Shape{node_shape.count().value(), node_shape.height().value(), - node_shape.width().value(), node_shape.depth().value()}); + make_buffer<T, LexicalLayout>(Shape{node_shape.count().value(), node_shape.height().value(), + node_shape.width().value(), node_shape.depth().value()}); // Copy buffer in an order arranged by encoder for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) diff --git a/compiler/locomotiv/src/Node/FilterEncode.cpp b/compiler/locomotiv/src/Node/FilterEncode.cpp index 0e2ac918f..84ba681ba 100644 --- a/compiler/locomotiv/src/Node/FilterEncode.cpp +++ b/compiler/locomotiv/src/Node/FilterEncode.cpp @@ -54,8 +54,8 @@ std::unique_ptr<locomotiv::NodeData> filter_encode(const loco::FilterEncode *nod // Make NHWC buffer from FilterShape Buffer<T> node_buf = - make_buffer<T, LexicalLayout>(Shape{node_shape.count().value(), node_shape.height().value(), - node_shape.width().value(), node_shape.depth().value()}); + make_buffer<T, LexicalLayout>(Shape{node_shape.count().value(), node_shape.height().value(), + node_shape.width().value(), node_shape.depth().value()}); // Copy buffer in an order arranged by encoder for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) diff --git a/compiler/locomotiv/src/Node/FilterEncode.test.cpp b/compiler/locomotiv/src/Node/FilterEncode.test.cpp index dcca94993..80d108ece 100644 --- a/compiler/locomotiv/src/Node/FilterEncode.test.cpp +++ b/compiler/locomotiv/src/Node/FilterEncode.test.cpp @@ -62,7 +62,7 @@ TEST(NodeExecution_FilterEncode, s32) // Encoder to correctly read input tensor as NCHW auto encoder = std::unique_ptr<loco::PermutingEncoder<loco::Domain::Filter>>( - new loco::PermutingEncoder<loco::Domain::Filter>); + new loco::PermutingEncoder<loco::Domain::Filter>); encoder->perm()->axis(loco::FilterAxis::Count) = 0; encoder->perm()->axis(loco::FilterAxis::Depth) = 1; encoder->perm()->axis(loco::FilterAxis::Height) = 2; @@ -116,7 +116,7 @@ TEST(NodeExecution_FilterEncode, f32) // Encoder to correctly read input tensor as CHNW auto encoder = std::unique_ptr<loco::PermutingEncoder<loco::Domain::Filter>>( - new loco::PermutingEncoder<loco::Domain::Filter>); + new loco::PermutingEncoder<loco::Domain::Filter>); encoder->perm()->axis(loco::FilterAxis::Depth) = 0; encoder->perm()->axis(loco::FilterAxis::Height) = 1; encoder->perm()->axis(loco::FilterAxis::Count) = 2; diff --git a/compiler/locomotiv/src/Node/MatrixCodec.test.cpp b/compiler/locomotiv/src/Node/MatrixCodec.test.cpp index da4afeded..7f684e41f 100644 --- a/compiler/locomotiv/src/Node/MatrixCodec.test.cpp +++ b/compiler/locomotiv/src/Node/MatrixCodec.test.cpp @@ -64,7 +64,7 @@ protected: const loco::Permutation<loco::Domain::Matrix> &perm) { auto encoder = std::unique_ptr<loco::PermutingEncoder<loco::Domain::Matrix>>( - new loco::PermutingEncoder<loco::Domain::Matrix>); + new loco::PermutingEncoder<loco::Domain::Matrix>); encoder->perm(perm); @@ -80,7 +80,7 @@ protected: const loco::Permutation<loco::Domain::Matrix> &perm) { auto decoder = std::unique_ptr<loco::PermutingDecoder<loco::Domain::Matrix>>( - new loco::PermutingDecoder<loco::Domain::Matrix>); + new loco::PermutingDecoder<loco::Domain::Matrix>); decoder->perm(perm); diff --git a/compiler/locomotiv/src/Node/MatrixDecode.cpp b/compiler/locomotiv/src/Node/MatrixDecode.cpp index 0310015f1..2a65a7b74 100644 --- a/compiler/locomotiv/src/Node/MatrixDecode.cpp +++ b/compiler/locomotiv/src/Node/MatrixDecode.cpp @@ -52,7 +52,7 @@ std::unique_ptr<locomotiv::NodeData> matrix_decode(const loco::MatrixDecode *nod // Make tensor buffer from TensorShape Buffer<T> node_buf = - make_buffer<T, LexicalLayout>(Shape{node_shape.dim(0).value(), node_shape.dim(1).value()}); + make_buffer<T, LexicalLayout>(Shape{node_shape.dim(0).value(), node_shape.dim(1).value()}); // Copy buffer in an order arranged by decoder for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) diff --git a/compiler/locomotiv/src/Node/MatrixEncode.cpp b/compiler/locomotiv/src/Node/MatrixEncode.cpp index e3554e15a..ac51e4256 100644 --- a/compiler/locomotiv/src/Node/MatrixEncode.cpp +++ b/compiler/locomotiv/src/Node/MatrixEncode.cpp @@ -54,7 +54,7 @@ std::unique_ptr<locomotiv::NodeData> matrix_encode(const loco::MatrixEncode *nod // Make HW buffer from MatrixShape Buffer<T> node_buf = - make_buffer<T, LexicalLayout>(Shape{node_shape.height().value(), node_shape.width().value()}); + make_buffer<T, LexicalLayout>(Shape{node_shape.height().value(), node_shape.width().value()}); // Copy buffer in an order arranged by encoder for (IndexEnumerator e{node_buf.shape()}; e.valid(); e.advance()) diff --git a/compiler/locomotiv/src/Node/MaxPool2D.cpp b/compiler/locomotiv/src/Node/MaxPool2D.cpp index 8dce1cb1e..dc626387b 100644 --- a/compiler/locomotiv/src/Node/MaxPool2D.cpp +++ b/compiler/locomotiv/src/Node/MaxPool2D.cpp @@ -79,9 +79,9 @@ nncc::core::ADT::tensor::Buffer<T> maxPool2D(const loco::MaxPool2D *maxpool2d, const uint32_t pad_right = maxpool2d->pad()->right(); const uint32_t output_height = - compute_out_size(ifm_height, pad_top + pad_bottom, window_height, stride_height); + compute_out_size(ifm_height, pad_top + pad_bottom, window_height, stride_height); const uint32_t output_width = - compute_out_size(ifm_width, pad_left + pad_right, window_width, stride_width); + compute_out_size(ifm_width, pad_left + pad_right, window_width, stride_width); // prepare output buffer Shape output_shape{batches, output_height, output_width, depth}; diff --git a/compiler/locomotiv/src/Node/MaxPool2D.test.cpp b/compiler/locomotiv/src/Node/MaxPool2D.test.cpp index 5046d4a6e..d00282dd7 100644 --- a/compiler/locomotiv/src/Node/MaxPool2D.test.cpp +++ b/compiler/locomotiv/src/Node/MaxPool2D.test.cpp @@ -82,7 +82,7 @@ void run_test(const float *ifm, const float *expected_ofm, const Shape &ifm_shap ASSERT_TRUE(*(maxpool2d_data->shape()) == ofm_shape); auto ofm_overlay = - make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); + make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) { const auto &ind = e.current(); diff --git a/compiler/locomotiv/src/Node/TensorConcat.cpp b/compiler/locomotiv/src/Node/TensorConcat.cpp index 188bb635b..84da3a3e5 100644 --- a/compiler/locomotiv/src/Node/TensorConcat.cpp +++ b/compiler/locomotiv/src/Node/TensorConcat.cpp @@ -52,7 +52,7 @@ void execute_node(loco::TensorConcat *tensor_concat) validate(lhs_data->dtype() == rhs_data->dtype(), "lhs and rhs of Concat should have same dtype"); validate(annot_domain(tensor_concat->lhs()) == loco::Domain::Tensor && - annot_domain(tensor_concat->rhs()) == loco::Domain::Tensor, + annot_domain(tensor_concat->rhs()) == loco::Domain::Tensor, "Some ingredients of TensorConcat is not Tensor"); // Calculate output shape diff --git a/compiler/locomotiv/src/Node/TransposedConv2D.cpp b/compiler/locomotiv/src/Node/TransposedConv2D.cpp index bec15a5df..2f3c3d089 100644 --- a/compiler/locomotiv/src/Node/TransposedConv2D.cpp +++ b/compiler/locomotiv/src/Node/TransposedConv2D.cpp @@ -65,7 +65,7 @@ Buffer<RET_T> calc_tr_conv2D(const loco::TransposedConv2D *tr_conv2d, locomotiv::validate(input_shape.rank() == 4, "ifm rank must be 4"); locomotiv::validate(filter_shape.rank() == 4, "filter rank must be 4"); locomotiv::validate(input_shape.dim(3) /* depth of input */ == - filter_shape.dim(3) /* depth of filter */, + filter_shape.dim(3) /* depth of filter */, "channel value mismatch"); const uint32_t input_height = input_shape.dim(1); @@ -86,9 +86,9 @@ Buffer<RET_T> calc_tr_conv2D(const loco::TransposedConv2D *tr_conv2d, // TODO Support dilations const uint32_t output_height = - compute_transposed_out_size(input_height, pad_top + pad_bottom, filter_height, stride_height); + compute_transposed_out_size(input_height, pad_top + pad_bottom, filter_height, stride_height); const uint32_t output_width = - compute_transposed_out_size(input_width, pad_left + pad_right, filter_width, stride_width); + compute_transposed_out_size(input_width, pad_left + pad_right, filter_width, stride_width); const uint32_t batches = input_shape.dim(0); const uint32_t input_depth = input_shape.dim(3); @@ -131,9 +131,9 @@ Buffer<RET_T> calc_tr_conv2D(const loco::TransposedConv2D *tr_conv2d, { auto input_value = input_buf->at(Index({batch, in_y, in_x, in_channel})); auto filter_value = - filter_buf->at(Index({out_channel, filter_y, filter_x, in_channel})); + filter_buf->at(Index({out_channel, filter_y, filter_x, in_channel})); output_buf.at(Index({batch, (unsigned)out_y, (unsigned)out_x, out_channel})) += - input_value * filter_value; + input_value * filter_value; } } } diff --git a/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp b/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp index ef759f51b..a516ef9f2 100644 --- a/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp +++ b/compiler/locomotiv/src/Node/TransposedConv2D.test.cpp @@ -97,7 +97,7 @@ void run_test(const float *ifm, const float *ker, const float *expected_ofm, con ASSERT_TRUE(*(conv2d_result->shape()) == ofm_shape); auto ofm_overlay = - make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); + make_overlay<float, LexicalLayout>(ofm_shape, const_cast<float *>(expected_ofm)); for (nncc::core::ADT::tensor::IndexEnumerator e{ofm_shape}; e.valid(); e.advance()) { const auto &ind = e.current(); diff --git a/compiler/locomotiv/src/NodeDataImpl.cpp b/compiler/locomotiv/src/NodeDataImpl.cpp index 2efebe5a9..9373b8dd2 100644 --- a/compiler/locomotiv/src/NodeDataImpl.cpp +++ b/compiler/locomotiv/src/NodeDataImpl.cpp @@ -16,8 +16,7 @@ #include "NodeDataImpl.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace @@ -59,7 +58,7 @@ template <> NodeDataImpl::NodeDataImpl(const Buffer<float> &buf) void annot_data(loco::Node *node, std::unique_ptr<NodeData> &&data) { - node->annot(stdex::make_unique<NodeDataAnnotation>(std::move(data))); + node->annot(std::make_unique<NodeDataAnnotation>(std::move(data))); } const NodeData *annot_data(const loco::Node *node) diff --git a/compiler/locomotiv/src/NodeExecution.h b/compiler/locomotiv/src/NodeExecution.h index 363188d38..eb0608d2b 100644 --- a/compiler/locomotiv/src/NodeExecution.h +++ b/compiler/locomotiv/src/NodeExecution.h @@ -62,7 +62,7 @@ private: return dynamic_cast<Derived *>(node); } -// clang-format off + // clang-format off /** * @brief Calculate for one specified node and update its result as NodeData. * Abort program when its ingredients are not ready or not supported. diff --git a/compiler/locomotiv/src/UserData.cpp b/compiler/locomotiv/src/UserData.cpp index b658ada9b..98f761efd 100644 --- a/compiler/locomotiv/src/UserData.cpp +++ b/compiler/locomotiv/src/UserData.cpp @@ -16,8 +16,7 @@ #include "UserData.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace @@ -55,7 +54,7 @@ const NodeData *user_data(const loco::Node *node) void user_data(loco::Node *node, std::unique_ptr<NodeData> &&data) { - node->annot(stdex::make_unique<UserDataAnnotation>(std::move(data))); + node->annot(std::make_unique<UserDataAnnotation>(std::move(data))); } void erase_user_data(loco::Node *node) { node->annot<UserDataAnnotation>(nullptr); } diff --git a/compiler/locop/CMakeLists.txt b/compiler/locop/CMakeLists.txt index 107ee8be8..f02fb1a72 100644 --- a/compiler/locop/CMakeLists.txt +++ b/compiler/locop/CMakeLists.txt @@ -13,7 +13,6 @@ target_link_libraries(locop PUBLIC loco) target_link_libraries(locop PRIVATE nncc_common) target_link_libraries(locop PUBLIC nncc_coverage) target_link_libraries(locop PRIVATE pp) -target_link_libraries(locop PRIVATE stdex) if(NOT ENABLE_TEST) return() @@ -23,5 +22,4 @@ endif(NOT ENABLE_TEST) nnas_find_package(GTest REQUIRED) GTest_AddTest(locop_test ${TESTS}) -target_link_libraries(locop_test stdex) target_link_libraries(locop_test locop) diff --git a/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp b/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp index 61d9e8ae7..75dd39f36 100644 --- a/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp +++ b/compiler/locop/src/CanonicalNodeSummaryBuilder.cpp @@ -25,8 +25,6 @@ #include <pp/Format.h> -#include <stdex/Memory.h> - #include <map> #include <set> diff --git a/compiler/locop/src/ExampleGraph.h b/compiler/locop/src/ExampleGraph.h index 76813bcd8..84010f75b 100644 --- a/compiler/locop/src/ExampleGraph.h +++ b/compiler/locop/src/ExampleGraph.h @@ -19,7 +19,7 @@ #include <loco.h> -#include <stdex/Memory.h> +#include <memory> namespace { @@ -55,7 +55,7 @@ template <> std::unique_ptr<Bundle<PullPush>> make_bundle(void) push->from(pull); - auto res = stdex::make_unique<Bundle<PullPush>>(); + auto res = std::make_unique<Bundle<PullPush>>(); res->g = std::move(g); res->pull = pull; diff --git a/compiler/locop/src/FormattedGraph.cpp b/compiler/locop/src/FormattedGraph.cpp index bf4175768..94bfbd2f8 100644 --- a/compiler/locop/src/FormattedGraph.cpp +++ b/compiler/locop/src/FormattedGraph.cpp @@ -23,8 +23,7 @@ #include <pp/Format.h> -#include <stdex/Memory.h> - +#include <memory> #include <map> #include <set> @@ -300,7 +299,7 @@ void FormattedGraphImpl<Formatter::LinearV1>::dump(std::ostream &os) const else { // Use Built-in NodeSummaryBuilder otherwise - node_summary_builder = stdex::make_unique<GenericNodeSummaryBuilder>(&symbols); + node_summary_builder = std::make_unique<GenericNodeSummaryBuilder>(&symbols); } // Print Graph Input(s) diff --git a/compiler/locop/src/FormattedGraph.test.cpp b/compiler/locop/src/FormattedGraph.test.cpp index aff9ebe5f..9f11a4e5d 100644 --- a/compiler/locop/src/FormattedGraph.test.cpp +++ b/compiler/locop/src/FormattedGraph.test.cpp @@ -17,7 +17,7 @@ #include "locop/FormattedGraph.h" #include "ExampleGraph.h" -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> @@ -42,7 +42,7 @@ TEST(LinearV1FormatterTest, user_defined_node_summary_builder) auto bundle = make_bundle<PullPush>(); auto g = bundle->graph(); { - bundle->push->annot(stdex::make_unique<MyAnnotation>()); + bundle->push->annot(std::make_unique<MyAnnotation>()); } struct MyBuilder final : public locop::NodeSummaryBuilder @@ -63,11 +63,11 @@ TEST(LinearV1FormatterTest, user_defined_node_summary_builder) { std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *) const final { - return stdex::make_unique<MyBuilder>(); + return std::make_unique<MyBuilder>(); } }; - std::cout << locop::fmt<locop::LinearV1>(g).with(stdex::make_unique<MyFactory>()) << std::endl; + std::cout << locop::fmt<locop::LinearV1>(g).with(std::make_unique<MyFactory>()) << std::endl; // TODO Check whether MyBuilder actually sees all the nodes in a graph SUCCEED(); @@ -134,11 +134,11 @@ TEST(LinearV1FormatterTest, node_summary_builder_composition) { std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tbl) const final { - return stdex::make_unique<CompositeBuilder>(tbl); + return std::make_unique<CompositeBuilder>(tbl); } }; - std::cout << locop::fmt<locop::LinearV1>(g).with(stdex::make_unique<MyFactory>()) << std::endl; + std::cout << locop::fmt<locop::LinearV1>(g).with(std::make_unique<MyFactory>()) << std::endl; // TODO Check whether MyBuilder actually sees all the nodes in a graph SUCCEED(); diff --git a/compiler/locop/src/FormattedTensorShape.cpp b/compiler/locop/src/FormattedTensorShape.cpp index b2b6ea074..bc6310313 100644 --- a/compiler/locop/src/FormattedTensorShape.cpp +++ b/compiler/locop/src/FormattedTensorShape.cpp @@ -25,7 +25,7 @@ std::ostream &operator<<(std::ostream &os, const loco::Dimension &d) return os; } -} // namespace +} // namespace loco namespace locop { diff --git a/compiler/locop/src/FormattedTensorShape.test.cpp b/compiler/locop/src/FormattedTensorShape.test.cpp index fc85df3a6..626b6cc23 100644 --- a/compiler/locop/src/FormattedTensorShape.test.cpp +++ b/compiler/locop/src/FormattedTensorShape.test.cpp @@ -16,7 +16,7 @@ #include "locop/FormattedTensorShape.h" -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> @@ -24,12 +24,26 @@ using namespace locop; TEST(FormattedTensorShapeTest, BracketFormat) { - auto tensor_shape = stdex::make_unique<loco::TensorShape>(); + auto tensor_shape = std::make_unique<loco::TensorShape>(); tensor_shape->rank(2); tensor_shape->dim(0) = 4; + tensor_shape->dim(1) = 8; std::cout << fmt<TensorShapeFormat::Bracket>(tensor_shape.get()) << std::endl; SUCCEED(); } + +TEST(FormattedTensorShapeTest, PlainFormat) +{ + auto tensor_shape = std::make_unique<loco::TensorShape>(); + + tensor_shape->rank(2); + tensor_shape->dim(0) = 4; + tensor_shape->dim(1) = 8; + + std::cout << fmt<TensorShapeFormat::Plain>(tensor_shape.get()) << std::endl; + + SUCCEED(); +} diff --git a/compiler/locop/src/GenericNodeSummaryBuilder.test.cpp b/compiler/locop/src/GenericNodeSummaryBuilder.test.cpp index d688b5490..cfa82c2a2 100644 --- a/compiler/locop/src/GenericNodeSummaryBuilder.test.cpp +++ b/compiler/locop/src/GenericNodeSummaryBuilder.test.cpp @@ -17,8 +17,7 @@ #include "locop/GenericNodeSummaryBuilder.h" #include "locop/FormattedGraph.h" -#include <stdex/Memory.h> - +#include <memory> #include <stdexcept> #include <gtest/gtest.h> @@ -44,7 +43,7 @@ TEST(GenericNodeSummaryBuilderTest, simple) { std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tbl) const final { - return stdex::make_unique<locop::GenericNodeSummaryBuilder>(tbl); + return std::make_unique<locop::GenericNodeSummaryBuilder>(tbl); } }; @@ -52,7 +51,7 @@ TEST(GenericNodeSummaryBuilderTest, simple) g->nodes()->create<MockNode>(); - std::cout << locop::fmt<locop::LinearV1>(g).with(stdex::make_unique<MockFactory>()) << std::endl; + std::cout << locop::fmt<locop::LinearV1>(g).with(std::make_unique<MockFactory>()) << std::endl; SUCCEED(); } diff --git a/compiler/locop/src/NodeSummary.cpp b/compiler/locop/src/NodeSummary.cpp index 3f8856997..20250a90f 100644 --- a/compiler/locop/src/NodeSummary.cpp +++ b/compiler/locop/src/NodeSummary.cpp @@ -16,8 +16,7 @@ #include "locop/NodeSummary.h" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace locop @@ -36,6 +35,6 @@ const std::string &NodeDesc::opname(void) const return *_name; } -void NodeDesc::opname(const std::string &v) { _name = stdex::make_unique<std::string>(v); } +void NodeDesc::opname(const std::string &v) { _name = std::make_unique<std::string>(v); } -} // namespace loco +} // namespace locop diff --git a/compiler/logo-core/src/Phase.test.cpp b/compiler/logo-core/src/Phase.test.cpp new file mode 100644 index 000000000..2ee09101b --- /dev/null +++ b/compiler/logo-core/src/Phase.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 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 <logo/Phase.h> + +#include <loco.h> + +#include <gtest/gtest.h> + +namespace +{ + +struct Bumblebee final : public logo::Pass +{ + const char *name(void) const final { return "Bee"; } + bool run(loco::Graph *) final { return false; } +}; + +} // namespace + +TEST(LogoPhaseSaturateTests, simple) +{ + loco::Graph g; + logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{&g}; + logo::Phase phase; + + phase.emplace_back(std::make_unique<Bumblebee>()); + phase_runner.run(phase); + + SUCCEED(); +} + +TEST(LogoPhaseRestartTests, simple) +{ + loco::Graph g; + logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{&g}; + logo::Phase phase; + + phase.emplace_back(std::make_unique<Bumblebee>()); + phase_runner.run(phase); + + SUCCEED(); +} diff --git a/compiler/logo/CMakeLists.txt b/compiler/logo/CMakeLists.txt index 399cb7586..a8efd9b03 100644 --- a/compiler/logo/CMakeLists.txt +++ b/compiler/logo/CMakeLists.txt @@ -9,7 +9,6 @@ target_include_directories(logo PUBLIC include) target_link_libraries(logo PUBLIC loco) target_link_libraries(logo PUBLIC logo_core) target_link_libraries(logo PRIVATE locomotiv) -target_link_libraries(logo PRIVATE stdex) if(NOT ENABLE_TEST) return() @@ -20,4 +19,3 @@ nnas_find_package(GTest REQUIRED) GTest_AddTest(logo_test ${TESTS}) target_include_directories(logo_test PRIVATE src) target_link_libraries(logo_test logo) -target_link_libraries(logo_test stdex) diff --git a/compiler/logo/requires.cmake b/compiler/logo/requires.cmake index 9a7d14788..c76183353 100644 --- a/compiler/logo/requires.cmake +++ b/compiler/logo/requires.cmake @@ -1,4 +1,3 @@ require("loco") require("logo-core") require("locomotiv") -require("stdex") diff --git a/compiler/logo/src/Passes/ConstantFoldingPass.cpp b/compiler/logo/src/Passes/ConstantFoldingPass.cpp index e038e7140..2bd4759ca 100644 --- a/compiler/logo/src/Passes/ConstantFoldingPass.cpp +++ b/compiler/logo/src/Passes/ConstantFoldingPass.cpp @@ -19,8 +19,6 @@ #include <loco.h> #include <loco/IR/CanonicalDialect.h> -#include <stdex/Memory.h> - #include <locomotiv/Session.h> #include <cassert> @@ -52,19 +50,19 @@ uint64_t num_elements(const loco::NodeMixin<loco::NodeTrait::TensorShape> &shape bool skip(const loco::Node *node) { static std::set<uint32_t> skip_op = { - // TODO Current implementation works for 'Tensor' domain only. Support other domains such as - // `Feature`, `Filter`, `Bias`, etc. - static_cast<uint32_t>(loco::CanonicalOpcode::FilterEncode), - static_cast<uint32_t>(loco::CanonicalOpcode::FeatureEncode), - static_cast<uint32_t>(loco::CanonicalOpcode::BiasEncode), - static_cast<uint32_t>(loco::CanonicalOpcode::DepthwiseFilterEncode), - - // We don't perform constant folding for Push - static_cast<uint32_t>(loco::CanonicalOpcode::Push), - - // TensorBroadcast is a good hint for optimization - // TODO Let this option be controlled by driver using logo - static_cast<uint32_t>(loco::CanonicalOpcode::TensorBroadcast), + // TODO Current implementation works for 'Tensor' domain only. Support other domains such as + // `Feature`, `Filter`, `Bias`, etc. + static_cast<uint32_t>(loco::CanonicalOpcode::FilterEncode), + static_cast<uint32_t>(loco::CanonicalOpcode::FeatureEncode), + static_cast<uint32_t>(loco::CanonicalOpcode::BiasEncode), + static_cast<uint32_t>(loco::CanonicalOpcode::DepthwiseFilterEncode), + + // We don't perform constant folding for Push + static_cast<uint32_t>(loco::CanonicalOpcode::Push), + + // TensorBroadcast is a good hint for optimization + // TODO Let this option be controlled by driver using logo + static_cast<uint32_t>(loco::CanonicalOpcode::TensorBroadcast), }; if (node->dialect() == loco::CanonicalDialect::get()) diff --git a/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp b/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp index b9c4942c4..5d222eb00 100644 --- a/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp +++ b/compiler/logo/src/Passes/ConstantFoldingPass.test.cpp @@ -24,6 +24,21 @@ using namespace logo::test; +TEST(ConstantFoldingTest, name) +{ + logo::ConstantFoldingPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(ConstantFoldingTest, run_NEG) +{ + loco::Graph g; + logo::ConstantFoldingPass pass; + + ASSERT_FALSE(pass.run(&g)); +} + namespace { diff --git a/compiler/logo/src/Passes/EmptyTestGraph.h b/compiler/logo/src/Passes/EmptyTestGraph.h new file mode 100644 index 000000000..67f2c8a11 --- /dev/null +++ b/compiler/logo/src/Passes/EmptyTestGraph.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 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 __LOGO_EMPTY_TEST_GRAPH_H__ +#define __LOGO_EMPTY_TEST_GRAPH_H__ + +#include <loco.h> + +namespace logo +{ + +void create_empty_test_net(loco::Graph *graph); + +} // namespace logo + +#endif // __LOGO_EMPTY_TEST_GRAPH_H__ diff --git a/compiler/logo/src/Passes/EmptyTestGraph.test.cpp b/compiler/logo/src/Passes/EmptyTestGraph.test.cpp new file mode 100644 index 000000000..46750b79c --- /dev/null +++ b/compiler/logo/src/Passes/EmptyTestGraph.test.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 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 <loco.h> + +#include <gtest/gtest.h> + +namespace logo +{ + +void create_empty_test_net(loco::Graph *graph) +{ + assert(graph); + + auto const_node = graph->nodes()->create<loco::ConstGen>(); + { + const_node->dtype(loco::DataType::FLOAT32); + const_node->rank(1); + const_node->dim(0) = 1; + const_node->size<loco::DataType::FLOAT32>(1); + const_node->at<loco::DataType::FLOAT32>(0) = 1.0f; + } + + auto push_node = graph->nodes()->create<loco::Push>(); + { + push_node->from(const_node); + } + + auto graph_output = graph->outputs()->create(); + { + graph_output->name("output"); + graph_output->dtype(loco::DataType::FLOAT32); + loco::link(graph_output, push_node); + } +} + +} // namespace logo diff --git a/compiler/logo/src/Passes/RemoveDeadNodePass.test.cpp b/compiler/logo/src/Passes/RemoveDeadNodePass.test.cpp new file mode 100644 index 000000000..c0ecbdaa9 --- /dev/null +++ b/compiler/logo/src/Passes/RemoveDeadNodePass.test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 <logo/RemoveDeadNodePass.h> + +#include "EmptyTestGraph.h" + +#include <gtest/gtest.h> + +TEST(RemoveDeadNodePassTest, name) +{ + logo::RemoveDeadNodePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(RemoveDeadNodePassTest, run_NEG) +{ + loco::Graph g; + logo::RemoveDeadNodePass pass; + + logo::create_empty_test_net(&g); + + ASSERT_FALSE(pass.run(&g)); +} diff --git a/compiler/stdex/src/Set.test.cpp b/compiler/logo/src/Passes/RemoveDeadNodeWithQueryPass.test.cpp index 90361936f..f14bfc30d 100644 --- a/compiler/stdex/src/Set.test.cpp +++ b/compiler/logo/src/Passes/RemoveDeadNodeWithQueryPass.test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,24 +14,25 @@ * limitations under the License. */ -#include "stdex/Set.h" +#include <logo/RemoveDeadNodeWithQueryPass.h> + +#include "EmptyTestGraph.h" #include <gtest/gtest.h> -TEST(SET, operator_eq) +TEST(RemoveDeadNodeWithQueryPassTest, name) { - ASSERT_TRUE(std::set<int>({1, 2, 3}) == std::set<int>({1, 2, 3})); - ASSERT_FALSE(std::set<int>({1, 3}) == std::set<int>({1, 2, 3})); + logo::RemoveDeadNodeWithQueryPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); } -TEST(SET, operator_diff) +TEST(RemoveDeadNodeWithQueryPassTest, run_NEG) { - const std::set<int> lhs{1, 2, 3}; - const std::set<int> rhs{2, 4}; + loco::Graph g; + logo::RemoveDeadNodeWithQueryPass pass; - auto res = lhs - rhs; + logo::create_empty_test_net(&g); - ASSERT_EQ(res.size(), 2); - ASSERT_NE(res.find(1), res.end()); - ASSERT_NE(res.find(3), res.end()); + ASSERT_FALSE(pass.run(&g)); } diff --git a/compiler/logo/src/Passes/RemoveForwardNodePass.test.cpp b/compiler/logo/src/Passes/RemoveForwardNodePass.test.cpp new file mode 100644 index 000000000..bb905aec5 --- /dev/null +++ b/compiler/logo/src/Passes/RemoveForwardNodePass.test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 <logo/RemoveForwardNodePass.h> + +#include "EmptyTestGraph.h" + +#include <gtest/gtest.h> + +TEST(RemoveForwardNodePassTest, name) +{ + logo::RemoveForwardNodePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(RemoveForwardNodePassTest, run_NEG) +{ + loco::Graph g; + logo::RemoveForwardNodePass pass; + + logo::create_empty_test_net(&g); + + ASSERT_FALSE(pass.run(&g)); +} diff --git a/compiler/logo/src/Passes/ReorderDecodePass.test.cpp b/compiler/logo/src/Passes/ReorderDecodePass.test.cpp new file mode 100644 index 000000000..f8e158d3a --- /dev/null +++ b/compiler/logo/src/Passes/ReorderDecodePass.test.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 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 <logo/ReorderDecodePass.h> + +#include "EmptyTestGraph.h" + +#include <gtest/gtest.h> + +TEST(ReorderDecodePassTest, TensorBiasAdd_name) +{ + logo::ReorderDecodePass<loco::TensorBiasAdd> pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(ReorderDecodePassTest, ReLU_name) +{ + logo::ReorderDecodePass<loco::ReLU> pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(ReorderDecodePassTest, TensorBiasAdd_run_NEG) +{ + loco::Graph g; + logo::ReorderDecodePass<loco::TensorBiasAdd> pass; + + logo::create_empty_test_net(&g); + + ASSERT_FALSE(pass.run(&g)); +} + +TEST(ReorderDecodePassTest, ReLU_run_NEG) +{ + loco::Graph g; + logo::ReorderDecodePass<loco::ReLU> pass; + + logo::create_empty_test_net(&g); + + ASSERT_FALSE(pass.run(&g)); +} diff --git a/compiler/logo/src/Passes/ResolveDuplicateReshapePass.test.cpp b/compiler/logo/src/Passes/ResolveDuplicateReshapePass.test.cpp new file mode 100644 index 000000000..de2df6fd5 --- /dev/null +++ b/compiler/logo/src/Passes/ResolveDuplicateReshapePass.test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 <logo/ResolveDuplicateReshapePass.h> + +#include "EmptyTestGraph.h" + +#include <gtest/gtest.h> + +TEST(ResolveDuplicateReshapePassTest, name) +{ + logo::ResolveDuplicateReshapePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(ResolveDuplicateReshapePassTest, run_NEG) +{ + loco::Graph g; + logo::ResolveDuplicateReshapePass pass; + + logo::create_empty_test_net(&g); + + ASSERT_FALSE(pass.run(&g)); +} diff --git a/compiler/logo/src/Passes/ResolveRedundantReshapePass.test.cpp b/compiler/logo/src/Passes/ResolveRedundantReshapePass.test.cpp new file mode 100644 index 000000000..9a7e95846 --- /dev/null +++ b/compiler/logo/src/Passes/ResolveRedundantReshapePass.test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 <logo/ResolveRedundantReshapePass.h> + +#include "EmptyTestGraph.h" + +#include <gtest/gtest.h> + +TEST(ResolveRedundantReshapePassTest, name) +{ + logo::ResolveRedundantReshapePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(ResolveRedundantReshapePassTest, run_NEG) +{ + loco::Graph g; + logo::ResolveRedundantReshapePass pass; + + logo::create_empty_test_net(&g); + + ASSERT_FALSE(pass.run(&g)); +} diff --git a/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp index 0bda85b6f..500f08623 100644 --- a/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp +++ b/compiler/logo/src/Passes/SimplifyDomainConversionPass.cpp @@ -20,8 +20,7 @@ #include <loco/IR/CanonicalDialect.h> #include <loco/IR/CanonicalNode.h> -#include <stdex/Memory.h> - +#include <memory> #include <set> #include <vector> #include <cassert> @@ -231,8 +230,8 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) perm_vec[to] = from; } - transposeCandidates.insert(stdex::make_unique<TransposeCtx>( - encode_node, decode_node, encode_node->input(), perm_vec)); + transposeCandidates.insert( + std::make_unique<TransposeCtx>(encode_node, decode_node, encode_node->input(), perm_vec)); } } @@ -293,8 +292,8 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) perm_vec[to] = from; } - transposeCandidates.insert(stdex::make_unique<TransposeCtx>( - encode_node, decode_node, encode_node->input(), perm_vec)); + transposeCandidates.insert( + std::make_unique<TransposeCtx>(encode_node, decode_node, encode_node->input(), perm_vec)); } } @@ -377,8 +376,8 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) perm_vec[to] = from; } - transposeCandidates.insert(stdex::make_unique<TransposeCtx>( - encode_node, decode_node, encode_node->input(), perm_vec)); + transposeCandidates.insert( + std::make_unique<TransposeCtx>(encode_node, decode_node, encode_node->input(), perm_vec)); } } @@ -397,7 +396,7 @@ bool SimplifyDomainConversionPass::run(loco::Graph *g) TransposeCtx(loco::Node *first, loco::Node *last, loco::Node *input, std::vector<loco::TensorAxis> perm) - : first_node(first), last_node(last), input_node(input), perm_vec(perm) + : first_node(first), last_node(last), input_node(input), perm_vec(perm) { /* empty */ } }; diff --git a/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp b/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp index 9a05763b4..75a288089 100644 --- a/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp +++ b/compiler/logo/src/Passes/SimplifyDomainConversionPass.test.cpp @@ -19,10 +19,26 @@ #include "TestHelper.h" #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> #include <gtest/gtest.h> +TEST(SimplifyDomainConversionPassTest, name) +{ + logo::SimplifyDomainConversionPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(SimplifyDomainConversionPassTest, run_NEG) +{ + loco::Graph g; + logo::SimplifyDomainConversionPass pass; + + ASSERT_FALSE(pass.run(&g)); +} + namespace { @@ -65,7 +81,7 @@ template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *inp { loco::Graph *g = input_for_decode->graph(); - auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>(); + auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>(); decoder->perm(perm<T>()); @@ -80,7 +96,7 @@ template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *inp { loco::Graph *g = input_for_encode->graph(); - auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); encoder->perm(perm<T>()); diff --git a/compiler/luci-eval-driver/CMakeLists.txt b/compiler/luci-eval-driver/CMakeLists.txt new file mode 100644 index 000000000..990f9d1a9 --- /dev/null +++ b/compiler/luci-eval-driver/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SRCS_EVAL_TESTER + src/EvalDriver.cpp + ) + +add_executable(luci_eval_driver ${SRCS_EVAL_TESTER}) +target_link_libraries(luci_eval_driver PRIVATE oops) +target_link_libraries(luci_eval_driver PRIVATE loco) +target_link_libraries(luci_eval_driver PRIVATE luci_import) +target_link_libraries(luci_eval_driver PRIVATE luci_export) +target_link_libraries(luci_eval_driver PRIVATE luci_lang) +target_link_libraries(luci_eval_driver PRIVATE luci_interpreter) +target_link_libraries(luci_eval_driver PRIVATE safemain) diff --git a/compiler/luci-eval-driver/requires.cmake b/compiler/luci-eval-driver/requires.cmake new file mode 100644 index 000000000..2904d9d3c --- /dev/null +++ b/compiler/luci-eval-driver/requires.cmake @@ -0,0 +1,5 @@ +require("oops") +require("loco") +require("luci") +require("luci-interpreter") +require("safemain") diff --git a/compiler/luci-value-test/tester/src/EvalTester.cpp b/compiler/luci-eval-driver/src/EvalDriver.cpp index b49602e5e..4762cffe7 100644 --- a/compiler/luci-value-test/tester/src/EvalTester.cpp +++ b/compiler/luci-eval-driver/src/EvalDriver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -21,11 +21,8 @@ #include <cstdlib> #include <fstream> -#include <iostream> #include <vector> -#include <map> #include <string> -#include <random> namespace { @@ -73,7 +70,7 @@ template <typename NodeT> size_t getTensorSize(const NodeT *node) } // namespace /* - * @brief EvalTester main + * @brief EvalDriver main * * Driver for testing luci-inerpreter * @@ -83,8 +80,8 @@ int entry(int argc, char **argv) if (argc != 5) { std::cerr - << "Usage: " << argv[0] - << " <path/to/circle/model> <num_inputs> <path/to/input/prefix> <path/to/output/file>\n"; + << "Usage: " << argv[0] + << " <path/to/circle/model> <num_inputs> <path/to/input/prefix> <path/to/output/file>\n"; return EXIT_FAILURE; } @@ -92,32 +89,12 @@ int entry(int argc, char **argv) const int32_t num_inputs = atoi(argv[2]); const char *input_prefix = argv[3]; const char *output_file = argv[4]; - const std::string intermediate_filename = std::string(filename) + ".inter.circle"; // Load model from the file - std::unique_ptr<luci::Module> initial_module = importModel(filename); - if (initial_module == nullptr) - { - std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl; - return EXIT_FAILURE; - } - - // Export to a Circle file - luci::CircleExporter exporter; - - luci::CircleFileExpContract contract(initial_module.get(), intermediate_filename); - - if (!exporter.invoke(&contract)) - { - std::cerr << "ERROR: Failed to export '" << intermediate_filename << "'" << std::endl; - return EXIT_FAILURE; - } - - // Import model again - std::unique_ptr<luci::Module> module = importModel(intermediate_filename); + std::unique_ptr<luci::Module> module = importModel(filename); if (module == nullptr) { - std::cerr << "ERROR: Failed to load '" << intermediate_filename << "'" << std::endl; + std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl; return EXIT_FAILURE; } diff --git a/compiler/luci-interpreter/src/Interpreter.cpp b/compiler/luci-interpreter/src/Interpreter.cpp index 639ffc1f0..b57b691d0 100644 --- a/compiler/luci-interpreter/src/Interpreter.cpp +++ b/compiler/luci-interpreter/src/Interpreter.cpp @@ -31,7 +31,7 @@ class EventNotifierImpl final : public EventNotifier public: EventNotifierImpl(const RuntimeToIR &runtime_to_ir, const std::vector<ExecutionObserver *> &observers) - : _runtime_to_ir(runtime_to_ir), _observers(observers) + : _runtime_to_ir(runtime_to_ir), _observers(observers) { } diff --git a/compiler/luci-interpreter/src/core/Kernel.h b/compiler/luci-interpreter/src/core/Kernel.h index 5f5efb219..5cdb2e360 100644 --- a/compiler/luci-interpreter/src/core/Kernel.h +++ b/compiler/luci-interpreter/src/core/Kernel.h @@ -29,7 +29,7 @@ class Kernel { protected: Kernel(std::vector<const Tensor *> inputs, std::vector<Tensor *> outputs) - : _inputs(std::move(inputs)), _outputs(std::move(outputs)) + : _inputs(std::move(inputs)), _outputs(std::move(outputs)) { } @@ -59,7 +59,7 @@ template <typename Params> class KernelWithParams : public Kernel protected: KernelWithParams(std::vector<const Tensor *> inputs, std::vector<Tensor *> outputs, const Params ¶ms) - : Kernel(std::move(inputs), std::move(outputs)), _params(params) + : Kernel(std::move(inputs), std::move(outputs)), _params(params) { } diff --git a/compiler/luci-interpreter/src/core/KernelParams.h b/compiler/luci-interpreter/src/core/KernelParams.h index b74be797b..dab6ba25f 100644 --- a/compiler/luci-interpreter/src/core/KernelParams.h +++ b/compiler/luci-interpreter/src/core/KernelParams.h @@ -44,6 +44,7 @@ struct ArgMaxParams struct ConcatenationParams { int axis; + Activation activation; }; struct Conv2DParams @@ -111,6 +112,12 @@ struct MulParams Activation activation; }; +struct PackParams +{ + int32_t values_count; + int32_t axis; +}; + struct Pool2DParams { Padding padding; diff --git a/compiler/luci-interpreter/src/core/RuntimeGraph.cpp b/compiler/luci-interpreter/src/core/RuntimeGraph.cpp index 57f6fed44..fb0ad304b 100644 --- a/compiler/luci-interpreter/src/core/RuntimeGraph.cpp +++ b/compiler/luci-interpreter/src/core/RuntimeGraph.cpp @@ -94,7 +94,7 @@ void RuntimeGraph::TensorAllocPlan::deallocate(size_t kernel_index) const } RuntimeGraph::RuntimeGraph(RuntimeModule *owning_module) - : _owning_module(owning_module), _tensor_alloc_plan(std::make_unique<TensorAllocPlan>()) + : _owning_module(owning_module), _tensor_alloc_plan(std::make_unique<TensorAllocPlan>()) { } diff --git a/compiler/luci-interpreter/src/core/Tensor.cpp b/compiler/luci-interpreter/src/core/Tensor.cpp index 6e0424ffa..a9e7be0a9 100644 --- a/compiler/luci-interpreter/src/core/Tensor.cpp +++ b/compiler/luci-interpreter/src/core/Tensor.cpp @@ -24,8 +24,8 @@ namespace luci_interpreter Tensor::Tensor(DataType element_type, Shape shape, AffineQuantization quantization, std::string name) - : _element_type(element_type), _shape(std::move(shape)), _quantization(std::move(quantization)), - _name(std::move(name)), _data_allocated(false) + : _element_type(element_type), _shape(std::move(shape)), _quantization(std::move(quantization)), + _name(std::move(name)), _data_allocated(false) { } diff --git a/compiler/luci-interpreter/src/kernels/Add.cpp b/compiler/luci-interpreter/src/kernels/Add.cpp index 8d119d516..7381c3849 100644 --- a/compiler/luci-interpreter/src/kernels/Add.cpp +++ b/compiler/luci-interpreter/src/kernels/Add.cpp @@ -31,7 +31,7 @@ namespace kernels { Add::Add(const Tensor *input1, const Tensor *input2, Tensor *output, const AddParams ¶ms) - : KernelWithParams<AddParams>({input1, input2}, {output}, params) + : KernelWithParams<AddParams>({input1, input2}, {output}, params) { } @@ -76,13 +76,13 @@ void Add::evalFloat() const params.float_activation_max = activation_max; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { tflite::reference_ops::BroadcastAdd4DSlow( - params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), - getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); + params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), + getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); } else { @@ -130,14 +130,13 @@ void Add::evalQuantized() const params.quantized_activation_max = activation_max; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { tflite::reference_ops::BroadcastAdd4DSlow( - params, getTensorShape(input1()), getTensorData<uint8_t>(input1()), - getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + params, getTensorShape(input1()), getTensorData<uint8_t>(input1()), getTensorShape(input2()), + getTensorData<uint8_t>(input2()), getTensorShape(output()), getTensorData<uint8_t>(output())); } else { @@ -176,12 +175,12 @@ void Add::evalQuantizedS16() const const int32_t shifted_input1_val = static_cast<int32_t>(input1_val) << left_shift; const int32_t shifted_input2_val = static_cast<int32_t>(input2_val) << left_shift; const int32_t scaled_input1_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input1_val, input1_multiplier, input1_shift); + shifted_input1_val, input1_multiplier, input1_shift); const int32_t scaled_input2_val = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( - shifted_input2_val, input2_multiplier, input2_shift); + shifted_input2_val, input2_multiplier, input2_shift); const int32_t raw_sum = scaled_input1_val + scaled_input2_val; const int32_t raw_output = tflite::MultiplyByQuantizedMultiplierSmallerThanOneExp( - raw_sum, output_multiplier, output_shift); + raw_sum, output_multiplier, output_shift); const int32_t clamped_output = std::min(activation_max, std::max(activation_min, raw_output)); return static_cast<int16_t>(clamped_output); }; diff --git a/compiler/luci-interpreter/src/kernels/Add.test.cpp b/compiler/luci-interpreter/src/kernels/Add.test.cpp index de8a3bbb0..5ad9beb30 100644 --- a/compiler/luci-interpreter/src/kernels/Add.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Add.test.cpp @@ -40,29 +40,29 @@ TEST(AddTest, Uint8) std::initializer_list<float> base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; std::initializer_list<int32_t> test_shapes[] = { - {1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; + {1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; std::initializer_list<float> test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; std::initializer_list<int32_t> output_shapes[] = { - {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; std::vector<std::vector<float>> output_data = { - {-0.1f, 2.6f, -0.7f, 2.8f, 0.7f, 3.0f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, - 1.0f, -0.8f, 0.4f, -0.6f, 1.8f, -0.2f, 1.4f, 3.0f, 0.8f, 3.0f, 2.2f, 3.0f, - -1.4f, 0.3f, -2.0f, 0.5f, -0.6f, 0.9f, 0.9f, -1.9f, 0.3f, -1.7f, 1.7f, -1.3f}, - {-0.1f, 2.6f, 0.5f, 1.0f, 1.8f, -0.2f, 1.4f, 3.0f, -2.0f, 0.5f, 1.7f, -1.3f}, - {-0.1f, 2.5f, 0.0f, 2.6f, -0.7f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, - 1.0f, -0.9f, 1.1f, -0.8f, 0.4f, -1.5f, 1.7f, 3.0f, 2.2f, 3.0f, 2.1f, 3.0f, - -1.1f, 0.5f, -0.6f, 1.0f, -0.7f, 0.9f, 1.2f, -1.7f, 1.7f, -1.2f, 1.6f, -1.3f}, - {-0.1f, 2.5f, 1.2f, 0.8f, 0.4f, -1.5f, 1.7f, 3.0f, -0.6f, 1.0f, 1.6f, -1.3f}}; + {-0.1f, 2.6f, -0.7f, 2.8f, 0.7f, 3.0f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, + 1.0f, -0.8f, 0.4f, -0.6f, 1.8f, -0.2f, 1.4f, 3.0f, 0.8f, 3.0f, 2.2f, 3.0f, + -1.4f, 0.3f, -2.0f, 0.5f, -0.6f, 0.9f, 0.9f, -1.9f, 0.3f, -1.7f, 1.7f, -1.3f}, + {-0.1f, 2.6f, 0.5f, 1.0f, 1.8f, -0.2f, 1.4f, 3.0f, -2.0f, 0.5f, 1.7f, -1.3f}, + {-0.1f, 2.5f, 0.0f, 2.6f, -0.7f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, + 1.0f, -0.9f, 1.1f, -0.8f, 0.4f, -1.5f, 1.7f, 3.0f, 2.2f, 3.0f, 2.1f, 3.0f, + -1.1f, 0.5f, -0.6f, 1.0f, -0.7f, 0.9f, 1.2f, -1.7f, 1.7f, -1.2f, 1.6f, -1.3f}, + {-0.1f, 2.5f, 1.2f, 0.8f, 0.4f, -1.5f, 1.7f, 3.0f, -0.6f, 1.0f, 1.6f, -1.3f}}; float kQuantizedTolerance = GetTolerance(-3.f, 3.f); std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-3.f, 3.f); for (int i = 0; i < output_data.size(); i++) { Tensor input1_tensor = - makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); + makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); Tensor input2_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first, quant_param.second, test_data); Tensor output_tensor = - makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); + makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); AddParams params{}; params.activation = Activation::NONE; @@ -81,9 +81,9 @@ TEST(AddTest, Uint8) Tensor input1_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first, quant_param.second, test_data); Tensor input2_tensor = - makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); + makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); Tensor output_tensor = - makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); + makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); AddParams params{}; params.activation = Activation::NONE; @@ -103,14 +103,14 @@ TEST(AddTest, Float) Shape base_shape = {2, 3, 1, 2}; std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; std::vector<std::vector<float>> test_outputs = { - {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, - 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, - 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, - {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, - {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, - 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, - 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, - {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; + {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, + 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, + 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, + {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, + {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, + 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, + 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, + {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; @@ -128,7 +128,7 @@ TEST(AddTest, Float) kernel.execute(); EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + << "With shape number " << i; } // Re-run with exchanged inputs. for (size_t i = 0; i < test_shapes.size(); ++i) @@ -145,7 +145,7 @@ TEST(AddTest, Float) kernel.execute(); EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + << "With shape number " << i; } } @@ -154,26 +154,26 @@ TEST(AddTest, SInt16) Shape base_shape = {2, 3, 1, 2}; std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; std::vector<std::vector<int32_t>> ref_output_shapes{ - {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; std::vector<std::vector<float>> ref_outputs = { - {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, - 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, - 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, - {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, - {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, - 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, - 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, - {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; + {0.0f, 2.6f, 0.0f, 2.8f, 0.7f, 3.2f, 1.1f, 0.8f, 0.5f, 1.0f, 1.9f, 1.4f, + 1.0f, 0.0f, 0.4f, 0.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.8f, 3.3f, 2.2f, 3.7f, + 0.0f, 0.3f, 0.0f, 0.5f, 0.0f, 0.9f, 0.9f, 0.0f, 0.3f, 0.0f, 1.7f, 0.0f}, + {0.0f, 2.6f, 0.5f, 1.0f, 1.8f, 0.0f, 1.4f, 3.1f, 0.0f, 0.5f, 1.7f, 0.0f}, + {0.0f, 2.5f, 0.0f, 2.6f, 0.0f, 1.9f, 1.1f, 0.7f, 1.2f, 0.8f, 0.5f, 0.1f, + 1.0f, 0.0f, 1.1f, 0.0f, 0.4f, 0.0f, 1.7f, 3.3f, 2.2f, 3.8f, 2.1f, 3.7f, + 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.9f, 1.2f, 0.0f, 1.7f, 0.0f, 1.6f, 0.0f}, + {0.0f, 2.5f, 1.2f, 0.8f, 0.4f, 0.0f, 1.7f, 3.3f, 0.0f, 1.0f, 1.6f, 0.0f}}; for (size_t i = 0; i < test_shapes.size(); ++i) { Tensor input1_tensor = makeInputTensor<DataType::S16>(base_shape, 3.0 / 32767, 0, input1_data); Tensor input2_tensor = - makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0, input2_data); + makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0, input2_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0); const float tolerance = output_tensor.scale(); @@ -186,15 +186,15 @@ TEST(AddTest, SInt16) EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; + << "With shape number " << i; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + << "With shape number " << i; } // Re-run with exchanged inputs and different scales. for (size_t i = 0; i < test_shapes.size(); ++i) { Tensor input1_tensor = - makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0, input2_data); + makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0, input2_data); Tensor input2_tensor = makeInputTensor<DataType::S16>(base_shape, 4.0 / 32767, 0, input1_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 5.0 / 32767, 0); const float tolerance = output_tensor.scale(); @@ -208,9 +208,9 @@ TEST(AddTest, SInt16) EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; + << "With shape number " << i; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + << "With shape number " << i; } } diff --git a/compiler/luci-interpreter/src/kernels/ArgMax.cpp b/compiler/luci-interpreter/src/kernels/ArgMax.cpp index 5c464ed09..2437d5762 100644 --- a/compiler/luci-interpreter/src/kernels/ArgMax.cpp +++ b/compiler/luci-interpreter/src/kernels/ArgMax.cpp @@ -24,7 +24,7 @@ namespace kernels { ArgMax::ArgMax(const Tensor *input, const Tensor *axis, Tensor *output, const ArgMaxParams ¶ms) - : KernelWithParams<ArgMaxParams>({input, axis}, {output}, params) + : KernelWithParams<ArgMaxParams>({input, axis}, {output}, params) { } @@ -60,11 +60,10 @@ void ArgMax::configure() void ArgMax::execute() const { -#define TF_LITE_ARG_MAX(data_type, axis_type, output_type) \ - tflite::optimized_ops::ArgMinMax(getTensorShape(input()), getTensorData<data_type>(input()), \ - getTensorData<axis_type>(axis()), getTensorShape(output()), \ - getTensorData<output_type>(output()), \ - std::greater<data_type>()) +#define TF_LITE_ARG_MAX(data_type, axis_type, output_type) \ + tflite::optimized_ops::ArgMinMax( \ + getTensorShape(input()), getTensorData<data_type>(input()), getTensorData<axis_type>(axis()), \ + getTensorShape(output()), getTensorData<output_type>(output()), std::greater<data_type>()) if (axis()->element_type() == DataType::S32) { switch (_params.output_type) diff --git a/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp b/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp index c6734a114..3362edbf6 100644 --- a/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp +++ b/compiler/luci-interpreter/src/kernels/ArgMax.test.cpp @@ -60,14 +60,14 @@ TYPED_TEST(ArgMaxTest, Simple) /*output_shape=*/{1, 1, 1}, /*input_data=*/ { - 1, 9, 7, 3, + 1, 9, 7, 3, // }, /*dimension_data=*/{3}, /*output_data=*/{1}); Check<TypeParam, int64_t>(/*input_shape=*/{1, 1, 1, 4}, /*dimension_shape=*/{}, /*output_shape=*/{1, 1, 1}, /*input_data=*/ { - 1, 9, 7, 3, + 1, 9, 7, 3, // }, /*dimension_data=*/{3}, /*output_data=*/{1}); } @@ -78,14 +78,16 @@ TYPED_TEST(ArgMaxTest, MultiDimensions) /*output_shape=*/{1, 1, 2}, /*input_data=*/ { - 1, 2, 7, 8, 1, 9, 7, 3, + 1, 2, 7, 8, // + 1, 9, 7, 3, // }, /*dimension_data=*/{3}, /*output_data=*/{3, 1}); Check<TypeParam, int64_t>(/*input_shape=*/{1, 1, 2, 4}, /*dimension_shape=*/{}, /*output_shape=*/{1, 1, 2}, /*input_data=*/ { - 1, 2, 7, 8, 1, 9, 7, 3, + 1, 2, 7, 8, // + 1, 9, 7, 3, // }, /*dimension_data=*/{3}, /*output_data=*/{3, 1}); } @@ -93,7 +95,8 @@ TYPED_TEST(ArgMaxTest, MultiDimensions) TEST(ArgMaxTest, UnsupportedType_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({1, 1, 2, 4}, { - 1, 2, 7, 8, 1, 9, 7, 3, + 1, 2, 7, 8, // + 1, 9, 7, 3, // }); Tensor dimension_tensor = makeInputTensor<DataType::S32>({}, {3}); Tensor output_tensor = makeOutputTensor(DataType::U8); diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp b/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp index df54f9786..65ea4c09e 100644 --- a/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp +++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.cpp @@ -30,7 +30,7 @@ namespace kernels { AveragePool2D::AveragePool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms) - : KernelWithParams<Pool2DParams>({input}, {output}, params) + : KernelWithParams<Pool2DParams>({input}, {output}, params) { } @@ -51,15 +51,15 @@ void AveragePool2D::configure() const int32_t input_width = input_shape.dim(2); const int32_t depth = input_shape.dim(3); - const int32_t output_height = computeOutputSize(_params.padding, input_height, - _params.filter_height, _params.stride_height); + const int32_t output_height = + computeOutputSize(_params.padding, input_height, _params.filter_height, _params.stride_height); const int32_t output_width = - computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width); + computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width); _padding_height = - computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height); + computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height); _padding_width = - computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width); + computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width); if (input()->element_type() == DataType::U8) { LUCI_INTERPRETER_CHECK(std::abs(output()->scale() - input()->scale()) <= 1.0e-6); @@ -149,8 +149,8 @@ void AveragePool2D::evalSInt16() const params.quantized_activation_max = activation_max; tflite::reference_integer_ops::AveragePool( - params, getTensorShape(input()), getTensorData<int16_t>(input()), // - getTensorShape(output()), getTensorData<int16_t>(output())); + params, getTensorShape(input()), getTensorData<int16_t>(input()), // + getTensorShape(output()), getTensorData<int16_t>(output())); } } // namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp b/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp index 83e48c89d..4d7dab86a 100644 --- a/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp +++ b/compiler/luci-interpreter/src/kernels/AveragePool2D.test.cpp @@ -30,9 +30,9 @@ TEST(AveragePool2DTest, Float) { Shape input_shape{1, 3, 5, 1}; std::vector<float> input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -50,8 +50,8 @@ TEST(AveragePool2DTest, Float) kernel.execute(); std::vector<float> ref_output_data{ - 0, 1.5, // - 4.5, 6, // + 0, 1.5, // + 4.5, 6, // }; EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 2, 1})); @@ -60,12 +60,12 @@ TEST(AveragePool2DTest, Float) TEST(AveragePool2DTest, Uint8_0) { std::vector<float> input_data{ - 0, -6, 12, 4, // - -3, -2, 10, 7, // + 0, -6, 12, 4, // + -3, -2, 10, 7, // }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375f, 15.9375f); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, - quant_param.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); Pool2DParams params{}; @@ -87,13 +87,13 @@ TEST(AveragePool2DTest, Uint8_0) TEST(AveragePool2DTest, Uint8_1) { std::vector<float> input_data{ - 0, 6, 12, 4, // - 3, 2, 10, 7, // + 0, 6, 12, 4, // + 3, 2, 10, 7, // }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375f, 15.9375f); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, - quant_param.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); Pool2DParams params{}; @@ -117,13 +117,13 @@ TEST(AveragePool2DTest, SInt16) Shape input_shape{1, 3, 5, 1}; std::vector<int32_t> ref_output_shape{1, 2, 2, 1}; std::vector<float> input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // }; std::vector<float> ref_output_data{ - 0, 1.5, // - 4.5, 6, // + 0, 1.5, // + 4.5, 6, // }; Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.5, 0, input_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); @@ -148,9 +148,9 @@ TEST(AveragePool2DTest, Invalid_Input_Shape_NEG) { Shape input_shape{1, 3, 5}; std::vector<float> input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -171,9 +171,9 @@ TEST(AveragePool2DTest, In_Out_Type_NEG) { Shape input_shape{1, 3, 5, 1}; std::vector<float> input_data{ - -4, -3, -2, -1, 0, // - 1, 2, 3, 4, 5, // - 6, 7, 8, 9, 10, // + -4, -3, -2, -1, 0, // + 1, 2, 3, 4, 5, // + 6, 7, 8, 9, 10, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8); @@ -193,8 +193,8 @@ TEST(AveragePool2DTest, In_Out_Type_NEG) TEST(AveragePool2DTest, Quant_Param_NEG) { std::vector<float> input_data{ - 0, -6, 12, 4, // - -3, -2, 10, 7, // + 0, -6, 12, 4, // + -3, -2, 10, 7, // }; std::pair<float, int32_t> quant_param1 = quantizationParams<uint8_t>(-15.9375f, 15.9375f); diff --git a/compiler/luci-interpreter/src/kernels/BatchToSpaceND.cpp b/compiler/luci-interpreter/src/kernels/BatchToSpaceND.cpp new file mode 100644 index 000000000..591fcc00a --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/BatchToSpaceND.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. 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 "kernels/BatchToSpaceND.h" +#include "kernels/Utils.h" + +#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h> + +#include <stdexcept> + +namespace luci_interpreter +{ + +namespace kernels +{ + +namespace +{ +const int kInputMinDimensionNum = 3; +const int kInputMaxDimensionNum = 4; +} // namespace + +BatchToSpaceND::BatchToSpaceND(const Tensor *input, const Tensor *block_shape, const Tensor *crops, + Tensor *output) + : Kernel({input, block_shape, crops}, {output}) +{ +} + +void BatchToSpaceND::configure() +{ + + const auto *block_shape_data = block_shape()->data<int32_t>(); + const auto *crops_data = crops()->data<int32_t>(); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() >= kInputMinDimensionNum); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() <= kInputMaxDimensionNum); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + int spatial_dims_num = input()->shape().num_dims() - 2; + + LUCI_INTERPRETER_CHECK(block_shape()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(block_shape()->shape().dim(0) == spatial_dims_num); + + LUCI_INTERPRETER_CHECK(crops()->shape().num_dims() == 2); + LUCI_INTERPRETER_CHECK(crops()->shape().dim(0) == spatial_dims_num); + LUCI_INTERPRETER_CHECK(crops()->shape().dim(1) == 2); + for (int i = 0; i < spatial_dims_num * 2; ++i) + { + LUCI_INTERPRETER_CHECK(crops_data[i] >= 0); + } + + Shape output_shape = Shape(input()->shape().num_dims()); + int output_batch_size = input()->shape().dim(0); + for (int i = 0; i < spatial_dims_num; ++i) + { + LUCI_INTERPRETER_CHECK(output_batch_size % block_shape_data[i] == 0); + output_batch_size = output_batch_size / block_shape_data[i]; + output_shape.dim(i + 1) = + input()->shape().dim(i + 1) * block_shape_data[i] - crops_data[i * 2] - crops_data[i * 2 + 1]; + } + + output_shape.dim(0) = output_batch_size; + output_shape.dim(input()->shape().num_dims() - 1) = + input()->shape().dim(input()->shape().num_dims() - 1); + output()->resize(output_shape); +} + +void BatchToSpaceND::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + tflite::optimized_ops::BatchToSpaceND( + getTensorShape(input()), getTensorData<float>(input()), getTensorShape(block_shape()), + getTensorData<int32_t>(block_shape()), getTensorShape(crops()), + getTensorData<int32_t>(crops()), getTensorShape(output()), getTensorData<float>(output())); + break; + case DataType::U8: + tflite::optimized_ops::BatchToSpaceND( + getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(block_shape()), + getTensorData<int32_t>(block_shape()), getTensorShape(crops()), + getTensorData<int32_t>(crops()), getTensorShape(output()), + getTensorData<uint8_t>(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/BatchToSpaceND.h b/compiler/luci-interpreter/src/kernels/BatchToSpaceND.h new file mode 100644 index 000000000..57703ea5d --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/BatchToSpaceND.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 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 LUCI_INTERPRETER_KERNELS_BATCHTOSPACEND_H +#define LUCI_INTERPRETER_KERNELS_BATCHTOSPACEND_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class BatchToSpaceND : public Kernel +{ +public: + BatchToSpaceND(const Tensor *input, const Tensor *block_shape, const Tensor *crops, + Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *block_shape() const { return _inputs[1]; } + const Tensor *crops() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_BATCHTOSPACEND_H diff --git a/compiler/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp b/compiler/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp new file mode 100644 index 000000000..a29981d17 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/BatchToSpaceND.test.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 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 "kernels/BatchToSpaceND.h" +#include "kernels/TestUtils.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template <typename T> +void Check(std::initializer_list<int32_t> input_shape, + std::initializer_list<int32_t> block_shape_shape, + std::initializer_list<int32_t> crops_shape, std::initializer_list<int32_t> output_shape, + std::initializer_list<T> input_data, std::initializer_list<int32_t> block_shape_data, + std::initializer_list<int32_t> crops_data, std::initializer_list<T> output_data) +{ + constexpr DataType element_type = getElementType<T>(); + Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data); + Tensor block_shape_tensor = makeInputTensor<DataType::S32>(block_shape_shape, block_shape_data); + Tensor crops_tensor = makeInputTensor<DataType::S32>(crops_shape, crops_data); + Tensor output_tensor = makeOutputTensor(element_type); + + BatchToSpaceND kernel(&input_tensor, &block_shape_tensor, &crops_tensor, &output_tensor); + kernel.configure(); + kernel.execute(); + + EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template <typename T> class BatchToSpaceNDTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types<float, uint8_t>; +TYPED_TEST_CASE(BatchToSpaceNDTest, DataTypes); + +TYPED_TEST(BatchToSpaceNDTest, Simple) +{ + Check<TypeParam>(/*input_shape=*/{4, 2, 2, 1}, /*block_shape_shape=*/{2}, /*crops_shape=*/{2, 2}, + /*output_shape=*/{1, 4, 4, 1}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + /*block_shape_data=*/{2, 2}, /*crops_data=*/{0, 0, 0, 0}, + /*output_data=*/{1, 5, 2, 6, 9, 13, 10, 14, 3, 7, 4, 8, 11, 15, 12, 16}); +} + +TEST(BatchToSpaceNDTest, Invalid_Shape_NEG) +{ + Tensor input_tensor = + makeInputTensor<DataType::FLOAT32>({3, 2, 2, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}); + Tensor block_shape_tensor = makeInputTensor<DataType::S32>({2}, {2, 2}); + Tensor crops_tensor = makeInputTensor<DataType::S32>({2, 2}, {0, 0, 0, 0}); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + BatchToSpaceND kernel(&input_tensor, &block_shape_tensor, &crops_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(BatchToSpaceNDTest, Invalid_Crops_NEG) +{ + Tensor input_tensor = makeInputTensor<DataType::FLOAT32>( + {4, 2, 2, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}); + Tensor block_shape_tensor = makeInputTensor<DataType::S32>({2}, {2, 2}); + Tensor crops_tensor = makeInputTensor<DataType::S32>({2, 2}, {0, 0, -1, 0}); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + BatchToSpaceND kernel(&input_tensor, &block_shape_tensor, &crops_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h b/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h index 62bd4158e..2d2842a9e 100644 --- a/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h +++ b/compiler/luci-interpreter/src/kernels/BinaryOpCommon.h @@ -38,7 +38,7 @@ void BinaryOpBroadcastSlow(const tflite::RuntimeShape &unextended_input1_shape, if (unextended_input1_shape == unextended_input2_shape) { const int flat_size = tflite::MatchingElementsSize( - unextended_input1_shape, unextended_input2_shape, unextended_output_shape); + unextended_input1_shape, unextended_input2_shape, unextended_output_shape); for (int i = 0; i < flat_size; ++i) { output_data[i] = op(input1_data[i], input2_data[i]); @@ -60,8 +60,8 @@ void BinaryOpBroadcastSlow(const tflite::RuntimeShape &unextended_input1_shape, auto fn = [&](int indexes[N]) { output_data[SubscriptToIndex(output_desc, indexes)] = - op(input1_data[SubscriptToIndex(desc1, indexes)], - input2_data[SubscriptToIndex(desc2, indexes)]); + op(input1_data[SubscriptToIndex(desc1, indexes)], + input2_data[SubscriptToIndex(desc2, indexes)]); }; tflite::NDOpsHelper<N>(output_desc, fn); } diff --git a/compiler/luci-interpreter/src/kernels/CMakeLists.txt b/compiler/luci-interpreter/src/kernels/CMakeLists.txt index a07589dca..d7ab76374 100644 --- a/compiler/luci-interpreter/src/kernels/CMakeLists.txt +++ b/compiler/luci-interpreter/src/kernels/CMakeLists.txt @@ -1,5 +1,4 @@ find_package(Threads REQUIRED) -nnas_find_package(GTest REQUIRED) set(SOURCES Add.h @@ -8,6 +7,8 @@ set(SOURCES ArgMax.cpp AveragePool2D.h AveragePool2D.cpp + BatchToSpaceND.h + BatchToSpaceND.cpp Concatenation.h Concatenation.cpp Conv2D.h @@ -70,8 +71,12 @@ set(SOURCES Minimum.cpp Mul.h Mul.cpp + Neg.h + Neg.cpp NotEqual.h NotEqual.cpp + Pack.h + Pack.cpp Pad.h Pad.cpp Pow.h @@ -96,6 +101,8 @@ set(SOURCES Slice.cpp Softmax.h Softmax.cpp + SpaceToBatchND.h + SpaceToBatchND.cpp SpaceToDepth.h SpaceToDepth.cpp Split.h @@ -104,6 +111,8 @@ set(SOURCES StridedSlice.cpp Sqrt.h Sqrt.cpp + SquaredDifference.h + SquaredDifference.cpp Squeeze.h Squeeze.cpp Sub.h @@ -135,11 +144,17 @@ target_link_libraries(luci_interpreter_kernels PUBLIC luci_interpreter_core PRIVATE nncc_common Threads::Threads) +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) set(TEST_SOURCES Add.test.cpp ArgMax.test.cpp AveragePool2D.test.cpp + BatchToSpaceND.test.cpp Concatenation.test.cpp Conv2D.test.cpp DepthToSpace.test.cpp @@ -171,7 +186,9 @@ set(TEST_SOURCES Mean.test.cpp Minimum.test.cpp Mul.test.cpp + Neg.test.cpp NotEqual.test.cpp + Pack.test.cpp Pad.test.cpp Pow.test.cpp Prelu.test.cpp @@ -184,10 +201,12 @@ set(TEST_SOURCES Rsqrt.test.cpp Slice.test.cpp Softmax.test.cpp + SpaceToBatchND.test.cpp SpaceToDepth.test.cpp Split.test.cpp StridedSlice.test.cpp Sqrt.test.cpp + SquaredDifference.test.cpp Squeeze.test.cpp Sub.test.cpp Tanh.test.cpp diff --git a/compiler/luci-interpreter/src/kernels/Concatenation.cpp b/compiler/luci-interpreter/src/kernels/Concatenation.cpp index 6f8820446..e3376c13d 100644 --- a/compiler/luci-interpreter/src/kernels/Concatenation.cpp +++ b/compiler/luci-interpreter/src/kernels/Concatenation.cpp @@ -29,7 +29,7 @@ namespace kernels Concatenation::Concatenation(std::vector<const Tensor *> inputs, Tensor *output, const ConcatenationParams ¶ms) - : KernelWithParams<ConcatenationParams>(std::move(inputs), {output}, params) + : KernelWithParams<ConcatenationParams>(std::move(inputs), {output}, params) { } @@ -39,6 +39,9 @@ void Concatenation::configure() LUCI_INTERPRETER_CHECK(num_inputs > 0); const Tensor *t0 = _inputs[0]; + // TODO: Support concat with fused activation function + LUCI_INTERPRETER_CHECK(params().activation == luci::FusedActFunc::NONE); + int axis = _params.axis; if (axis < 0) axis += t0->shape().num_dims(); diff --git a/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp b/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp index 91707a256..ee9b7d0d3 100644 --- a/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Concatenation.test.cpp @@ -38,6 +38,7 @@ TEST(ConcatenationTest, Float) // Try different 'axis' and expect different results. { params.axis = 0; + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); kernel.configure(); @@ -48,6 +49,7 @@ TEST(ConcatenationTest, Float) } { params.axis = -2; // Same as '0'. + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); kernel.configure(); @@ -58,6 +60,7 @@ TEST(ConcatenationTest, Float) } { params.axis = 1; + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); kernel.configure(); @@ -68,6 +71,7 @@ TEST(ConcatenationTest, Float) } { params.axis = -1; // Same as '1'. + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); kernel.configure(); @@ -84,6 +88,7 @@ TEST(ConcatenationTest, Input_Number_Check_NEG) ConcatenationParams params{}; params.axis = -1; + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({}, &output_tensor, params); EXPECT_ANY_THROW(kernel.configure()); @@ -99,6 +104,7 @@ TEST(ConcatenationTest, Invalid_Axis_NEG) ConcatenationParams params{}; params.axis = -3; + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); EXPECT_ANY_THROW(kernel.configure()); @@ -114,6 +120,7 @@ TEST(ConcatenationTest, Mismatching_Input_Type_NEG) ConcatenationParams params{}; params.axis = -1; + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); EXPECT_ANY_THROW(kernel.configure()); @@ -129,6 +136,7 @@ TEST(ConcatenationTest, Mismatching_Input_Dimension_Num_NEG) ConcatenationParams params{}; params.axis = -1; + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); EXPECT_ANY_THROW(kernel.configure()); @@ -144,6 +152,7 @@ TEST(ConcatenationTest, Mismatching_Input_Dimension_NEG) ConcatenationParams params{}; params.axis = -1; + params.activation = luci::FusedActFunc::NONE; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); EXPECT_ANY_THROW(kernel.configure()); @@ -159,6 +168,24 @@ TEST(ConcatenationTest, Unsupported_Configure_Type_NEG) ConcatenationParams params{}; params.axis = -1; + params.activation = luci::FusedActFunc::NONE; + + Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); +} + +// TODO: Remove this test when concat w/ fused_activation is supported +TEST(ConcatenationTest, With_Fused_Activation_NEG) +{ + std::vector<float> input1_data{1, 2, 3, 4, 5, 6}; + std::vector<float> input2_data{7, 8, 9, 10, 11, 12}; + Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input1_data); + Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input2_data); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + ConcatenationParams params{}; + + params.axis = 1; + params.activation = luci::FusedActFunc::RELU; Concatenation kernel({&input1_tensor, &input2_tensor}, &output_tensor, params); EXPECT_ANY_THROW(kernel.configure()); diff --git a/compiler/luci-interpreter/src/kernels/Conv2D.cpp b/compiler/luci-interpreter/src/kernels/Conv2D.cpp index c5069e403..56ca96a34 100644 --- a/compiler/luci-interpreter/src/kernels/Conv2D.cpp +++ b/compiler/luci-interpreter/src/kernels/Conv2D.cpp @@ -31,7 +31,7 @@ namespace kernels Conv2D::Conv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output, const Conv2DParams ¶ms) - : KernelWithParams<Conv2DParams>({input, filter, bias}, {output}, params) + : KernelWithParams<Conv2DParams>({input, filter, bias}, {output}, params) { } @@ -84,11 +84,11 @@ void Conv2D::configure() bias()->shape().dim(0) == output_depth)); const int32_t output_height = - computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height, - _params.dilation_height_factor); + computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height, + _params.dilation_height_factor); const int32_t output_width = - computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width, - _params.dilation_width_factor); + computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width, + _params.dilation_width_factor); _padding_height = computePadding(_params.stride_height, _params.dilation_height_factor, input_height, filter_height, output_height); @@ -100,11 +100,11 @@ void Conv2D::configure() // Allocate tensor for Im2Col, if needed. // The checks here should be aligned with the actual implementation. const bool need_dilated_im2col = - _params.dilation_height_factor != 1 || _params.dilation_width_factor != 1; + _params.dilation_height_factor != 1 || _params.dilation_width_factor != 1; const bool need_non_dilated_im2col = _params.stride_height != 1 || _params.stride_width != 1 || filter_height != 1 || filter_width != 1; const bool need_im2col = - input()->element_type() != DataType::S16 && (need_dilated_im2col || need_non_dilated_im2col); + input()->element_type() != DataType::S16 && (need_dilated_im2col || need_non_dilated_im2col); if (need_im2col) { const int input_depth = input_shape.dim(3); @@ -113,7 +113,7 @@ void Conv2D::configure() try { _im2col = - std::make_unique<Tensor>(input()->element_type(), im2col_shape, AffineQuantization{}, ""); + std::make_unique<Tensor>(input()->element_type(), im2col_shape, AffineQuantization{}, ""); } catch (std::bad_alloc &ba) { @@ -174,16 +174,31 @@ void Conv2D::evalFloat() const params.float_activation_max = activation_max; if (_im2col) - tflite::optimized_ops::Conv(params, getTensorShape(input()), getTensorData<float>(input()), - getTensorShape(filter()), getTensorData<float>(filter()), - getTensorShape(bias()), getTensorData<float>(bias()), - getTensorShape(output()), getTensorData<float>(output()), - getTensorShape(_im2col.get()), getTensorData<float>(_im2col.get())); - else - tflite::reference_ops::Conv( + { + try + { + tflite::optimized_ops::Conv( + params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()), + getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()), + getTensorShape(output()), getTensorData<float>(output()), getTensorShape(_im2col.get()), + getTensorData<float>(_im2col.get())); + } + catch (std::bad_alloc &ba) + { + // Failed memory allocation + _im2col->deallocate(); + + tflite::reference_ops::Conv( params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()), getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()), getTensorShape(output()), getTensorData<float>(output()), tflite::RuntimeShape(), nullptr); + } + } + else + tflite::reference_ops::Conv( + params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()), + getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()), + getTensorShape(output()), getTensorData<float>(output()), tflite::RuntimeShape(), nullptr); } void Conv2D::evalQuantized() const @@ -223,10 +238,10 @@ void Conv2D::evalQuantized() const gemmlowp_context->set_max_num_threads(static_cast<int>(std::thread::hardware_concurrency())); tflite::optimized_ops::Conv( - params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(filter()), - getTensorData<uint8_t>(filter()), getTensorShape(bias()), getTensorData<int32_t>(bias()), - getTensorShape(output()), getTensorData<uint8_t>(output()), getTensorShape(_im2col.get()), - getTensorData<uint8_t>(_im2col.get()), gemmlowp_context.get()); + params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(filter()), + getTensorData<uint8_t>(filter()), getTensorShape(bias()), getTensorData<int32_t>(bias()), + getTensorShape(output()), getTensorData<uint8_t>(output()), getTensorShape(_im2col.get()), + getTensorData<uint8_t>(_im2col.get()), gemmlowp_context.get()); } void Conv2D::evalQuantizedPerChannel() const @@ -260,10 +275,10 @@ void Conv2D::evalQuantizedPerChannel() const calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); const std::vector<double> effective_output_scale = - getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); const std::vector<ChannelQuantMultipliers> multipliers_raw = - quantizeMultipliers(effective_output_scale); + quantizeMultipliers(effective_output_scale); BroadcastableWrapper<ChannelQuantMultipliers> quant_multipliers(multipliers_raw); for (int32_t batch = 0; batch < batches; ++batch) @@ -288,9 +303,9 @@ void Conv2D::evalQuantizedPerChannel() const for (int32_t in_c = 0; in_c < input_depth; ++in_c) { const uint8_t input_val = - input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; const uint8_t filter_val = - filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; acc += static_cast<int32_t>(input_val - input()->zero_point()) * static_cast<int32_t>(filter_val - filter()->zero_points()[out_c]); } @@ -303,7 +318,7 @@ void Conv2D::evalQuantizedPerChannel() const } int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( - acc, quant_multipliers[out_c].multiplier, quant_multipliers[out_c].shift); + acc, quant_multipliers[out_c].multiplier, quant_multipliers[out_c].shift); scaled_acc += output()->zero_point(); scaled_acc = std::max(scaled_acc, activation_min); @@ -346,10 +361,10 @@ void Conv2D::evalQuantizedS16() const calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); const std::vector<double> effective_output_scale = - getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); const std::vector<ChannelQuantMultipliers> multipliers_raw = - quantizeMultipliers(effective_output_scale); + quantizeMultipliers(effective_output_scale); BroadcastableWrapper<ChannelQuantMultipliers> multipliers(multipliers_raw); for (int32_t batch = 0; batch < batches; ++batch) @@ -374,9 +389,9 @@ void Conv2D::evalQuantizedS16() const for (int32_t in_c = 0; in_c < input_depth; ++in_c) { const int16_t input_val = - input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; const int16_t filter_val = - filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; acc += static_cast<int64_t>(input_val) * static_cast<int64_t>(filter_val); } } @@ -388,7 +403,7 @@ void Conv2D::evalQuantizedS16() const } int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( - acc, multipliers[out_c].multiplier, multipliers[out_c].shift); + acc, multipliers[out_c].multiplier, multipliers[out_c].shift); scaled_acc = std::max(scaled_acc, activation_min); scaled_acc = std::min(scaled_acc, activation_max); diff --git a/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp b/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp index 35a0c5491..8610a4fe6 100644 --- a/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Conv2D.test.cpp @@ -32,16 +32,16 @@ TEST(Conv2DTest, Float) Shape filter_shape{2, 2, 2, 2}; Shape bias_shape{2}; std::vector<float> input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 }; std::vector<float> filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 }; std::vector<float> bias_data{1, 2}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -62,8 +62,8 @@ TEST(Conv2DTest, Float) kernel.execute(); std::vector<float> ref_output_data{ - 11, 16, 7, 20, // row = 0 - 0, 40, 0, 44, // row = 1 + 11, 16, 7, 20, // row = 0 + 0, 40, 0, 44, // row = 1 }; std::vector<int32_t> ref_output_shape{1, 2, 2, 2}; EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); @@ -76,17 +76,17 @@ TEST(Conv2DTest, FloatCheck) Shape filter_shape{3, 2, 2, 1}; Shape bias_shape{3}; std::vector<float> input_data{ - // First batch - 1, 1, 1, 1, // row = 1 - 2, 2, 2, 2, // row = 2 - // Second batch - 1, 2, 3, 4, // row = 1 - 1, 2, 3, 4, // row = 2 + // First batch + 1, 1, 1, 1, // row = 1 + 2, 2, 2, 2, // row = 2 + // Second batch + 1, 2, 3, 4, // row = 1 + 1, 2, 3, 4, // row = 2 }; std::vector<float> filter_data{ - 1, 2, 3, 4, // first 2x2 filter - -1, 1, -1, 1, // second 2x2 filter - -1, -1, 1, 1, // third 2x2 filter + 1, 2, 3, 4, // first 2x2 filter + -1, 1, -1, 1, // second 2x2 filter + -1, -1, 1, 1, // third 2x2 filter }; std::vector<float> bias_data{1, 2, 3}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -107,10 +107,10 @@ TEST(Conv2DTest, FloatCheck) kernel.execute(); std::vector<float> ref_output_data{ - 18, 2, 5, // first batch, left - 18, 2, 5, // first batch, right - 17, 4, 3, // second batch, left - 37, 4, 3, // second batch, right + 18, 2, 5, // first batch, left + 18, 2, 5, // first batch, right + 17, 4, 3, // second batch, left + 37, 4, 3, // second batch, right }; std::vector<int32_t> ref_output_shape{2, 1, 2, 3}; EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); @@ -120,17 +120,17 @@ TEST(Conv2DTest, FloatCheck) TEST(Conv2DTest, Uint8) { std::vector<float> input_data{ - // First batch - 1, 1, 1, 1, // row = 1 - 2, 2, 2, 2, // row = 2 - // Second batch - 1, 2, 3, 4, // row = 1 - 1, 2, 3, 4, // row = 2 + // First batch + 1, 1, 1, 1, // row = 1 + 2, 2, 2, 2, // row = 2 + // Second batch + 1, 2, 3, 4, // row = 1 + 1, 2, 3, 4, // row = 2 }; std::vector<float> filter_data{ - 1, 2, 3, 4, // first 2x2 filter - -1, 1, -1, 1, // second 2x2 filter - -1, -1, 1, 1, // third 2x2 filter + 1, 2, 3, 4, // first 2x2 filter + -1, 1, -1, 1, // second 2x2 filter + -1, -1, 1, 1, // third 2x2 filter }; std::vector<float> bias_data{1, 2, 3}; @@ -142,9 +142,9 @@ TEST(Conv2DTest, Uint8) Tensor filter_tensor = makeInputTensor<DataType::U8>({3, 2, 2, 1}, input_quant_param.first, input_quant_param.second, filter_data); Tensor bias_tensor = makeInputTensor<DataType::S32>( - {3}, input_quant_param.first * input_quant_param.first, 0, bias_data); + {3}, input_quant_param.first * input_quant_param.first, 0, bias_data); Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); Conv2DParams params{}; params.padding = Padding::VALID; @@ -159,10 +159,10 @@ TEST(Conv2DTest, Uint8) kernel.execute(); std::vector<float> ref_output_data{ - 18, 2, 5, // first batch, left - 18, 2, 5, // first batch, right - 17, 4, 3, // second batch, left - 37, 4, 3, // second batch, right + 18, 2, 5, // first batch, left + 18, 2, 5, // first batch, right + 17, 4, 3, // second batch, left + 37, 4, 3, // second batch, right }; std::vector<int32_t> ref_output_shape{2, 1, 2, 3}; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); @@ -173,17 +173,17 @@ TEST(Conv2DTest, Uint8_CWQ) { const int output_channels = 3; std::vector<float> input_data{ - // First batch - 1, 1, 1, 1, // row = 1 - 2, 2, 2, 2, // row = 2 - // Second batch - 1, 2, 3, 4, // row = 1 - 1, 2, 3, 4, // row = 2 + // First batch + 1, 1, 1, 1, // row = 1 + 2, 2, 2, 2, // row = 2 + // Second batch + 1, 2, 3, 4, // row = 1 + 1, 2, 3, 4, // row = 2 }; std::vector<float> filter_data{ - 1, 2, 3, 4, // first 2x2 filter - -1, 1, -1, 1, // second 2x2 filter - -1, -1, 1, 1, // third 2x2 filter + 1, 2, 3, 4, // first 2x2 filter + -1, 1, -1, 1, // second 2x2 filter + -1, -1, 1, 1, // third 2x2 filter }; std::vector<float> bias_data{1, 2, 3}; Shape filter_shape{output_channels, 2, 2, 1}; @@ -212,11 +212,11 @@ TEST(Conv2DTest, Uint8_CWQ) Tensor input_tensor = makeInputTensor<DataType::U8>({2, 2, 4, 1}, input_quant_param.first, input_quant_param.second, input_data); Tensor filter_tensor = - makeInputTensor<DataType::U8>(filter_shape, filter_scales, filter_zerops, 0, filter_data); + makeInputTensor<DataType::U8>(filter_shape, filter_scales, filter_zerops, 0, filter_data); Tensor bias_tensor = - makeInputTensor<DataType::S32>({output_channels}, bias_scales, zerop, 0, bias_data); + makeInputTensor<DataType::S32>({output_channels}, bias_scales, zerop, 0, bias_data); Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); Conv2DParams params{}; params.padding = Padding::VALID; @@ -231,10 +231,10 @@ TEST(Conv2DTest, Uint8_CWQ) kernel.execute(); std::vector<float> ref_output_data{ - 18, 2, 5, // first batch, left - 18, 2, 5, // first batch, right - 17, 4, 3, // second batch, left - 37, 4, 3, // second batch, right + 18, 2, 5, // first batch, left + 18, 2, 5, // first batch, right + 17, 4, 3, // second batch, left + 37, 4, 3, // second batch, right }; std::vector<int32_t> ref_output_shape{2, 1, 2, 3}; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); @@ -249,21 +249,21 @@ TEST(Conv2DTest, SInt16) std::vector<int32_t> ref_output_shape{1, 2, 2, 2}; std::vector<float> input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 }; std::vector<float> filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 }; std::vector<float> bias_data{1, 2}; std::vector<float> ref_output_data{ - 11, 16, 7, 20, // row = 0 - 0, 40, 0, 44, // row = 1 + 11, 16, 7, 20, // row = 0 + 0, 40, 0, 44, // row = 1 }; Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.25, 0, input_data); @@ -295,22 +295,22 @@ TEST(Conv2DTest, SInt16_CWQ_weights) std::vector<int32_t> ref_output_shape{1, 2, 2, 3}; std::vector<float> input_data{ - 1, 2, // row = 0, col 0 - 3, 4, // row = 0, col 1 - 5, 6, // row = 1, col 0 - 7, 8, // row = 1, col 1 + 1, 2, // row = 0, col 0 + 3, 4, // row = 0, col 1 + 5, 6, // row = 1, col 0 + 7, 8, // row = 1, col 1 }; std::vector<float> filter_data{ - 4, -3, // out = 0 - 1, -3, // out = 1 - 5, -3, // out = 2 + 4, -3, // out = 0 + 1, -3, // out = 1 + 5, -3, // out = 2 }; std::vector<float> bias_data{1, 10, 5}; std::vector<float> ref_output_data{ - 0, 5, 4, // row 0, col 0 - 1, 1, 8, // row 0, col 1 - 3, 0, 12, // row 1, col 0 - 5, 0, 16, // row 1, col 1 + 0, 5, 4, // row 0, col 0 + 1, 1, 8, // row 0, col 1 + 3, 0, 12, // row 1, col 0 + 5, 0, 16, // row 1, col 1 }; float input_scale = 0.25f; @@ -323,7 +323,7 @@ TEST(Conv2DTest, SInt16_CWQ_weights) Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, input_scale, 0, input_data); Tensor filter_tensor = - makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 0, filter_data); + makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 0, filter_data); Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, bias_scales, zerop, 0, bias_data); Tensor output_tensor = makeOutputTensor(DataType::S16, output_scale, 0); @@ -349,16 +349,16 @@ TEST(Conv2DTest, Unsupported_Type_Configure_NEG) Shape filter_shape{2, 2, 2, 2}; Shape bias_shape{2}; std::vector<int32_t> input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 }; std::vector<float> filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 }; std::vector<float> bias_data{1, 2}; Tensor input_tensor = makeInputTensor<DataType::S32>(input_shape, input_data); @@ -384,16 +384,16 @@ TEST(Conv2DTest, Invalid_Bias_Type_NEG) Shape filter_shape{2, 2, 2, 2}; Shape bias_shape{2}; std::vector<float> input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 }; std::vector<float> filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 }; std::vector<uint8_t> bias_data{1, 2}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -419,16 +419,16 @@ TEST(Conv2DTest, Invalid_Bias_Data_NEG) Shape filter_shape{2, 2, 2, 2}; Shape bias_shape{3}; std::vector<float> input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 }; std::vector<float> filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 }; std::vector<float> bias_data{1, 2, 3}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -454,16 +454,16 @@ TEST(Conv2DTest, Invalid_Input_Shape_NEG) Shape filter_shape{2, 2, 2, 2}; Shape bias_shape{2}; std::vector<float> input_data{ - 1, 2, 3, 4, 5, 6, // row = 0 - 7, 8, 9, 10, 11, 12, // row = 1 - 13, 14, 15, 16, 17, 18, // row = 2 - 19, 20, 21, 22, 23, 24, // row = 3 + 1, 2, 3, 4, 5, 6, // row = 0 + 7, 8, 9, 10, 11, 12, // row = 1 + 13, 14, 15, 16, 17, 18, // row = 2 + 19, 20, 21, 22, 23, 24, // row = 3 }; std::vector<float> filter_data{ - 1, 2, -3, -4, // out = 0, row = 0 - -5, 6, -7, 8, // out = 1, row = 0 - 4, -2, 3, -1, // out = 0, row = 1 - -8, -6, 7, 5, // out = 1, row = 1 + 1, 2, -3, -4, // out = 0, row = 0 + -5, 6, -7, 8, // out = 1, row = 0 + 4, -2, 3, -1, // out = 0, row = 1 + -8, -6, 7, 5, // out = 1, row = 1 }; std::vector<float> bias_data{1, 2}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); diff --git a/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp b/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp index 57238313c..f2b9e4ccc 100644 --- a/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp +++ b/compiler/luci-interpreter/src/kernels/DepthToSpace.cpp @@ -24,7 +24,7 @@ namespace kernels { DepthToSpace::DepthToSpace(const Tensor *input, Tensor *output, const DepthToSpaceParams ¶ms) - : KernelWithParams<DepthToSpaceParams>({input}, {output}, params) + : KernelWithParams<DepthToSpaceParams>({input}, {output}, params) { } diff --git a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp index 921133191..1452f4421 100644 --- a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp +++ b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.cpp @@ -30,7 +30,7 @@ namespace kernels DepthwiseConv2D::DepthwiseConv2D(const Tensor *input, const Tensor *filter, const Tensor *bias, Tensor *output, const DepthwiseConv2DParams ¶ms) - : KernelWithParams<DepthwiseConv2DParams>({input, filter, bias}, {output}, params) + : KernelWithParams<DepthwiseConv2DParams>({input, filter, bias}, {output}, params) { } @@ -85,11 +85,11 @@ void DepthwiseConv2D::configure() bias()->shape().dim(0) == channels_out)); const int32_t output_height = - computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height, - _params.dilation_height_factor); + computeOutputSize(_params.padding, input_height, filter_height, _params.stride_height, + _params.dilation_height_factor); const int32_t output_width = - computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width, - _params.dilation_width_factor); + computeOutputSize(_params.padding, input_width, filter_width, _params.stride_width, + _params.dilation_width_factor); _padding_height = computePadding(_params.stride_height, _params.dilation_height_factor, input_height, filter_height, output_height); @@ -149,9 +149,9 @@ void DepthwiseConv2D::evalFloat() const params.float_activation_max = activation_max; tflite::reference_ops::DepthwiseConv( - params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()), - getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()), - getTensorShape(output()), getTensorData<float>(output())); + params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(filter()), + getTensorData<float>(filter()), getTensorShape(bias()), getTensorData<float>(bias()), + getTensorShape(output()), getTensorData<float>(output())); } void DepthwiseConv2D::evalQuantizedPerChannel() const @@ -185,10 +185,10 @@ void DepthwiseConv2D::evalQuantizedPerChannel() const calculateActivationRangeQuantized(_params.activation, output(), &activation_min, &activation_max); const std::vector<double> effective_output_scales = - getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); std::vector<ChannelQuantMultipliers> quant_multipliers_raw = - quantizeMultipliers(effective_output_scales); + quantizeMultipliers(effective_output_scales); BroadcastableWrapper<ChannelQuantMultipliers> quant_multipliers(quant_multipliers_raw); for (int batch = 0; batch < batches; ++batch) @@ -213,13 +213,13 @@ void DepthwiseConv2D::evalQuantizedPerChannel() const const int in_y = in_y_origin + dilation_height_factor * filter_y; // Zero padding by omitting the areas outside the image. const bool is_point_inside_image = - (in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height); + (in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height); if (is_point_inside_image) { int32 input_val = - input_data[calcOffset(input_shape, batch, in_y, in_x, in_channel)]; + input_data[calcOffset(input_shape, batch, in_y, in_x, in_channel)]; int32 filter_val = - filter_data[calcOffset(filter_shape, 0, filter_y, filter_x, output_channel)]; + filter_data[calcOffset(filter_shape, 0, filter_y, filter_x, output_channel)]; acc += (filter_val - filter()->zero_points()[output_channel]) * (input_val - input()->zero_point()); } @@ -232,12 +232,12 @@ void DepthwiseConv2D::evalQuantizedPerChannel() const int32_t output_multiplier = quant_multipliers[output_channel].multiplier; int output_shift = quant_multipliers[output_channel].shift; int32_t scaled_acc = - tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); scaled_acc += output()->zero_point(); scaled_acc = std::max(scaled_acc, activation_min); scaled_acc = std::min(scaled_acc, activation_max); output_data[calcOffset(output_shape, batch, out_y, out_x, output_channel)] = - static_cast<uint8_t>(scaled_acc); + static_cast<uint8_t>(scaled_acc); } } } @@ -278,9 +278,9 @@ void DepthwiseConv2D::evalQuantized() const params.quantized_activation_max = activation_max; tflite::reference_ops::DepthwiseConv( - params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(filter()), - getTensorData<uint8_t>(filter()), getTensorShape(bias()), getTensorData<int32_t>(bias()), - getTensorShape(output()), getTensorData<uint8_t>(output())); + params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(filter()), + getTensorData<uint8_t>(filter()), getTensorShape(bias()), getTensorData<int32_t>(bias()), + getTensorShape(output()), getTensorData<uint8_t>(output())); } void DepthwiseConv2D::evalQuantizedS16() const @@ -310,10 +310,10 @@ void DepthwiseConv2D::evalQuantizedS16() const const int32_t depth_multiplier = _params.depth_multiplier; const std::vector<double> effective_output_scales = - getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); std::vector<ChannelQuantMultipliers> quant_multipliers_raw = - quantizeMultipliers(effective_output_scales); + quantizeMultipliers(effective_output_scales); BroadcastableWrapper<ChannelQuantMultipliers> quant_multipliers(quant_multipliers_raw); @@ -344,9 +344,9 @@ void DepthwiseConv2D::evalQuantizedS16() const if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width)) { const int16_t input_val = - input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; const int16_t filter_val = - filter_data[calcOffset(filter_shape, 0, filter_y, filter_x, out_c)]; + filter_data[calcOffset(filter_shape, 0, filter_y, filter_x, out_c)]; acc += static_cast<int64_t>(input_val) * static_cast<int64_t>(filter_val); } } @@ -359,7 +359,7 @@ void DepthwiseConv2D::evalQuantizedS16() const int32_t output_multiplier = quant_multipliers[out_c].multiplier; int output_shift = quant_multipliers[out_c].shift; int32_t scaled_acc = - tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); scaled_acc = std::max(scaled_acc, activation_min); scaled_acc = std::min(scaled_acc, activation_max); diff --git a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp index f79e888a1..3e2f434dd 100644 --- a/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp +++ b/compiler/luci-interpreter/src/kernels/DepthwiseConv2D.test.cpp @@ -32,16 +32,16 @@ TEST(DepthwiseConv2DTest, Float) Shape filter_shape{1, 2, 2, 4}; Shape bias_shape{4}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -63,8 +63,8 @@ TEST(DepthwiseConv2DTest, Float) kernel.execute(); std::vector<float> ref_output_data{ - 71, 0, 99, 0, // - 167, 0, 227, 28, // + 71, 0, 99, 0, // + 167, 0, 227, 28, // }; EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4})); @@ -73,15 +73,15 @@ TEST(DepthwiseConv2DTest, Float) TEST(DepthwiseConv2DTest, Uint8) { std::vector<float> input_data{ - 1, 2, 7, 8, // column 1 - 3, 4, 9, 10, // column 2 - 5, 6, 11, 12, // column 3 + 1, 2, 7, 8, // column 1 + 3, 4, 9, 10, // column 2 + 5, 6, 11, 12, // column 3 }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; @@ -93,9 +93,9 @@ TEST(DepthwiseConv2DTest, Uint8) Tensor filter_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 4}, input_quant_param.first, input_quant_param.second, filter_data); Tensor bias_tensor = makeInputTensor<DataType::S32>( - {4}, input_quant_param.first * input_quant_param.first, 0, bias_data); + {4}, input_quant_param.first * input_quant_param.first, 0, bias_data); Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); DepthwiseConv2DParams params{}; params.padding = Padding::VALID; @@ -111,8 +111,8 @@ TEST(DepthwiseConv2DTest, Uint8) kernel.execute(); std::vector<float> ref_output_data{ - 71, -34, 99, -20, // - 91, -26, 127, -4, // + 71, -34, 99, -20, // + 91, -26, 127, -4, // }; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 1, 4})); @@ -126,21 +126,21 @@ TEST(DepthwiseConv2DTest, SInt16) std::vector<int32_t> ref_output_shape{1, 2, 1, 4}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; std::vector<float> ref_output_data{ - 71, 0, 99, 0, // - 167, 0, 227, 28, // + 71, 0, 99, 0, // + 167, 0, 227, 28, // }; Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.25, 0, input_data); @@ -174,21 +174,21 @@ TEST(DepthwiseConv2DTest, SInt16_CWQ_weights) std::vector<int32_t> ref_output_shape{1, 2, 1, output_channels}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; std::vector<float> ref_output_data{ - 71, 0, 99, 0, // - 167, 0, 227, 28, // + 71, 0, 99, 0, // + 167, 0, 227, 28, // }; float input_scale = 0.25; @@ -199,7 +199,7 @@ TEST(DepthwiseConv2DTest, SInt16_CWQ_weights) std::vector<int32_t> zerop(4, 0); Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, input_scale, 0, input_data); Tensor filter_tensor = - makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 3, filter_data); + makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 3, filter_data); Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, bias_scales, zerop, 0, bias_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0); @@ -229,20 +229,20 @@ TEST(DepthwiseConv2DTest, Uint8_CWQ_weights) std::vector<int32_t> ref_output_shape{1, 2, 1, output_channels}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; std::vector<float> ref_output_data{ - 71, -34, 99, -20, // - 91, -26, 127, -4, // + 71, -34, 99, -20, // + 91, -26, 127, -4, // }; std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(0, 16); @@ -270,10 +270,10 @@ TEST(DepthwiseConv2DTest, Uint8_CWQ_weights) Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first, input_quant_param.second, input_data); Tensor filter_tensor = - makeInputTensor<DataType::U8>(filter_shape, filter_scales, filter_zerops, 3, filter_data); + makeInputTensor<DataType::U8>(filter_shape, filter_scales, filter_zerops, 3, filter_data); Tensor bias_tensor = makeInputTensor<DataType::S32>(bias_shape, bias_scales, zerop, 0, bias_data); Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); DepthwiseConv2DParams params{}; params.padding = Padding::VALID; @@ -299,16 +299,16 @@ TEST(DepthwiseConv2DTest, InvalidBiasType_NEG) Shape filter_shape{1, 2, 2, 4}; Shape bias_shape{4}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<int32_t> bias_data{1, 2, 3, 4}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -335,16 +335,16 @@ TEST(DepthwiseConv2DTest, InOutTypeMismatch_NEG) Shape filter_shape{1, 2, 2, 4}; Shape bias_shape{4}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -371,16 +371,16 @@ TEST(DepthwiseConv2DTest, InvalidInputShape_NEG) Shape filter_shape{2, 2, 4}; Shape bias_shape{4}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -407,16 +407,16 @@ TEST(DepthwiseConv2DTest, InvalidFilterShape_NEG) Shape filter_shape{2, 1, 2, 4}; Shape bias_shape{4}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); @@ -443,16 +443,16 @@ TEST(DepthwiseConv2DTest, InvalidBiasDim_NEG) Shape filter_shape{1, 2, 4, 2}; Shape bias_shape{4}; std::vector<float> input_data{ - 1, 2, 7, 8, // - 3, 4, 9, 10, // - 5, 6, 11, 12, // - 13, 14, 15, 16, // + 1, 2, 7, 8, // + 3, 4, 9, 10, // + 5, 6, 11, 12, // + 13, 14, 15, 16, // }; std::vector<float> filter_data{ - 1, 2, 3, 4, // - -9, 10, -11, 12, // - 5, 6, 7, 8, // - 13, -14, 15, -16, // + 1, 2, 3, 4, // + -9, 10, -11, 12, // + 5, 6, 7, 8, // + 13, -14, 15, -16, // }; std::vector<float> bias_data{1, 2, 3, 4}; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); diff --git a/compiler/luci-interpreter/src/kernels/Div.cpp b/compiler/luci-interpreter/src/kernels/Div.cpp index e75876b3a..db1496d37 100644 --- a/compiler/luci-interpreter/src/kernels/Div.cpp +++ b/compiler/luci-interpreter/src/kernels/Div.cpp @@ -26,7 +26,7 @@ namespace kernels { Div::Div(const Tensor *input1, const Tensor *input2, Tensor *output, const DivParams ¶ms) - : KernelWithParams<DivParams>({input1, input2}, {output}, params) + : KernelWithParams<DivParams>({input1, input2}, {output}, params) { } @@ -63,13 +63,13 @@ void Div::evalFloat() const params.float_activation_min = activation_min; params.float_activation_max = activation_max; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { tflite::reference_ops::BroadcastDivSlow( - params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), - getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); + params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), + getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); } else { @@ -107,14 +107,13 @@ void Div::evalQuantized() const params.quantized_activation_max = activation_max; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { tflite::reference_ops::BroadcastDivSlow( - params, getTensorShape(input1()), getTensorData<uint8_t>(input1()), - getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + params, getTensorShape(input1()), getTensorData<uint8_t>(input1()), getTensorShape(input2()), + getTensorData<uint8_t>(input2()), getTensorShape(output()), getTensorData<uint8_t>(output())); } else { diff --git a/compiler/luci-interpreter/src/kernels/Div.test.cpp b/compiler/luci-interpreter/src/kernels/Div.test.cpp index 77eb2e9c1..1a0c4af15 100644 --- a/compiler/luci-interpreter/src/kernels/Div.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Div.test.cpp @@ -99,12 +99,12 @@ TEST(DivTest, Uint8) std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.f, 1.f); Tensor input1_tensor = - makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input1_data); + makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input1_data); Tensor input2_tensor = - makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input2_data); + makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, input2_data); Tensor output_tensor = - makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); + makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); DivParams params{}; params.activation = Activation::RELU; diff --git a/compiler/luci-interpreter/src/kernels/Elu.test.cpp b/compiler/luci-interpreter/src/kernels/Elu.test.cpp index 0235d6552..e26eed03e 100644 --- a/compiler/luci-interpreter/src/kernels/Elu.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Elu.test.cpp @@ -43,25 +43,25 @@ void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int TEST(EluTest, SimpleElu) { Check( - /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, - /*input_data=*/ - { - 0, -6, 2, -4, // - 3, -2, 10, -0.1, // - }, - /*output_data=*/ - { - 0.0, -0.997521, 2.0, -0.981684, // - 3.0, -0.864665, 10.0, -0.0951626, // - }); + /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, + /*input_data=*/ + { + 0, -6, 2, -4, // + 3, -2, 10, -0.1, // + }, + /*output_data=*/ + { + 0.0, -0.997521, 2.0, -0.981684, // + 3.0, -0.864665, 10.0, -0.0951626, // + }); } TEST(EluTest, InOutTypeMismatch_NEG) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - 0, -6, 2, -4, // - 3, -2, 10, -0.1, // + 0, -6, 2, -4, // + 3, -2, 10, -0.1, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8); diff --git a/compiler/luci-interpreter/src/kernels/Equal.test.cpp b/compiler/luci-interpreter/src/kernels/Equal.test.cpp index fb0de8bbf..ba2827ba9 100644 --- a/compiler/luci-interpreter/src/kernels/Equal.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Equal.test.cpp @@ -30,18 +30,18 @@ using namespace testing; TEST(EqualTest, FloatSimple) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, true, false, // Row 1 - false, true, false, // Row 2 + false, true, false, // Row 1 + false, true, false, // Row 2 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data); @@ -59,21 +59,21 @@ TEST(EqualTest, FloatSimple) TEST(EqualTest, FloatBroardcast) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - 0.9, 0.7, 0.5, // Row 4 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + 0.9, 0.7, 0.5, // Row 4 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 + 0.9, 0.7, 0.5, // Row 1 }; std::vector<bool> ref_output_data{ - false, true, false, // Row 1 - false, false, false, // Row 2 - false, false, false, // Row 3 - true, true, true, // Row 4 + false, true, false, // Row 1 + false, false, false, // Row 2 + false, false, false, // Row 3 + true, true, true, // Row 4 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({4, 3}, x_data); @@ -95,27 +95,27 @@ const float F_MAX = 127.0 / 128.0; TEST(EqualTest, Uint8Quantized) { std::vector<float> x_data{ - 0.5, 0.5, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.5, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.5, 0.55, 0.5, // Row 1 - -1, 0, 0.05, 1, // Row 2 + 0.9, 0.5, 0.55, 0.5, // Row 1 + -1, 0, 0.05, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, true, false, false, // Row 1 - false, true, true, false, // Row 2 + false, true, false, false, // Row 1 + false, true, true, false, // Row 2 }; std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, - x_quant_param.second, x_data); + Tensor x_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data); std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 2); - Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, - y_quant_param.second, y_data); + Tensor y_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); @@ -130,28 +130,28 @@ TEST(EqualTest, Uint8Quantized) TEST(EqualTest, Uint8QuantizedBroadcast) { std::vector<float> x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - -1, 0.05, 0, 1, // Row 4 + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + -1, 0.05, 0, 1, // Row 4 }; std::vector<float> y_data{ - -1, 0.05, 0, 1, // Row 1 + -1, 0.05, 0, 1, // Row 1 }; std::vector<bool> ref_output_data{ - false, false, false, false, // Row 1 - false, false, true, false, // Row 2 - false, false, false, false, // Row 3 - true, true, true, true, // Row 4 + false, false, false, false, // Row 1 + false, false, true, false, // Row 2 + false, false, false, false, // Row 3 + true, true, true, true, // Row 4 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); Equal kernel(&x_tensor, &y_tensor, &output_tensor); diff --git a/compiler/luci-interpreter/src/kernels/Floor.test.cpp b/compiler/luci-interpreter/src/kernels/Floor.test.cpp index 3e1ab6f3a..d90d611d9 100644 --- a/compiler/luci-interpreter/src/kernels/Floor.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Floor.test.cpp @@ -30,14 +30,14 @@ TEST(FloorTest, SimpleFloat) { std::initializer_list<int32_t> input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - 0.2, 8.6, 2.4, 4.3, // Row 1 - 3, 7.1, 10.5, -0.9, // Row 2 + 0.2, 8.6, 2.4, 4.3, // Row 1 + 3, 7.1, 10.5, -0.9, // Row 2 }; std::initializer_list<int32_t> ref_output_shape{1, 2, 4, 1}; std::vector<float> ref_output_data{ - 0, 8, 2, 4, // Row 1 - 3, 7, 10, -1, // Row 2 + 0, 8, 2, 4, // Row 1 + 3, 7, 10, -1, // Row 2 }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.cpp b/compiler/luci-interpreter/src/kernels/FloorDiv.cpp index b6f36cea3..a7a10a336 100644 --- a/compiler/luci-interpreter/src/kernels/FloorDiv.cpp +++ b/compiler/luci-interpreter/src/kernels/FloorDiv.cpp @@ -28,7 +28,7 @@ namespace kernels { FloorDiv::FloorDiv(const Tensor *input, const Tensor *alpha, Tensor *output) - : Kernel({input, alpha}, {output}) + : Kernel({input, alpha}, {output}) { } @@ -70,14 +70,14 @@ void FloorDiv::evalFloat() const if (x()->shape() != y()->shape()) { tflite::reference_ops::BroadcastBinaryFunction4DSlow<float, float, float>( - getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), - getTensorData<float>(output()), FloorDivFunc); + getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + getTensorData<float>(output()), FloorDivFunc); } else { tflite::reference_ops::BinaryFunction<float, float, float>( - getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), - getTensorData<float>(output()), FloorDivFunc); + getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + getTensorData<float>(output()), FloorDivFunc); } } diff --git a/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp b/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp index a5bc700f7..16831ca80 100644 --- a/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp +++ b/compiler/luci-interpreter/src/kernels/FloorDiv.test.cpp @@ -31,20 +31,20 @@ TEST(FloorDivTest, FloatSimple) { Shape x_shape{2, 3}; std::vector<float> x_data{ - 0.5, 2.4, 3.1, // Row 1 - 1.9, -1.9, -2.8, // Row 2 + 0.5, 2.4, 3.1, // Row 1 + 1.9, -1.9, -2.8, // Row 2 }; Shape y_shape = x_shape; std::vector<float> y_data{ - 2.0, 0.5, 3.0, // Row 1 - 1.0, -1.0, -2.0, // Row 2 + 2.0, 0.5, 3.0, // Row 1 + 1.0, -1.0, -2.0, // Row 2 }; std::vector<int32_t> ref_output_shape{2, 3}; std::vector<float> ref_output_data{ - 0, 4, 1, // Row 1 - 1, 1, 1, // Row 2 + 0, 4, 1, // Row 1 + 1, 1, 1, // Row 2 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(x_shape, x_data); @@ -64,21 +64,21 @@ TEST(FloorDivTest, FloatBroadcast) { Shape x_shape{1, 3}; std::vector<float> x_data{ - 0.5, 2.4, -3.1, // Row 1 + 0.5, 2.4, -3.1, // Row 1 }; Shape y_shape{3, 3}; std::vector<float> y_data{ - 1.0, 1.0, 1.0, // Row 1 - 2.0, -0.5, -2.0, // Row 2 - 0.3, 0.7, 0.9, // Row 3 + 1.0, 1.0, 1.0, // Row 1 + 2.0, -0.5, -2.0, // Row 2 + 0.3, 0.7, 0.9, // Row 3 }; std::vector<int32_t> ref_output_shape{3, 3}; std::vector<float> ref_output_data{ - 0, 2, -4, // Row 1 - 0, -5, 1, // Row 2 - 1, 3, -4, // Row 3 + 0, 2, -4, // Row 1 + 0, -5, 1, // Row 2 + 1, 3, -4, // Row 3 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>(x_shape, x_data); diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.cpp b/compiler/luci-interpreter/src/kernels/FullyConnected.cpp index 7fa76d5e7..48433b42d 100644 --- a/compiler/luci-interpreter/src/kernels/FullyConnected.cpp +++ b/compiler/luci-interpreter/src/kernels/FullyConnected.cpp @@ -30,7 +30,7 @@ namespace kernels FullyConnected::FullyConnected(const Tensor *input, const Tensor *weights, const Tensor *bias, Tensor *output, const FullyConnectedParams ¶ms) - : KernelWithParams<FullyConnectedParams>({input, weights, bias}, {output}, params) + : KernelWithParams<FullyConnectedParams>({input, weights, bias}, {output}, params) { } @@ -97,9 +97,9 @@ void FullyConnected::evalFloat() const params.weights_format = tflite::FullyConnectedWeightsFormat::kDefault; tflite::reference_ops::FullyConnected( - params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(weights()), - getTensorData<float>(weights()), getTensorShape(bias()), getTensorData<float>(bias()), - getTensorShape(output()), getTensorData<float>(output())); + params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(weights()), + getTensorData<float>(weights()), getTensorShape(bias()), getTensorData<float>(bias()), + getTensorShape(output()), getTensorData<float>(output())); } void FullyConnected::evalQuantized() const @@ -110,7 +110,7 @@ void FullyConnected::evalQuantized() const int32_t output_activation_max; int32_t output_multiplier; real_multiplier = - getQuantizedConvolutionMultipler(input()->scale(), weights()->scale(), output()->scale()); + getQuantizedConvolutionMultipler(input()->scale(), weights()->scale(), output()->scale()); quantizeMultiplier(real_multiplier, &output_multiplier, &output_shift); calculateActivationRangeQuantized(params().activation, output(), &output_activation_min, &output_activation_max); @@ -130,9 +130,9 @@ void FullyConnected::evalQuantized() const op_params.lhs_cacheable = false; op_params.rhs_cacheable = false; tflite::reference_ops::FullyConnected( - op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), - getTensorShape(weights()), getTensorData<uint8_t>(weights()), getTensorShape(bias()), - getTensorData<int32_t>(bias()), getTensorShape(output()), getTensorData<uint8_t>(output())); + op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(weights()), + getTensorData<uint8_t>(weights()), getTensorShape(bias()), getTensorData<int32_t>(bias()), + getTensorShape(output()), getTensorData<uint8_t>(output())); } } // namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp b/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp index d194ce1a0..0259d3e1d 100644 --- a/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp +++ b/compiler/luci-interpreter/src/kernels/FullyConnected.test.cpp @@ -50,10 +50,10 @@ void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int template <> void Check<uint8_t>( - std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> weights_shape, - std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape, - std::initializer_list<float> input_data, std::initializer_list<float> weights_data, - std::initializer_list<float> bias_data, std::initializer_list<float> output_data) + std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> weights_shape, + std::initializer_list<int32_t> bias_shape, std::initializer_list<int32_t> output_shape, + std::initializer_list<float> input_data, std::initializer_list<float> weights_data, + std::initializer_list<float> bias_data, std::initializer_list<float> output_data) { const float quantized_tolerance = getTolerance(-127, 128, 255); std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64); @@ -63,9 +63,9 @@ void Check<uint8_t>( Tensor weights_tensor = makeInputTensor<DataType::U8>(weights_shape, input_quant_param.first, input_quant_param.second, weights_data); Tensor bias_tensor = makeInputTensor<DataType::S32>( - bias_shape, input_quant_param.first * input_quant_param.first, 0, bias_data); + bias_shape, input_quant_param.first * input_quant_param.first, 0, bias_data); Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); FullyConnectedParams params{}; params.activation = Activation::RELU; @@ -90,32 +90,33 @@ TYPED_TEST(FullyConnectedTest, Simple) { Check<TypeParam>({3, 2, 2, 1}, {3, 6}, {3}, {2, 3}, { - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 }, { - -3, -7, 4, -4, -6, 4, // unit = 0 - 3, 5, 2, 3, -3, -8, // unit = 1 - -3, 7, 4, 9, 0, -5, // unit = 2 + -3, -7, 4, -4, -6, 4, // unit = 0 + 3, 5, 2, 3, -3, -8, // unit = 1 + -3, 7, 4, 9, 0, -5, // unit = 2 }, - {-1, -5, -8}, { - 0, 0, 32, // batch = 0 - 22, 11, 47, // batch = 1 - }); + {-1, -5, -8}, + { + 0, 0, 32, // batch = 0 + 22, 11, 47, // batch = 1 + }); } TEST(FullyConnectedTest, InvalidBiasType_NEG) { Shape input_shape{3, 2, 2, 1}; std::vector<float> input_data{ - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 }; Shape weights_shape{3, 6}; std::vector<float> weights_data{ - -3, -7, 4, -4, -6, 4, // unit = 0 - 3, 5, 2, 3, -3, -8, // unit = 1 - -3, 7, 4, 9, 0, -5, // unit = 2 + -3, -7, 4, -4, -6, 4, // unit = 0 + 3, 5, 2, 3, -3, -8, // unit = 1 + -3, 7, 4, 9, 0, -5, // unit = 2 }; Shape bias_shape{3}; std::vector<int32_t> bias_data{-1, -5, -8}; @@ -136,14 +137,14 @@ TEST(FullyConnectedTest, InvalidWeightShapeDim_NEG) { Shape input_shape{3, 2, 2, 1}; std::vector<float> input_data{ - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 }; Shape weights_shape{1, 3, 6}; std::vector<float> weights_data{ - -3, -7, 4, -4, -6, 4, // unit = 0 - 3, 5, 2, 3, -3, -8, // unit = 1 - -3, 7, 4, 9, 0, -5, // unit = 2 + -3, -7, 4, -4, -6, 4, // unit = 0 + 3, 5, 2, 3, -3, -8, // unit = 1 + -3, 7, 4, 9, 0, -5, // unit = 2 }; Shape bias_shape{3}; std::vector<float> bias_data{-1, -5, -8}; @@ -164,17 +165,17 @@ TEST(FullyConnectedTest, BiasElementNumWeightDimMismatch_NEG) { Shape input_shape{3, 2, 2, 1}; std::vector<float> input_data{ - -3, -5, 5, 4, 9, -2, // batch = 0 - -3, -2, -4, 9, -8, 1, // batch = 1 + -3, -5, 5, 4, 9, -2, // batch = 0 + -3, -2, -4, 9, -8, 1, // batch = 1 }; Shape weights_shape{6, 3}; std::vector<float> weights_data{ - -3, -7, 4, // unit = 0 - -4, -6, 4, // unit = 1 - 3, 5, 2, // unit = 2 - 3, -3, -8, // unit = 3 - -3, 7, 4, // unit = 4 - 9, 0, -5, // unit = 5 + -3, -7, 4, // unit = 0 + -4, -6, 4, // unit = 1 + 3, 5, 2, // unit = 2 + 3, -3, -8, // unit = 3 + -3, 7, 4, // unit = 4 + 9, 0, -5, // unit = 5 }; Shape bias_shape{3}; std::vector<float> bias_data{-1, -5, -8}; diff --git a/compiler/luci-interpreter/src/kernels/Greater.test.cpp b/compiler/luci-interpreter/src/kernels/Greater.test.cpp index 3122fa840..3fcc86603 100644 --- a/compiler/luci-interpreter/src/kernels/Greater.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Greater.test.cpp @@ -30,18 +30,18 @@ using namespace testing; TEST(GreaterTest, FloatSimple) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, false, true, // Row 1 - true, false, false, // Row 2 + false, false, true, // Row 1 + true, false, false, // Row 2 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data); @@ -59,19 +59,19 @@ TEST(GreaterTest, FloatSimple) TEST(GreaterTest, FloatBroardcast) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 + 0.9, 0.7, 0.5, // Row 1 }; std::vector<bool> ref_output_data{ - false, false, true, // Row 1 - true, false, false, // Row 2 - false, false, true, // Row 3 + false, false, true, // Row 1 + true, false, false, // Row 2 + false, false, true, // Row 3 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data); @@ -93,25 +93,25 @@ const float F_MAX = 127.0 / 128.0; TEST(GreaterTest, Uint8Quantized) { std::vector<float> x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, false, true, true, // Row 1 - true, false, true, false, // Row 2 + false, false, true, true, // Row 1 + true, false, true, false, // Row 2 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); Greater kernel(&x_tensor, &y_tensor, &output_tensor); @@ -125,27 +125,27 @@ TEST(GreaterTest, Uint8Quantized) TEST(GreaterTest, Uint8QuantizedRescale) { std::vector<float> x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, false, true, true, // Row 1 - true, false, true, false, // Row 2 + false, false, true, true, // Row 1 + true, false, true, false, // Row 2 }; std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 3); - Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, - x_quant_param.second, x_data); - Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, - y_quant_param.second, y_data); + Tensor x_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data); + Tensor y_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); Greater kernel(&x_tensor, &y_tensor, &output_tensor); @@ -159,26 +159,26 @@ TEST(GreaterTest, Uint8QuantizedRescale) TEST(GreaterTest, Uint8QuantizedBroadcast) { std::vector<float> x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 }; std::vector<float> y_data{ - -1, 0.05, 0, 1, // Row 1 + -1, 0.05, 0, 1, // Row 1 }; std::vector<bool> ref_output_data{ - true, false, true, false, // Row 1 - true, true, false, false, // Row 2 - true, false, true, false, // Row 3 + true, false, true, false, // Row 1 + true, true, false, false, // Row 2 + true, false, true, false, // Row 3 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); Greater kernel(&x_tensor, &y_tensor, &output_tensor); diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp b/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp index 68135e27c..e7c1b4afe 100644 --- a/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp +++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.cpp @@ -28,7 +28,7 @@ namespace kernels { GreaterEqual::GreaterEqual(const Tensor *x, const Tensor *y, Tensor *output) - : Kernel({x, y}, {output}) + : Kernel({x, y}, {output}) { } @@ -101,8 +101,8 @@ void GreaterEqual::evalQuantized() const if (op_params.is_broadcast) { tflite::reference_ops::Broadcast4DSlowGreaterEqualWithScaling( - op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, - getTensorShape(output()), output_data); + op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + output_data); } else { diff --git a/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp b/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp index 11e62644c..7c79d8abc 100644 --- a/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp +++ b/compiler/luci-interpreter/src/kernels/GreaterEqual.test.cpp @@ -30,18 +30,18 @@ using namespace testing; TEST(GreaterEqualTest, FloatSimple) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, true, true, // Row 1 - true, true, false, // Row 2 + false, true, true, // Row 1 + true, true, false, // Row 2 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data); @@ -59,19 +59,19 @@ TEST(GreaterEqualTest, FloatSimple) TEST(GreaterEqualTest, FloatBroardcast) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 + 0.9, 0.7, 0.5, // Row 1 }; std::vector<bool> ref_output_data{ - false, true, true, // Row 1 - true, false, false, // Row 2 - false, false, true, // Row 3 + false, true, true, // Row 1 + true, false, false, // Row 2 + false, false, true, // Row 3 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data); @@ -93,25 +93,25 @@ const float F_MAX = 127.0 / 128.0; TEST(GreaterEqualTest, Uint8Quantized) { std::vector<float> x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.6, 0.55, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.6, 0.55, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, true, true, true, // Row 1 - true, false, true, false, // Row 2 + false, true, true, true, // Row 1 + true, false, true, false, // Row 2 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); @@ -125,27 +125,27 @@ TEST(GreaterEqualTest, Uint8Quantized) TEST(GreaterEqualTest, Uint8QuantizedRescale) { std::vector<float> x_data{ - 0.5, 0.5, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.5, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.5, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.5, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - false, true, true, true, // Row 1 - true, false, true, false, // Row 2 + false, true, true, true, // Row 1 + true, false, true, false, // Row 2 }; std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5); - Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, - x_quant_param.second, x_data); - Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, - y_quant_param.second, y_data); + Tensor x_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data); + Tensor y_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); @@ -159,26 +159,26 @@ TEST(GreaterEqualTest, Uint8QuantizedRescale) TEST(GreaterEqualTest, Uint8QuantizedBroadcast) { std::vector<float> x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 }; std::vector<float> y_data{ - -1, 0.05, 0, 1, // Row 1 + -1, 0.05, 0, 1, // Row 1 }; std::vector<bool> ref_output_data{ - true, false, true, false, // Row 1 - true, true, true, false, // Row 2 - true, false, true, false, // Row 3 + true, false, true, false, // Row 1 + true, true, true, false, // Row 2 + true, false, true, false, // Row 3 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); GreaterEqual kernel(&x_tensor, &y_tensor, &output_tensor); diff --git a/compiler/luci-interpreter/src/kernels/If.cpp b/compiler/luci-interpreter/src/kernels/If.cpp index ca982d591..a267f6267 100644 --- a/compiler/luci-interpreter/src/kernels/If.cpp +++ b/compiler/luci-interpreter/src/kernels/If.cpp @@ -34,8 +34,8 @@ static std::vector<const Tensor *> joinInputs(const Tensor *cond, If::If(const Tensor *cond, const std::vector<const Tensor *> &inputs, std::vector<Tensor *> outputs, RuntimeGraph *then_graph, RuntimeGraph *else_graph) - : Kernel(joinInputs(cond, inputs), std::move(outputs)), _then_graph(then_graph), - _else_graph(else_graph) + : Kernel(joinInputs(cond, inputs), std::move(outputs)), _then_graph(then_graph), + _else_graph(else_graph) { } diff --git a/compiler/luci-interpreter/src/kernels/If.test.cpp b/compiler/luci-interpreter/src/kernels/If.test.cpp index 6967407fb..0dba310d9 100644 --- a/compiler/luci-interpreter/src/kernels/If.test.cpp +++ b/compiler/luci-interpreter/src/kernels/If.test.cpp @@ -34,11 +34,11 @@ RuntimeGraph *buildAddSubgraph(RuntimeModule *module) { RuntimeGraph *graph = module->addGraph(); Tensor *input1 = graph->addTensor( - std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); Tensor *input2 = graph->addTensor( - std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); Tensor *output = graph->addTensor( - std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); graph->setInputTensors({input1, input2}); graph->setOutputTensors({output}); @@ -54,11 +54,11 @@ RuntimeGraph *buildMulSubgraph(RuntimeModule *module) { RuntimeGraph *graph = module->addGraph(); Tensor *input1 = graph->addTensor( - std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); Tensor *input2 = graph->addTensor( - std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); Tensor *output = graph->addTensor( - std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); + std::make_unique<Tensor>(DataType::FLOAT32, Shape{}, AffineQuantization{}, "")); graph->setInputTensors({input1, input2}); graph->setOutputTensors({output}); diff --git a/compiler/luci-interpreter/src/kernels/InstanceNorm.cpp b/compiler/luci-interpreter/src/kernels/InstanceNorm.cpp index 8e8241a28..b8317e2f2 100644 --- a/compiler/luci-interpreter/src/kernels/InstanceNorm.cpp +++ b/compiler/luci-interpreter/src/kernels/InstanceNorm.cpp @@ -28,7 +28,7 @@ namespace kernels InstanceNorm::InstanceNorm(const Tensor *input, const Tensor *gamma, const Tensor *beta, Tensor *output, const InstanceNormParams ¶ms) - : KernelWithParams<InstanceNormParams>({input, gamma, beta}, {output}, params) + : KernelWithParams<InstanceNormParams>({input, gamma, beta}, {output}, params) { } @@ -96,11 +96,11 @@ void InstanceNorm::evalFloat() const for (int32_t width = 0; width < widths; width++) { double input_value = - input_data[tflite::Offset(output_shape, batch, height, width, channel)]; + input_data[tflite::Offset(output_shape, batch, height, width, channel)]; double output_value = input_value * a + b; output_data[tflite::Offset(output_shape, batch, height, width, channel)] = - tflite::ActivationFunctionWithMinMax((float)output_value, activation_min, - activation_max); + tflite::ActivationFunctionWithMinMax((float)output_value, activation_min, + activation_max); } } } diff --git a/compiler/luci-interpreter/src/kernels/L2Normalize.cpp b/compiler/luci-interpreter/src/kernels/L2Normalize.cpp index 0bf133d9c..2eaf5404e 100644 --- a/compiler/luci-interpreter/src/kernels/L2Normalize.cpp +++ b/compiler/luci-interpreter/src/kernels/L2Normalize.cpp @@ -28,7 +28,7 @@ namespace kernels { L2Normalize::L2Normalize(const Tensor *input, Tensor *output, const L2NormParams ¶ms) - : KernelWithParams<L2NormParams>({input}, {output}, params) + : KernelWithParams<L2NormParams>({input}, {output}, params) { } diff --git a/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp b/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp index 8f9431182..6281b451b 100644 --- a/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp +++ b/compiler/luci-interpreter/src/kernels/L2Normalize.test.cpp @@ -51,11 +51,11 @@ void Check<uint8_t>(std::initializer_list<int32_t> input_shape, std::initializer_list<float> output_data) { std::pair<float, int32_t> quant_param = - quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f, - std::max(input_data) > 0 ? std::max(input_data) : 0.f); + quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f, + std::max(input_data) > 0 ? std::max(input_data) : 0.f); Tensor input_tensor = - makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data); + makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 128., 128); L2NormParams params{}; diff --git a/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp b/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp index 979364a7f..5bf3ba5a8 100644 --- a/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp +++ b/compiler/luci-interpreter/src/kernels/L2Pool2D.cpp @@ -30,7 +30,7 @@ namespace kernels { L2Pool2D::L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms) - : KernelWithParams<Pool2DParams>({input}, {output}, params) + : KernelWithParams<Pool2DParams>({input}, {output}, params) { } @@ -49,11 +49,11 @@ void L2Pool2D::configure() int out_width, out_height; out_width = computeOutputSize(padding, width, params().filter_width, params().stride_width, 1); out_height = - computeOutputSize(padding, height, params().filter_height, params().stride_height, 1); + computeOutputSize(padding, height, params().filter_height, params().stride_height, 1); _padding_width = - computePadding(params().stride_width, 1, width, params().filter_width, out_width); + computePadding(params().stride_width, 1, width, params().filter_width, out_width); _padding_height = - computePadding(params().stride_height, 1, height, params().filter_height, out_height); + computePadding(params().stride_height, 1, height, params().filter_height, out_height); LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32); output()->resize({batches, out_height, out_width, channels_out}); diff --git a/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp b/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp index 5f834e3c1..52f426a08 100644 --- a/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp +++ b/compiler/luci-interpreter/src/kernels/L2Pool2D.test.cpp @@ -31,8 +31,8 @@ TEST(L2Pool2DTest, FloatNone) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // + 0, 6, 2, 4, // + 3, 2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -58,8 +58,8 @@ TEST(L2Pool2DTest, FloatRelu) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - -1, -6, 2, 4, // - -3, -2, 10, 7, // + -1, -6, 2, 4, // + -3, -2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -85,8 +85,8 @@ TEST(L2Pool2DTest, FloatRelu1) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - -0.1, -0.6, 2, 4, // - -0.3, -0.2, 10, 7, // + -0.1, -0.6, 2, 4, // + -0.3, -0.2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -112,8 +112,8 @@ TEST(L2Pool2DTest, FloatRelu6) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - -0.1, -0.6, 2, 4, // - -0.3, -0.2, 10, 7, // + -0.1, -0.6, 2, 4, // + -0.3, -0.2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -139,8 +139,8 @@ TEST(L2Pool2DTest, FloatPaddingSame) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // + 0, 6, 2, 4, // + 3, 2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -166,8 +166,8 @@ TEST(L2Pool2DTest, FloatPaddingSameStride) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // + 0, 6, 2, 4, // + 3, 2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -193,8 +193,8 @@ TEST(L2Pool2DTest, FloatPaddingValidStride) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // + 0, 6, 2, 4, // + 3, 2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -220,8 +220,8 @@ TEST(L2Pool2DTest, InvalidInputShape_NEG) { Shape input_shape{1, 2, 4}; std::vector<float> input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // + 0, 6, 2, 4, // + 3, 2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -242,8 +242,8 @@ TEST(L2Pool2DTest, InvalidInputOutputType_NEG) { Shape input_shape{1, 2, 4}; std::vector<float> input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // + 0, 6, 2, 4, // + 3, 2, 10, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8); diff --git a/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp b/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp index 919b12792..f468da5d3 100644 --- a/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp +++ b/compiler/luci-interpreter/src/kernels/LeakyRelu.cpp @@ -30,7 +30,7 @@ namespace kernels { LeakyRelu::LeakyRelu(const Tensor *input, Tensor *output, const LeakyReluParams ¶ms) - : KernelWithParams<LeakyReluParams>({input}, {output}, params) + : KernelWithParams<LeakyReluParams>({input}, {output}, params) { } @@ -82,8 +82,8 @@ void LeakyRelu::evalQuantized() const op_params.output_shift_identity = _output_shift_identity; tflite::reference_ops::QuantizeLeakyRelu( - op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(output()), + getTensorData<uint8_t>(output())); } } // namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp b/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp index 2778549ed..b5cc3e7fc 100644 --- a/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp +++ b/compiler/luci-interpreter/src/kernels/LeakyRelu.test.cpp @@ -56,7 +56,7 @@ void Check<uint8_t>(std::initializer_list<int32_t> input_shape, const float quantized_tolerance = getTolerance(-8, 127.f / 16.f, 255); std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-8, 127.f / 16.f); Tensor input_tensor = - makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data); + makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); LeakyReluParams params{}; @@ -84,13 +84,13 @@ TYPED_TEST(LeakReluTest, Simple) Check<TypeParam>(/*input_shape=*/{2, 3}, /*output_shape=*/{2, 3}, /*input_data=*/ { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 }, /*output_data=*/ { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -0.5f, -1.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -0.5f, -1.0f, // Row 2 }, /*alpha=*/0.5f); @@ -100,8 +100,8 @@ TYPED_TEST(LeakReluTest, Simple) TEST(LeakReluTest, IvalidInputOutputType_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 }); Tensor output_tensor = makeOutputTensor(DataType::U8); diff --git a/compiler/luci-interpreter/src/kernels/Less.test.cpp b/compiler/luci-interpreter/src/kernels/Less.test.cpp index 73aa30b36..2972bd559 100644 --- a/compiler/luci-interpreter/src/kernels/Less.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Less.test.cpp @@ -30,18 +30,18 @@ using namespace testing; TEST(LessTest, FloatSimple) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, false, false, // Row 1 - false, false, true, // Row 2 + true, false, false, // Row 1 + false, false, true, // Row 2 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data); @@ -59,19 +59,19 @@ TEST(LessTest, FloatSimple) TEST(LessTest, FloatBroardcast) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 + 0.9, 0.7, 0.5, // Row 1 }; std::vector<bool> ref_output_data{ - true, false, false, // Row 1 - false, true, true, // Row 2 - true, true, false, // Row 3 + true, false, false, // Row 1 + false, true, true, // Row 2 + true, true, false, // Row 3 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data); @@ -93,25 +93,25 @@ const float F_MAX = 127.0 / 128.0; TEST(LessTest, Uint8Quantized) { std::vector<float> x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.6, 0.55, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.6, 0.55, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, false, false, false, // Row 1 - false, true, false, true, // Row 2 + true, false, false, false, // Row 1 + false, true, false, true, // Row 2 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); Less kernel(&x_tensor, &y_tensor, &output_tensor); @@ -125,27 +125,27 @@ TEST(LessTest, Uint8Quantized) TEST(LessTest, Uint8QuantizedRescale) { std::vector<float> x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, false, false, false, // Row 1 - false, true, false, true, // Row 2 + true, false, false, false, // Row 1 + false, true, false, true, // Row 2 }; std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5); - Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, - x_quant_param.second, x_data); - Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, - y_quant_param.second, y_data); + Tensor x_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data); + Tensor y_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); Less kernel(&x_tensor, &y_tensor, &output_tensor); @@ -159,26 +159,26 @@ TEST(LessTest, Uint8QuantizedRescale) TEST(LessTest, Uint8QuantizedBroadcast) { std::vector<float> x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 }; std::vector<float> y_data{ - -1, 0.05, 0, 1, // Row 1 + -1, 0.05, 0, 1, // Row 1 }; std::vector<bool> ref_output_data{ - false, true, false, true, // Row 1 - false, false, false, true, // Row 2 - false, true, false, true, // Row 3 + false, true, false, true, // Row 1 + false, false, false, true, // Row 2 + false, true, false, true, // Row 3 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); Less kernel(&x_tensor, &y_tensor, &output_tensor); diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.cpp b/compiler/luci-interpreter/src/kernels/LessEqual.cpp index b8aaba178..5f4c7f7aa 100644 --- a/compiler/luci-interpreter/src/kernels/LessEqual.cpp +++ b/compiler/luci-interpreter/src/kernels/LessEqual.cpp @@ -98,8 +98,8 @@ void LessEqual::evalQuantized() const if (op_params.is_broadcast) { tflite::reference_ops::Broadcast4DSlowLessEqualWithScaling( - op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, - getTensorShape(output()), output_data); + op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + output_data); } else { diff --git a/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp b/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp index 9184c061f..db65815a6 100644 --- a/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp +++ b/compiler/luci-interpreter/src/kernels/LessEqual.test.cpp @@ -30,18 +30,18 @@ using namespace testing; TEST(LessEqualTest, FloatSimple) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, true, false, // Row 1 - false, true, true, // Row 2 + true, true, false, // Row 1 + false, true, true, // Row 2 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data); @@ -59,19 +59,19 @@ TEST(LessEqualTest, FloatSimple) TEST(LessEqualTest, FloatBroardcast) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 + 0.9, 0.7, 0.5, // Row 1 }; std::vector<bool> ref_output_data{ - true, true, false, // Row 1 - false, true, true, // Row 2 - true, true, false, // Row 3 + true, true, false, // Row 1 + false, true, true, // Row 2 + true, true, false, // Row 3 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({3, 3}, x_data); @@ -93,25 +93,25 @@ const float F_MAX = 127.0 / 128.0; TEST(LessEqualTest, Uint8Quantized) { std::vector<float> x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.6, 0.55, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.6, 0.55, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, true, false, false, // Row 1 - false, true, false, true, // Row 2 + true, true, false, false, // Row 1 + false, true, false, true, // Row 2 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); @@ -125,27 +125,27 @@ TEST(LessEqualTest, Uint8Quantized) TEST(LessEqualTest, Uint8QuantizedRescale) { std::vector<float> x_data{ - 0.5, 0.6, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.6, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.6, 0.6, 0.5, // Row 1 - -1, 0.05, 0, 1, // Row 2 + 0.9, 0.6, 0.6, 0.5, // Row 1 + -1, 0.05, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, true, false, false, // Row 1 - false, true, false, true, // Row 2 + true, true, false, false, // Row 1 + false, true, false, true, // Row 2 }; std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 1.2, F_MAX * 1.5); - Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, - x_quant_param.second, x_data); - Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, - y_quant_param.second, y_data); + Tensor x_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data); + Tensor y_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); @@ -159,26 +159,26 @@ TEST(LessEqualTest, Uint8QuantizedRescale) TEST(LessEqualTest, Uint8QuantizedBroadcast) { std::vector<float> x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 }; std::vector<float> y_data{ - -1, 0.05, 0, 1, // Row 1 + -1, 0.05, 0, 1, // Row 1 }; std::vector<bool> ref_output_data{ - false, true, false, true, // Row 1 - false, false, true, true, // Row 2 - false, true, false, true, // Row 3 + false, true, false, true, // Row 1 + false, false, true, true, // Row 2 + false, true, false, true, // Row 3 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 3, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); LessEqual kernel(&x_tensor, &y_tensor, &output_tensor); diff --git a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp index b78e27128..fd2ec41a1 100644 --- a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp +++ b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.cpp @@ -29,8 +29,8 @@ namespace kernels { LocalResponseNormalization::LocalResponseNormalization( - const Tensor *input, Tensor *output, const LocalResponseNormalizationParams ¶ms) - : KernelWithParams<LocalResponseNormalizationParams>({input}, {output}, params) + const Tensor *input, Tensor *output, const LocalResponseNormalizationParams ¶ms) + : KernelWithParams<LocalResponseNormalizationParams>({input}, {output}, params) { } @@ -53,8 +53,8 @@ void LocalResponseNormalization::execute() const op_params.alpha = params().alpha; op_params.beta = params().beta; tflite::optimized_ops::LocalResponseNormalization( - op_params, getTensorShape(input()), getTensorData<float>(input()), - getTensorShape(output()), getTensorData<float>(output())); + op_params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(output()), + getTensorData<float>(output())); break; default: throw std::runtime_error("Unsupported type."); diff --git a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp index d98305c1a..6a4331d34 100644 --- a/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp +++ b/compiler/luci-interpreter/src/kernels/LocalResponseNormalization.test.cpp @@ -30,7 +30,7 @@ using namespace testing; TEST(LocalResponseNormalizationTest, SameAsL2Norm) { Tensor input_tensor = - makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); + makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); LocalResponseNormalizationParams params{}; @@ -50,7 +50,7 @@ TEST(LocalResponseNormalizationTest, SameAsL2Norm) TEST(LocalResponseNormalizationTest, WithAlpha) { Tensor input_tensor = - makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); + makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); LocalResponseNormalizationParams params{}; @@ -70,7 +70,7 @@ TEST(LocalResponseNormalizationTest, WithAlpha) TEST(LocalResponseNormalizationTest, WithBias) { Tensor input_tensor = - makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); + makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); LocalResponseNormalizationParams params{}; @@ -90,7 +90,7 @@ TEST(LocalResponseNormalizationTest, WithBias) TEST(LocalResponseNormalizationTest, SmallRadius) { Tensor input_tensor = - makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); + makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); LocalResponseNormalizationParams params{}; @@ -110,7 +110,7 @@ TEST(LocalResponseNormalizationTest, SmallRadius) TEST(LocalResponseNormalizationTest, InvalidInputDimension_NEG) { Tensor input_tensor = - makeInputTensor<DataType::FLOAT32>({1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); + makeInputTensor<DataType::FLOAT32>({1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); LocalResponseNormalizationParams params{}; @@ -126,7 +126,7 @@ TEST(LocalResponseNormalizationTest, InvalidInputDimension_NEG) TEST(LocalResponseNormalizationTest, InvalidInputOutputType_NEG) { Tensor input_tensor = - makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); + makeInputTensor<DataType::FLOAT32>({1, 1, 1, 6}, {-1.1, 0.6, 0.7, 1.2, -0.7, 0.1}); Tensor output_tensor = makeOutputTensor(DataType::U8); LocalResponseNormalizationParams params{}; diff --git a/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp b/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp index d3b331dfe..8a90c1dd0 100644 --- a/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp +++ b/compiler/luci-interpreter/src/kernels/LogSoftmax.test.cpp @@ -31,8 +31,8 @@ TEST(LogSoftmaxTest, Float) { Shape input_shape{2, 4}; std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 10, 1, // + 0, -6, 2, 4, // + 3, -2, 10, 1, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -42,8 +42,8 @@ TEST(LogSoftmaxTest, Float) kernel.execute(); std::vector<float> ref_output_data{ - -4.14297, -10.14297, -2.14297, -.142971, // - -7.00104, -12.00104, -.00104087, -9.00104, // + -4.14297, -10.14297, -2.14297, -.142971, // + -7.00104, -12.00104, -.00104087, -9.00104, // }; EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); } @@ -55,11 +55,11 @@ TEST(LogSoftmaxTest, Uint8) float kLogSoftmaxQuantizedTolerance = 16. / 256; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(kMin, kMax); std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 10, 1, // + 0, -6, 2, 4, // + 3, -2, 10, 1, // }; Tensor input_tensor = - makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data); + makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255); LogSoftmax kernel(&input_tensor, &output_tensor); @@ -67,8 +67,8 @@ TEST(LogSoftmaxTest, Uint8) kernel.execute(); std::vector<float> ref_output_data{ - -4.14297, -10.14297, -2.14297, -.142971, // - -7.00104, -12.00104, -.00104087, -9.00104, // + -4.14297, -10.14297, -2.14297, -.142971, // + -7.00104, -12.00104, -.00104087, -9.00104, // }; std::vector<int32_t> ref_output_shape{2, 4}; EXPECT_THAT(dequantizeTensorData(output_tensor), @@ -81,8 +81,8 @@ TEST(LogSoftmaxTest, Uint8) TEST(LogSoftmaxTest, InvalidInputOutputType_NEG) { std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 10, 1, // + 0, -6, 2, 4, // + 3, -2, 10, 1, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 4}, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, 16. / 256, 255); @@ -95,11 +95,11 @@ TEST(LogSoftmaxTest, InvalidOutputQuantParam_NEG) { std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-10, 10); std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 10, 1, // + 0, -6, 2, 4, // + 3, -2, 10, 1, // }; Tensor input_tensor = - makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data); + makeInputTensor<DataType::U8>({2, 4}, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, 20. / 256, 255); LogSoftmax kernel(&input_tensor, &output_tensor); diff --git a/compiler/luci-interpreter/src/kernels/LogicalAnd.cpp b/compiler/luci-interpreter/src/kernels/LogicalAnd.cpp index d50d50472..8e7263231 100644 --- a/compiler/luci-interpreter/src/kernels/LogicalAnd.cpp +++ b/compiler/luci-interpreter/src/kernels/LogicalAnd.cpp @@ -27,7 +27,7 @@ namespace kernels { LogicalAnd::LogicalAnd(const Tensor *input1, const Tensor *input2, Tensor *output) - : Kernel({input1, input2}, {output}) + : Kernel({input1, input2}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/LogicalOr.cpp b/compiler/luci-interpreter/src/kernels/LogicalOr.cpp index bd2208a4b..7027a2a8b 100644 --- a/compiler/luci-interpreter/src/kernels/LogicalOr.cpp +++ b/compiler/luci-interpreter/src/kernels/LogicalOr.cpp @@ -28,7 +28,7 @@ namespace kernels { LogicalOr::LogicalOr(const Tensor *input1, const Tensor *input2, Tensor *output) - : Kernel({input1, input2}, {output}) + : Kernel({input1, input2}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/Logistic.test.cpp b/compiler/luci-interpreter/src/kernels/Logistic.test.cpp index d3bbb330d..41369a417 100644 --- a/compiler/luci-interpreter/src/kernels/Logistic.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Logistic.test.cpp @@ -48,7 +48,7 @@ void Check<uint8_t>(std::initializer_list<int32_t> input_shape, std::initializer_list<float> output_data) { std::pair<float, int32_t> input_quant_param = - quantizationParams<uint8_t>(std::min(input_data), std::max(input_data)); + quantizationParams<uint8_t>(std::min(input_data), std::max(input_data)); Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first, input_quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, 1. / 256, 0); @@ -72,37 +72,37 @@ TYPED_TEST_CASE(LogisticTest, DataTypes); TYPED_TEST(LogisticTest, Simple) { Check<TypeParam>( - {89}, {89}, - {-10.0000000000, -9.7727272727, -9.5454545455, -9.3181818182, -9.0909090909, -8.8636363636, - -8.6363636364, -8.4090909091, -8.1818181818, -7.9545454545, -7.7272727273, -7.5000000000, - -7.2727272727, -7.0454545455, -6.8181818182, -6.5909090909, -6.3636363636, -6.1363636364, - -5.9090909091, -5.6818181818, -5.4545454545, -5.2272727273, -5.0000000000, -4.7727272727, - -4.5454545455, -4.3181818182, -4.0909090909, -3.8636363636, -3.6363636364, -3.4090909091, - -3.1818181818, -2.9545454545, -2.7272727273, -2.5000000000, -2.2727272727, -2.0454545455, - -1.8181818182, -1.5909090909, -1.3636363636, -1.1363636364, -0.9090909091, -0.6818181818, - -0.4545454545, -0.2272727273, 0.0000000000, 0.2272727273, 0.4545454545, 0.6818181818, - 0.9090909091, 1.1363636364, 1.3636363636, 1.5909090909, 1.8181818182, 2.0454545455, - 2.2727272727, 2.5000000000, 2.7272727273, 2.9545454545, 3.1818181818, 3.4090909091, - 3.6363636364, 3.8636363636, 4.0909090909, 4.3181818182, 4.5454545455, 4.7727272727, - 5.0000000000, 5.2272727273, 5.4545454545, 5.6818181818, 5.9090909091, 6.1363636364, - 6.3636363636, 6.5909090909, 6.8181818182, 7.0454545455, 7.2727272727, 7.5000000000, - 7.7272727273, 7.9545454545, 8.1818181818, 8.4090909091, 8.6363636364, 8.8636363636, - 9.0909090909, 9.3181818182, 9.5454545455, 9.7727272727, 10.0000000000}, - {0.0000453979, 0.0000569815, 0.0000715205, 0.0000897689, 0.0001126729, 0.0001414198, - 0.0001774998, 0.0002227827, 0.0002796147, 0.0003509396, 0.0004404502, 0.0005527786, - 0.0006937345, 0.0008706021, 0.0010925128, 0.0013709094, 0.0017201256, 0.0021581065, - 0.0027073042, 0.0033957870, 0.0042586071, 0.0053394826, 0.0066928509, 0.0083863576, - 0.0105038445, 0.0131488902, 0.0164489307, 0.0205599431, 0.0256715863, 0.0320125562, - 0.0398556989, 0.0495221198, 0.0613831074, 0.0758581800, 0.0934070047, 0.1145124805, - 0.1396521834, 0.1692560327, 0.2036499335, 0.2429886272, 0.2871859014, 0.3358556241, - 0.3882805886, 0.4434251301, 0.5000000000, 0.5565748699, 0.6117194114, 0.6641443759, - 0.7128140986, 0.7570113728, 0.7963500665, 0.8307439673, 0.8603478166, 0.8854875195, - 0.9065929953, 0.9241418200, 0.9386168926, 0.9504778802, 0.9601443011, 0.9679874438, - 0.9743284137, 0.9794400569, 0.9835510693, 0.9868511098, 0.9894961555, 0.9916136424, - 0.9933071491, 0.9946605174, 0.9957413929, 0.9966042130, 0.9972926958, 0.9978418935, - 0.9982798744, 0.9986290906, 0.9989074872, 0.9991293979, 0.9993062655, 0.9994472214, - 0.9995595498, 0.9996490604, 0.9997203853, 0.9997772173, 0.9998225002, 0.9998585802, - 0.9998873271, 0.9999102311, 0.9999284795, 0.9999430185, 0.9999546021}); + {89}, {89}, + {-10.0000000000, -9.7727272727, -9.5454545455, -9.3181818182, -9.0909090909, -8.8636363636, + -8.6363636364, -8.4090909091, -8.1818181818, -7.9545454545, -7.7272727273, -7.5000000000, + -7.2727272727, -7.0454545455, -6.8181818182, -6.5909090909, -6.3636363636, -6.1363636364, + -5.9090909091, -5.6818181818, -5.4545454545, -5.2272727273, -5.0000000000, -4.7727272727, + -4.5454545455, -4.3181818182, -4.0909090909, -3.8636363636, -3.6363636364, -3.4090909091, + -3.1818181818, -2.9545454545, -2.7272727273, -2.5000000000, -2.2727272727, -2.0454545455, + -1.8181818182, -1.5909090909, -1.3636363636, -1.1363636364, -0.9090909091, -0.6818181818, + -0.4545454545, -0.2272727273, 0.0000000000, 0.2272727273, 0.4545454545, 0.6818181818, + 0.9090909091, 1.1363636364, 1.3636363636, 1.5909090909, 1.8181818182, 2.0454545455, + 2.2727272727, 2.5000000000, 2.7272727273, 2.9545454545, 3.1818181818, 3.4090909091, + 3.6363636364, 3.8636363636, 4.0909090909, 4.3181818182, 4.5454545455, 4.7727272727, + 5.0000000000, 5.2272727273, 5.4545454545, 5.6818181818, 5.9090909091, 6.1363636364, + 6.3636363636, 6.5909090909, 6.8181818182, 7.0454545455, 7.2727272727, 7.5000000000, + 7.7272727273, 7.9545454545, 8.1818181818, 8.4090909091, 8.6363636364, 8.8636363636, + 9.0909090909, 9.3181818182, 9.5454545455, 9.7727272727, 10.0000000000}, + {0.0000453979, 0.0000569815, 0.0000715205, 0.0000897689, 0.0001126729, 0.0001414198, + 0.0001774998, 0.0002227827, 0.0002796147, 0.0003509396, 0.0004404502, 0.0005527786, + 0.0006937345, 0.0008706021, 0.0010925128, 0.0013709094, 0.0017201256, 0.0021581065, + 0.0027073042, 0.0033957870, 0.0042586071, 0.0053394826, 0.0066928509, 0.0083863576, + 0.0105038445, 0.0131488902, 0.0164489307, 0.0205599431, 0.0256715863, 0.0320125562, + 0.0398556989, 0.0495221198, 0.0613831074, 0.0758581800, 0.0934070047, 0.1145124805, + 0.1396521834, 0.1692560327, 0.2036499335, 0.2429886272, 0.2871859014, 0.3358556241, + 0.3882805886, 0.4434251301, 0.5000000000, 0.5565748699, 0.6117194114, 0.6641443759, + 0.7128140986, 0.7570113728, 0.7963500665, 0.8307439673, 0.8603478166, 0.8854875195, + 0.9065929953, 0.9241418200, 0.9386168926, 0.9504778802, 0.9601443011, 0.9679874438, + 0.9743284137, 0.9794400569, 0.9835510693, 0.9868511098, 0.9894961555, 0.9916136424, + 0.9933071491, 0.9946605174, 0.9957413929, 0.9966042130, 0.9972926958, 0.9978418935, + 0.9982798744, 0.9986290906, 0.9989074872, 0.9991293979, 0.9993062655, 0.9994472214, + 0.9995595498, 0.9996490604, 0.9997203853, 0.9997772173, 0.9998225002, 0.9998585802, + 0.9998873271, 0.9999102311, 0.9999284795, 0.9999430185, 0.9999546021}); } TEST(LogisticTest, IvalidInputOutputType_NEG) diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp b/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp index 123e6e1a2..8d9760ff2 100644 --- a/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp +++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.cpp @@ -30,7 +30,7 @@ namespace kernels { MaxPool2D::MaxPool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms) - : KernelWithParams<Pool2DParams>({input}, {output}, params) + : KernelWithParams<Pool2DParams>({input}, {output}, params) { } @@ -44,15 +44,15 @@ void MaxPool2D::configure() const int32_t input_width = input_shape.dim(2); const int32_t depth = input_shape.dim(3); - const int32_t output_height = computeOutputSize(_params.padding, input_height, - _params.filter_height, _params.stride_height); + const int32_t output_height = + computeOutputSize(_params.padding, input_height, _params.filter_height, _params.stride_height); const int32_t output_width = - computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width); + computeOutputSize(_params.padding, input_width, _params.filter_width, _params.stride_width); _padding_height = - computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height); + computePadding(_params.stride_height, 1, input_height, _params.filter_height, output_height); _padding_width = - computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width); + computePadding(_params.stride_width, 1, input_width, _params.filter_width, output_width); output()->resize({batches, output_height, output_width, depth}); if (input()->element_type() == DataType::U8) @@ -142,8 +142,8 @@ void MaxPool2D::evalSInt16() const params.quantized_activation_max = activation_max; tflite::reference_integer_ops::MaxPool( - params, getTensorShape(input()), getTensorData<int16_t>(input()), // - getTensorShape(output()), getTensorData<int16_t>(output())); + params, getTensorShape(input()), getTensorData<int16_t>(input()), // + getTensorShape(output()), getTensorData<int16_t>(output())); } } // namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp b/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp index 1d7fe06c4..b9991f7ec 100644 --- a/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp +++ b/compiler/luci-interpreter/src/kernels/MaxPool2D.test.cpp @@ -30,9 +30,9 @@ TEST(MaxPool2DTest, Float) { Shape input_shape{1, 3, 5, 1}; std::vector<float> input_data{ - 1, -1, 0, -2, 2, // - -7, -6, -5, -4, -3, // - 5, 4, 3, 6, 7, // + 1, -1, 0, -2, 2, // + -7, -6, -5, -4, -3, // + 5, 4, 3, 6, 7, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -50,8 +50,8 @@ TEST(MaxPool2DTest, Float) kernel.execute(); std::vector<float> ref_output_data{ - 1, 2, // - 5, 6, // + 1, 2, // + 5, 6, // }; std::initializer_list<int32_t> ref_output_shape{1, 2, 2, 1}; EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); @@ -62,11 +62,11 @@ TEST(MaxPool2DTest, Uint8) { std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-15.9375, 15.9375); std::vector<float> input_data{ - 0, -6, 12, 4, // - -3, -2, 10, 7, // + 0, -6, 12, 4, // + -3, -2, 10, 7, // }; - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, - quant_param.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); Pool2DParams params{}; @@ -92,13 +92,13 @@ TEST(MaxPool2DTest, SInt16) Shape input_shape{1, 3, 5, 1}; std::vector<int32_t> ref_output_shape{1, 2, 2, 1}; std::vector<float> input_data{ - 1, -1, 0, -2, 2, // - -7, -6, -5, -4, -3, // - 5, 4, 3, 6, 7, // + 1, -1, 0, -2, 2, // + -7, -6, -5, -4, -3, // + 5, 4, 3, 6, 7, // }; std::vector<float> ref_output_data{ - 1, 2, // - 5, 6, // + 1, 2, // + 5, 6, // }; Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.2, 0, input_data); diff --git a/compiler/luci-interpreter/src/kernels/Maximum.cpp b/compiler/luci-interpreter/src/kernels/Maximum.cpp index c522b0706..b102b5e27 100644 --- a/compiler/luci-interpreter/src/kernels/Maximum.cpp +++ b/compiler/luci-interpreter/src/kernels/Maximum.cpp @@ -27,7 +27,7 @@ namespace kernels { Maximum::Maximum(const Tensor *input1, const Tensor *input2, Tensor *output) - : Kernel({input1, input2}, {output}) + : Kernel({input1, input2}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/Mean.cpp b/compiler/luci-interpreter/src/kernels/Mean.cpp index f20cf7d89..421632812 100644 --- a/compiler/luci-interpreter/src/kernels/Mean.cpp +++ b/compiler/luci-interpreter/src/kernels/Mean.cpp @@ -124,7 +124,7 @@ static Shape getOutputShape(const Shape &input_shape, const int *axes_data, int } Mean::Mean(const Tensor *input, const Tensor *axes, Tensor *output, const ReducerParams ¶ms) - : KernelWithParams<ReducerParams>({input, axes}, {output}, params) + : KernelWithParams<ReducerParams>({input, axes}, {output}, params) { } @@ -149,16 +149,15 @@ void Mean::configure() tflite::MeanParams params{}; resolveAxes(axes_data, num_axes, ¶ms); - const bool need_temporaries = - !(_params.keep_dims && input_num_dims == 4 && params.axis_count == 2 && - ((params.axis[0] == 1 && params.axis[1] == 2) || - (params.axis[0] == 2 && params.axis[1] == 1))); + const bool need_temporaries = !( + _params.keep_dims && input_num_dims == 4 && params.axis_count == 2 && + ((params.axis[0] == 1 && params.axis[1] == 2) || (params.axis[0] == 2 && params.axis[1] == 1))); if (need_temporaries) { _temp_index = - std::make_unique<Tensor>(DataType::S32, Shape(input_num_dims), AffineQuantization{}, ""); + std::make_unique<Tensor>(DataType::S32, Shape(input_num_dims), AffineQuantization{}, ""); _resolved_axes = - std::make_unique<Tensor>(DataType::S32, Shape(num_axes), AffineQuantization{}, ""); + std::make_unique<Tensor>(DataType::S32, Shape(num_axes), AffineQuantization{}, ""); _temp_sum = std::make_unique<Tensor>(input()->element_type(), output()->shape(), AffineQuantization{}, ""); } @@ -209,11 +208,11 @@ void Mean::evalFloat() const else { tflite::reference_ops::Mean( - getTensorData<float>(input()), getTensorShape(input()).DimsData(), - input()->shape().num_dims(), getTensorData<float>(output()), - getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes, - _params.keep_dims, getTensorData<int>(_temp_index.get()), - getTensorData<int>(_resolved_axes.get()), getTensorData<float>(_temp_sum.get())); + getTensorData<float>(input()), getTensorShape(input()).DimsData(), + input()->shape().num_dims(), getTensorData<float>(output()), + getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes, + _params.keep_dims, getTensorData<int>(_temp_index.get()), + getTensorData<int>(_resolved_axes.get()), getTensorData<float>(_temp_sum.get())); } } @@ -240,22 +239,22 @@ void Mean::evalQuantized() const else if (input()->zero_point() == output()->zero_point() && input()->scale() == output()->scale()) { tflite::reference_ops::Mean( - getTensorData<uint8_t>(input()), getTensorShape(input()).DimsData(), - input()->shape().num_dims(), getTensorData<uint8_t>(output()), - getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes, - _params.keep_dims, getTensorData<int>(_temp_index.get()), - getTensorData<int>(_resolved_axes.get()), getTensorData<int>(_temp_sum.get())); + getTensorData<uint8_t>(input()), getTensorShape(input()).DimsData(), + input()->shape().num_dims(), getTensorData<uint8_t>(output()), + getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes, + _params.keep_dims, getTensorData<int>(_temp_index.get()), + getTensorData<int>(_resolved_axes.get()), getTensorData<int>(_temp_sum.get())); } else { tflite::reference_ops::QuantizedMeanOrSum<>( - getTensorData<uint8_t>(input()), input()->zero_point(), input()->scale(), - getTensorShape(input()).DimsData(), input()->shape().num_dims(), - getTensorData<uint8_t>(output()), output()->zero_point(), output()->scale(), - getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes, - _params.keep_dims, getTensorData<int>(_temp_index.get()), - getTensorData<int>(_resolved_axes.get()), getTensorData<int>(_temp_sum.get()), - /*compute_sum=*/false); + getTensorData<uint8_t>(input()), input()->zero_point(), input()->scale(), + getTensorShape(input()).DimsData(), input()->shape().num_dims(), + getTensorData<uint8_t>(output()), output()->zero_point(), output()->scale(), + getTensorShape(output()).DimsData(), output()->shape().num_dims(), axes_data, num_axes, + _params.keep_dims, getTensorData<int>(_temp_index.get()), + getTensorData<int>(_resolved_axes.get()), getTensorData<int>(_temp_sum.get()), + /*compute_sum=*/false); } } @@ -288,7 +287,7 @@ void Mean::evalQuantizedS16() const assert(output_shape.dim(3) == depth); const double real_multiplier = - static_cast<double>(input()->scale()) / static_cast<double>(output()->scale()); + static_cast<double>(input()->scale()) / static_cast<double>(output()->scale()); int32_t output_multiplier{}; int output_shift{}; @@ -309,11 +308,11 @@ void Mean::evalQuantizedS16() const } } int32_t scaled_acc = - tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); + tflite::MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift); // Divide by the number of elements rounding to the nearest integer. scaled_acc = scaled_acc > 0 - ? (scaled_acc + num_elements_in_axes / 2) / num_elements_in_axes - : (scaled_acc - num_elements_in_axes / 2) / num_elements_in_axes; + ? (scaled_acc + num_elements_in_axes / 2) / num_elements_in_axes + : (scaled_acc - num_elements_in_axes / 2) / num_elements_in_axes; scaled_acc = std::max(scaled_acc, output_min); scaled_acc = std::min(scaled_acc, output_max); diff --git a/compiler/luci-interpreter/src/kernels/Mean.test.cpp b/compiler/luci-interpreter/src/kernels/Mean.test.cpp index e81d2ad5f..fa0ba2169 100644 --- a/compiler/luci-interpreter/src/kernels/Mean.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Mean.test.cpp @@ -107,7 +107,7 @@ TEST(MeanTest, Uint8KeepDims) std::vector<int32_t> axis_data{1}; Tensor input_tensor = - makeInputTensor<DataType::U8>({3, 2}, quant_param.first, quant_param.second, input_data); + makeInputTensor<DataType::U8>({3, 2}, quant_param.first, quant_param.second, input_data); Tensor axis_tensor = makeInputTensor<DataType::S32>({1}, axis_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); @@ -133,7 +133,7 @@ TEST(MeanTest, Uint8NotKeepDims) std::vector<int32_t> axis_data{1}; Tensor input_tensor = - makeInputTensor<DataType::U8>({1, 3, 2}, quant_param.first, quant_param.second, input_data); + makeInputTensor<DataType::U8>({1, 3, 2}, quant_param.first, quant_param.second, input_data); Tensor axis_tensor = makeInputTensor<DataType::S32>({1}, axis_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); diff --git a/compiler/luci-interpreter/src/kernels/Minimum.cpp b/compiler/luci-interpreter/src/kernels/Minimum.cpp index 5eb13455e..5d3dcde72 100644 --- a/compiler/luci-interpreter/src/kernels/Minimum.cpp +++ b/compiler/luci-interpreter/src/kernels/Minimum.cpp @@ -27,7 +27,7 @@ namespace kernels { Minimum::Minimum(const Tensor *input1, const Tensor *input2, Tensor *output) - : Kernel({input1, input2}, {output}) + : Kernel({input1, input2}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/Mul.cpp b/compiler/luci-interpreter/src/kernels/Mul.cpp index 513d147a3..4e6e3f75a 100644 --- a/compiler/luci-interpreter/src/kernels/Mul.cpp +++ b/compiler/luci-interpreter/src/kernels/Mul.cpp @@ -30,7 +30,7 @@ namespace kernels { Mul::Mul(const Tensor *input1, const Tensor *input2, Tensor *output, const MulParams ¶ms) - : KernelWithParams<MulParams>({input1, input2}, {output}, params) + : KernelWithParams<MulParams>({input1, input2}, {output}, params) { } @@ -73,13 +73,13 @@ void Mul::evalFloat() const params.float_activation_max = activation_max; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { tflite::optimized_ops::BroadcastMul4DSlow( - params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), - getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); + params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), + getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); } else { diff --git a/compiler/luci-interpreter/src/kernels/Mul.test.cpp b/compiler/luci-interpreter/src/kernels/Mul.test.cpp index 1409b3fae..fc7ffb5a1 100644 --- a/compiler/luci-interpreter/src/kernels/Mul.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Mul.test.cpp @@ -32,14 +32,14 @@ TEST(MulTest, Float) Shape base_shape = {2, 3, 1, 2}; std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; std::vector<std::vector<float>> test_outputs = { - {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, - 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, - 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, - 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, - 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, - {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; + {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, + 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, + 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, + 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, + 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, + {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; @@ -57,7 +57,7 @@ TEST(MulTest, Float) kernel.execute(); EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + << "With shape number " << i; } // Re-run with exchanged inputs. for (size_t i = 0; i < test_shapes.size(); ++i) @@ -74,7 +74,7 @@ TEST(MulTest, Float) kernel.execute(); EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + << "With shape number " << i; } } @@ -83,25 +83,25 @@ TEST(MulTest, SInt16) Shape base_shape = {2, 3, 1, 2}; std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; std::vector<std::vector<int32_t>> ref_output_shapes{ - {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; + {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; std::vector<std::vector<float>> ref_outputs = { - {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, - 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, - 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, - {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, - 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, - 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, - {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; + {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f, + 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f, + 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f}, + {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f, + 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f, + 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f}, + {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}}; for (size_t i = 0; i < test_shapes.size(); ++i) { Tensor input1_tensor = makeInputTensor<DataType::S16>(base_shape, 3.0 / 32767, 0, input1_data); Tensor input2_tensor = - makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0, input2_data); + makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0, input2_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0); const float tolerance = output_tensor.scale() * 2; @@ -114,15 +114,15 @@ TEST(MulTest, SInt16) EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; + << "With shape number " << i; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + << "With shape number " << i; } // Re-run with exchanged inputs and different scales. for (size_t i = 0; i < test_shapes.size(); ++i) { Tensor input1_tensor = - makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0, input2_data); + makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0, input2_data); Tensor input2_tensor = makeInputTensor<DataType::S16>(base_shape, 4.0 / 32767, 0, input1_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 3.0 / 32767, 0); const float tolerance = output_tensor.scale() * 2; @@ -136,9 +136,9 @@ TEST(MulTest, SInt16) EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shapes[i])) - << "With shape number " << i; + << "With shape number " << i; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance)) - << "With shape number " << i; + << "With shape number " << i; } } diff --git a/compiler/luci-interpreter/src/kernels/Neg.cpp b/compiler/luci-interpreter/src/kernels/Neg.cpp new file mode 100644 index 000000000..99f4d4a21 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Neg.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 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 "kernels/Neg.h" +#include "kernels/Utils.h" + +#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h> + +#include <stdexcept> + +namespace luci_interpreter +{ + +namespace kernels +{ + +Neg::Neg(const Tensor *input, Tensor *output) : Kernel({input}, {output}) {} + +void Neg::configure() +{ + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + output()->resize(input()->shape()); +} + +void Neg::execute() const +{ + switch (input()->element_type()) + { + case DataType::FLOAT32: + evalFloat(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +void Neg::evalFloat() const +{ + tflite::reference_ops::Negate(getTensorShape(input()), getTensorData<float>(input()), + getTensorShape(output()), getTensorData<float>(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Neg.h b/compiler/luci-interpreter/src/kernels/Neg.h new file mode 100644 index 000000000..69fa1a18e --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Neg.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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 LUCI_INTERPRETER_KERNELS_NEG_H +#define LUCI_INTERPRETER_KERNELS_NEG_H + +#include "core/Kernel.h" +#include <vector> + +namespace luci_interpreter +{ +namespace kernels +{ + +class Neg : public Kernel +{ +public: + Neg(const Tensor *input, Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + void evalFloat() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_NEG_H diff --git a/compiler/luci-interpreter/src/kernels/Neg.test.cpp b/compiler/luci-interpreter/src/kernels/Neg.test.cpp new file mode 100644 index 000000000..33256e1c6 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Neg.test.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. 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 "kernels/Neg.h" +#include "kernels/TestUtils.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template <typename T> +void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> output_shape, + std::initializer_list<T> input_data, std::initializer_list<T> output_data) +{ + constexpr DataType element_type = getElementType<T>(); + Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data); + Tensor output_tensor = makeOutputTensor(element_type); + + Neg kernel(&input_tensor, &output_tensor); + + kernel.configure(); + kernel.execute(); + + EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +TEST(NegTest, FloatSimple) +{ + Check<float>(/*input_shape=*/{2, 3}, + /*output_shape=*/{2, 3}, + /*input_data=*/ + { + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 + }, + /*output_data=*/ + { + 0.0f, -1.0f, -3.0f, // Row 1 + -1.0f, 1.0f, 2.0f, // Row 2 + }); + + SUCCEED(); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.cpp b/compiler/luci-interpreter/src/kernels/NotEqual.cpp index cd2f6c2c1..99d5e0fa0 100644 --- a/compiler/luci-interpreter/src/kernels/NotEqual.cpp +++ b/compiler/luci-interpreter/src/kernels/NotEqual.cpp @@ -98,8 +98,8 @@ void NotEqual::evalQuantized() const if (op_params.is_broadcast) { tflite::reference_ops::Broadcast4DSlowNotEqualWithScaling( - op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, - getTensorShape(output()), output_data); + op_params, getTensorShape(x()), x_data, getTensorShape(y()), y_data, getTensorShape(output()), + output_data); } else { diff --git a/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp b/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp index 8c8712371..f9dc7781b 100644 --- a/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp +++ b/compiler/luci-interpreter/src/kernels/NotEqual.test.cpp @@ -30,18 +30,18 @@ using namespace testing; TEST(NotEqualTest, FloatSimple) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 - -1, 0, 1, // Row 2 + 0.9, 0.7, 0.5, // Row 1 + -1, 0, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, false, true, // Row 1 - true, false, true, // Row 2 + true, false, true, // Row 1 + true, false, true, // Row 2 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, x_data); @@ -59,21 +59,21 @@ TEST(NotEqualTest, FloatSimple) TEST(NotEqualTest, FloatBroardcast) { std::vector<float> x_data{ - 0.5, 0.7, 0.9, // Row 1 - 1, 0, -1, // Row 2 - -1, 0, 1, // Row 3 - 0.9, 0.7, 0.5, // Row 4 + 0.5, 0.7, 0.9, // Row 1 + 1, 0, -1, // Row 2 + -1, 0, 1, // Row 3 + 0.9, 0.7, 0.5, // Row 4 }; std::vector<float> y_data{ - 0.9, 0.7, 0.5, // Row 1 + 0.9, 0.7, 0.5, // Row 1 }; std::vector<bool> ref_output_data{ - true, false, true, // Row 1 - true, true, true, // Row 2 - true, true, true, // Row 3 - false, false, false, // Row 4 + true, false, true, // Row 1 + true, true, true, // Row 2 + true, true, true, // Row 3 + false, false, false, // Row 4 }; Tensor x_tensor = makeInputTensor<DataType::FLOAT32>({4, 3}, x_data); @@ -95,27 +95,27 @@ const float F_MAX = 127.0 / 128.0; TEST(NotEqualTest, Uint8Quantized) { std::vector<float> x_data{ - 0.5, 0.5, 0.7, 0.9, // Row 1 - 1, 0, 0.05, -1, // Row 2 + 0.5, 0.5, 0.7, 0.9, // Row 1 + 1, 0, 0.05, -1, // Row 2 }; std::vector<float> y_data{ - 0.9, 0.5, 0.55, 0.5, // Row 1 - -1, 0, 0.05, 1, // Row 2 + 0.9, 0.5, 0.55, 0.5, // Row 1 + -1, 0, 0.05, 1, // Row 2 }; std::vector<bool> ref_output_data{ - true, false, true, true, // Row 1 - true, false, false, true, // Row 2 + true, false, true, true, // Row 1 + true, false, false, true, // Row 2 }; std::pair<float, int32_t> x_quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); - Tensor x_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, - x_quant_param.second, x_data); + Tensor x_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, x_quant_param.first, x_quant_param.second, x_data); std::pair<float, int32_t> y_quant_param = quantizationParams<uint8_t>(F_MIN * 2, F_MAX * 2); - Tensor y_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, - y_quant_param.second, y_data); + Tensor y_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, y_quant_param.first, y_quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); @@ -130,28 +130,28 @@ TEST(NotEqualTest, Uint8Quantized) TEST(NotEqualTest, Uint8QuantizedBroadcast) { std::vector<float> x_data{ - 0.4, -0.8, 0.7, 0.3, // Row 1 - -0.5, 0.1, 0, 0.5, // Row 2 - 1, 0, 0.05, -1, // Row 3 - -1, 0.05, 0, 1, // Row 4 + 0.4, -0.8, 0.7, 0.3, // Row 1 + -0.5, 0.1, 0, 0.5, // Row 2 + 1, 0, 0.05, -1, // Row 3 + -1, 0.05, 0, 1, // Row 4 }; std::vector<float> y_data{ - -1, 0.05, 0, 1, // Row 1 + -1, 0.05, 0, 1, // Row 1 }; std::vector<bool> ref_output_data{ - true, true, true, true, // Row 1 - true, true, false, true, // Row 2 - true, true, true, true, // Row 3 - false, false, false, false, // Row 4 + true, true, true, true, // Row 1 + true, true, false, true, // Row 2 + true, true, true, true, // Row 3 + false, false, false, false, // Row 4 }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(F_MIN, F_MAX); Tensor x_tensor = - makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data); + makeInputTensor<DataType::U8>({1, 4, 4, 1}, quant_param.first, quant_param.second, x_data); Tensor y_tensor = - makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); + makeInputTensor<DataType::U8>({1, 1, 4, 1}, quant_param.first, quant_param.second, y_data); Tensor output_tensor = makeOutputTensor(DataType::BOOL); NotEqual kernel(&x_tensor, &y_tensor, &output_tensor); diff --git a/compiler/luci-interpreter/src/kernels/Pack.cpp b/compiler/luci-interpreter/src/kernels/Pack.cpp new file mode 100644 index 000000000..6fee93890 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Pack.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. 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 "kernels/Pack.h" +#include "kernels/Utils.h" + +#include <tensorflow/lite/kernels/internal/reference/reference_ops.h> + +#include <stdexcept> + +namespace luci_interpreter +{ +namespace kernels +{ + +Pack::Pack(std::vector<const Tensor *> inputs, Tensor *output, const PackParams ¶ms) + : KernelWithParams<PackParams>(std::move(inputs), {output}, params) +{ +} + +void Pack::configure() +{ + LUCI_INTERPRETER_CHECK(_inputs.size() == static_cast<uint32_t>(params().values_count)); + const Tensor *t0 = _inputs[0]; + const int dimension_size = t0->shape().num_dims() + 1; + int axis = params().axis; + if (axis < 0) + { + axis += dimension_size; + } + LUCI_INTERPRETER_CHECK(axis >= 0 && axis <= t0->shape().num_dims()); + + if (t0->element_type() != DataType::S32 && t0->element_type() != DataType::FLOAT32 && + t0->element_type() != DataType::U8 && t0->element_type() != DataType::S8 && + t0->element_type() != DataType::S16 && t0->element_type() != DataType::S64) + { + throw std::runtime_error("Unsupported type."); + } + + for (uint32_t i = 1; i < _inputs.size(); ++i) + { + const Tensor *tensor = _inputs[i]; + LUCI_INTERPRETER_CHECK(tensor->element_type() == t0->element_type()); + LUCI_INTERPRETER_CHECK(tensor->shape().num_dims() == t0->shape().num_dims()); + for (int d = 0; d < t0->shape().num_dims(); ++d) + { + LUCI_INTERPRETER_CHECK(tensor->shape().dim(d) == t0->shape().dim(d)); + } + } + + Shape output_shape(dimension_size); + int i = 0; + for (int index = 0; index < dimension_size; ++index) + { + if (index == axis) + { + output_shape.dim(index) = params().values_count; + } + else + { + output_shape.dim(index) = t0->shape().dim(i++); + } + } + + if (t0->element_type() == DataType::S32 || t0->element_type() == DataType::U8 || + t0->element_type() == DataType::S8 || t0->element_type() == DataType::S16 || + t0->element_type() == DataType::S64) + { + LUCI_INTERPRETER_CHECK(output()->zero_point() == t0->zero_point()); + LUCI_INTERPRETER_CHECK(output()->scale() == t0->scale()); + // Guarantee input/output quantization params match as we do not support + // packing quantized tensors. + for (int i = 0; i < params().values_count; i++) + { + LUCI_INTERPRETER_CHECK(_inputs[i]->zero_point() == t0->zero_point()); + LUCI_INTERPRETER_CHECK(_inputs[i]->scale() == t0->scale()); + } + } + + output()->resize(output_shape); +} + +void Pack::execute() const +{ + switch (_inputs[0]->element_type()) + { + case DataType::FLOAT32: + evalGeneric<float>(); + break; + case DataType::U8: + evalGeneric<uint8_t>(); + break; + case DataType::S8: + evalGeneric<int8_t>(); + break; + case DataType::S16: + evalGeneric<int16_t>(); + break; + case DataType::S32: + evalGeneric<int32_t>(); + break; + case DataType::S64: + evalGeneric<int64_t>(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template <typename T> void Pack::evalGeneric() const +{ + const Tensor *t0 = _inputs[0]; + const int dimension_size = t0->shape().num_dims() + 1; + int axis = params().axis; + if (axis < 0) + { + axis += dimension_size; + } + + VectorOfTensors<T, true> inputs(_inputs); + tflite::PackParams params{}; + params.axis = axis; + params.inputs_count = _inputs.size(); + tflite::reference_ops::Pack<T>(params, inputs.shapes(), inputs.data(), getTensorShape(output()), + getTensorData<T>(output())); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Pack.h b/compiler/luci-interpreter/src/kernels/Pack.h new file mode 100644 index 000000000..4a2fcfd80 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Pack.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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 LUCI_INTERPRETER_KERNELS_PACK_H +#define LUCI_INTERPRETER_KERNELS_PACK_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class Pack : public KernelWithParams<PackParams> +{ +public: + Pack(std::vector<const Tensor *> inputs, Tensor *output, const PackParams ¶ms); + + const Tensor *input(int index) const { return _inputs[index]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template <typename T> void evalGeneric() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_PACK_H diff --git a/compiler/luci-interpreter/src/kernels/Pack.test.cpp b/compiler/luci-interpreter/src/kernels/Pack.test.cpp new file mode 100644 index 000000000..092bd449a --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/Pack.test.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 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 "kernels/Pack.h" +#include "kernels/TestUtils.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template <typename T> +void Check(std::vector<std::initializer_list<int32_t>> input_shapes, + std::initializer_list<int32_t> output_shape, std::vector<std::vector<T>> input_datas, + std::initializer_list<T> output_data, int32_t axis) +{ + constexpr DataType element_type = getElementType<T>(); + std::vector<const Tensor *> inputs(input_datas.size()); + std::vector<Tensor> tmp_inputs; + for (int i = 0; i < input_datas.size(); i++) + { + if (std::is_same<T, float>::value) + { + tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {}, "")); + tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); + } + else + { + tmp_inputs.push_back(Tensor(element_type, input_shapes[i], {{1.0f / 255}, {128}}, "")); + tmp_inputs[i].writeData(input_datas[i].data(), input_datas[i].size() * sizeof(T)); + } + } + for (int i = 0; i < input_datas.size(); i++) + { + inputs[i] = &tmp_inputs[i]; + } + + Tensor output_tensor = makeOutputTensor(element_type); + if (!std::is_same<T, float>::value) + { + output_tensor = makeOutputTensor(element_type, 1.0f / 255, 128); + } + + PackParams params{}; + params.axis = axis; + params.values_count = input_datas.size(); + Pack kernel(inputs, &output_tensor, params); + + kernel.configure(); + kernel.execute(); + + EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shape)); +} + +template <typename T> class PackTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types<uint8_t, float>; +TYPED_TEST_CASE(PackTest, DataTypes); + +TYPED_TEST(PackTest, ThreeInputs) +{ + Check<TypeParam>(/*input_shapes=*/{{2}, {2}, {2}}, + /*output_shape=*/{3, 2}, + /*input_datas=*/ + {{1, 4}, {2, 5}, {3, 6}}, + /*output_data=*/ + {1, 4, 2, 5, 3, 6}, /*axis=*/0); + + SUCCEED(); +} + +TYPED_TEST(PackTest, NegAxis) +{ + Check<TypeParam>(/*input_shapes=*/{{2}, {2}, {2}}, + /*output_shape=*/{2, 3}, + /*input_datas=*/ + {{1, 4}, {2, 5}, {3, 6}}, + /*output_data=*/ + {1, 2, 3, 4, 5, 6}, /*axis=*/-1); + + SUCCEED(); +} + +TEST(Pack, MismatchingInputValuesCount_NEG) +{ + std::vector<float> input1_data{1, 4}; + std::vector<float> input2_data{2, 5}; + std::vector<float> input3_data{3, 6}; + Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2}, input1_data); + Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({2}, input2_data); + Tensor input3_tensor = makeInputTensor<DataType::FLOAT32>({2}, input3_data); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + PackParams params{}; + { + params.axis = 0; + params.values_count = 2; + + Pack kernel({&input1_tensor, &input2_tensor, &input3_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); + } +} + +TEST(Pack, InvalidInputAxis_NEG) +{ + std::vector<float> input1_data{1, 4}; + std::vector<float> input2_data{2, 5}; + std::vector<float> input3_data{3, 6}; + Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({2}, input1_data); + Tensor input2_tensor = makeInputTensor<DataType::FLOAT32>({2}, input2_data); + Tensor input3_tensor = makeInputTensor<DataType::FLOAT32>({2}, input3_data); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + PackParams params{}; + { + params.axis = 2; + params.values_count = 3; + + Pack kernel({&input1_tensor, &input2_tensor, &input3_tensor}, &output_tensor, params); + EXPECT_ANY_THROW(kernel.configure()); + } +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Pad.cpp b/compiler/luci-interpreter/src/kernels/Pad.cpp index bdf3a2a95..3e76080a9 100644 --- a/compiler/luci-interpreter/src/kernels/Pad.cpp +++ b/compiler/luci-interpreter/src/kernels/Pad.cpp @@ -26,7 +26,7 @@ namespace kernels { Pad::Pad(const Tensor *input, const Tensor *paddings, Tensor *output) - : Kernel({input, paddings}, {output}) + : Kernel({input, paddings}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/Pad.test.cpp b/compiler/luci-interpreter/src/kernels/Pad.test.cpp index 4bee07629..75b2e560e 100644 --- a/compiler/luci-interpreter/src/kernels/Pad.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Pad.test.cpp @@ -34,8 +34,8 @@ TEST(Pad, Uint8) std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f); std::vector<float> input_data{-0.8, 0.2, 0.9, 0.7, 0.1, -0.3}; std::vector<int32_t> paddings_data{0, 0, 0, 2, 1, 3, 0, 0}; - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first, - quant_param.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first, quant_param.second, input_data); Tensor paddings_tensor = makeInputTensor<DataType::S32>({4, 2}, paddings_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); diff --git a/compiler/luci-interpreter/src/kernels/Pow.cpp b/compiler/luci-interpreter/src/kernels/Pow.cpp index a0c092d33..722c64024 100644 --- a/compiler/luci-interpreter/src/kernels/Pow.cpp +++ b/compiler/luci-interpreter/src/kernels/Pow.cpp @@ -27,7 +27,7 @@ namespace kernels { Pow::Pow(const Tensor *input1, const Tensor *input2, Tensor *output) - : Kernel({input1, input2}, {output}) + : Kernel({input1, input2}, {output}) { } @@ -59,7 +59,7 @@ template <typename T> void Pow::eval() const tflite::ArithmeticParams params{}; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { diff --git a/compiler/luci-interpreter/src/kernels/Prelu.cpp b/compiler/luci-interpreter/src/kernels/Prelu.cpp index e658d87b5..c4b288f1b 100644 --- a/compiler/luci-interpreter/src/kernels/Prelu.cpp +++ b/compiler/luci-interpreter/src/kernels/Prelu.cpp @@ -30,24 +30,58 @@ namespace kernels { Prelu::Prelu(const Tensor *input, const Tensor *alpha, Tensor *output) - : Kernel({input, alpha}, {output}) + : Kernel({input, alpha}, {output}) { } +Prelu::~Prelu() +{ + // Destructor declared to delete vector of alpha quantized data properly +} + void Prelu::configure() { LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); LUCI_INTERPRETER_CHECK(alpha()->element_type() == output()->element_type()); + LUCI_INTERPRETER_CHECK(input()->scales().size() <= 1); + LUCI_INTERPRETER_CHECK(output()->scales().size() <= 1); - if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16) + if (input()->element_type() == DataType::U8) { - if (input()->element_type() == DataType::S16) + LUCI_INTERPRETER_CHECK(alpha()->scales().size() <= 1); // remove when CWQ kernel arrives + _alpha_multipliers.resize(1); + double alpha_multiplier = input()->scale() * alpha()->scale() / output()->scale(); + quantizeMultiplier(alpha_multiplier, &_alpha_multipliers[0].multiplier, + &_alpha_multipliers[0].shift); + double identity_multiplier = input()->scale() / output()->scale(); + quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity); + } + else if (input()->element_type() == DataType::S16) + { + // Common check for correctness of quant params + LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && output()->zero_point() == 0); + for (size_t channel = 0; channel < alpha()->zero_points().size(); ++channel) { - LUCI_INTERPRETER_CHECK(input()->zero_point() == 0 && alpha()->zero_point() == 0 && - output()->zero_point() == 0); + LUCI_INTERPRETER_CHECK(alpha()->zero_points()[channel] == 0); } - double alpha_multiplier = input()->scale() * alpha()->scale() / output()->scale(); - quantizeMultiplier(alpha_multiplier, &_output_multiplier_alpha, &_output_shift_alpha); + // Prelu specific checks for CWQ + LUCI_INTERPRETER_CHECK(alpha()->quantized_dimension() == alpha()->shape().num_dims() - 1); + LUCI_INTERPRETER_CHECK(static_cast<int32_t>(alpha()->scales().size()) == + alpha()->shape().dim(alpha()->quantized_dimension())); + LUCI_INTERPRETER_CHECK(alpha()->shape().num_elements() == + input()->shape().dim(input()->shape().num_dims() - 1)); + + // all dimension of alpha except last one should be size 1 + for (int dim = 0; dim < alpha()->shape().num_dims() - 1; ++dim) + { + LUCI_INTERPRETER_CHECK(alpha()->shape().dim(dim) == 1); + } + + std::vector<double> real_multipliers = + getQuantizedConvolutionMultiplers(input()->scale(), alpha()->scales(), output()->scale()); + + _alpha_multipliers = quantizeMultipliers(real_multipliers); + double identity_multiplier = input()->scale() / output()->scale(); quantizeMultiplier(identity_multiplier, &_output_multiplier_identity, &_output_shift_identity); } @@ -84,9 +118,9 @@ void Prelu::evalFloat() const if (input()->shape() != alpha()->shape()) { tflite::reference_ops::BroadcastBinaryFunction4DSlow<float, float, float>( - getTensorShape(input()), getTensorData<float>(input()), getTensorShape(alpha()), - getTensorData<float>(alpha()), getTensorShape(output()), getTensorData<float>(output()), - PreluFunc); + getTensorShape(input()), getTensorData<float>(input()), getTensorShape(alpha()), + getTensorData<float>(alpha()), getTensorShape(output()), getTensorData<float>(output()), + PreluFunc); } else { @@ -109,44 +143,66 @@ void Prelu::evalQuantized() const op_params.output_offset = output()->zero_point(); op_params.output_shift_1 = _output_shift_identity; op_params.output_multiplier_1 = _output_multiplier_identity; - op_params.output_shift_2 = _output_shift_alpha; - op_params.output_multiplier_2 = _output_multiplier_alpha; + op_params.output_shift_2 = _alpha_multipliers[0].shift; + op_params.output_multiplier_2 = _alpha_multipliers[0].multiplier; if (input()->shape() != alpha()->shape()) { tflite::reference_ops::BroadcastPrelu4DSlow( - op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), - getTensorShape(alpha()), getTensorData<uint8_t>(alpha()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(alpha()), + getTensorData<uint8_t>(alpha()), getTensorShape(output()), getTensorData<uint8_t>(output())); } else { - tflite::reference_ops::Prelu<uint8_t>(op_params, getTensorShape(input()), - getTensorData<uint8_t>(input()), getTensorShape(alpha()), - getTensorData<uint8_t>(alpha()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + tflite::reference_ops::Prelu<uint8_t>( + op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(alpha()), + getTensorData<uint8_t>(alpha()), getTensorShape(output()), getTensorData<uint8_t>(output())); } } -void Prelu::evalQuantizedS16() const +static inline int16_t evalElemS16Prelu(int16_t input_val, int16_t alpha_val, + const ChannelQuantMultipliers &identity_mult, + const ChannelQuantMultipliers &alpha_mult) { constexpr int32_t quantized_min = std::numeric_limits<int16_t>::min(); constexpr int32_t quantized_max = std::numeric_limits<int16_t>::max(); - auto fn = [this, quantized_min, quantized_max](int16_t input_val, int16_t alpha_val) { - const int32_t output_val = - input_val >= 0 - ? tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier_identity, - _output_shift_identity) - : tflite::MultiplyByQuantizedMultiplier(input_val * alpha_val, _output_multiplier_alpha, - _output_shift_alpha); - const int32_t clamped_output = std::min(quantized_max, std::max(quantized_min, output_val)); - return static_cast<int16_t>(clamped_output); - }; - - BinaryOpBroadcastSlow(getTensorShape(input()), getTensorData<int16_t>(input()), - getTensorShape(alpha()), getTensorData<int16_t>(alpha()), - getTensorShape(output()), getTensorData<int16_t>(output()), fn); + const int32_t output_val = + input_val >= 0 ? tflite::MultiplyByQuantizedMultiplier(input_val, identity_mult.multiplier, + identity_mult.shift) + : tflite::MultiplyByQuantizedMultiplier(input_val * alpha_val, + alpha_mult.multiplier, alpha_mult.shift); + const int32_t clamped_output = std::min(quantized_max, std::max(quantized_min, output_val)); + return clamped_output; +} + +void Prelu::evalQuantizedS16() const +{ + // Note that this kernel assumes alpha is CWQ + tflite::RuntimeShape input_shape = getTensorShape(input()); + const int16_t *input_data = input()->data<int16_t>(); + const int16_t *alpha_data = alpha()->data<int16_t>(); + int16_t *output_data = output()->data<int16_t>(); + + const ChannelQuantMultipliers pos_mult{_output_shift_identity, _output_multiplier_identity}; + + const int last_dim = input()->shape().num_dims() - 1; + + int32_t outer_dims_size = 1; + for (int i = 0; i < last_dim; ++i) + outer_dims_size *= input_shape.Dims(i); + int32_t quant_dim_size = input_shape.Dims(last_dim); + + for (int32_t outer_dims = 0; outer_dims < outer_dims_size; ++outer_dims) + for (int32_t quant_channel = 0; quant_channel < quant_dim_size; ++quant_channel) + { + const ChannelQuantMultipliers &neg_mult = _alpha_multipliers[quant_channel]; + size_t offset = static_cast<size_t>(outer_dims) * static_cast<size_t>(quant_dim_size); + offset += quant_channel; + + output_data[offset] = + evalElemS16Prelu(input_data[offset], alpha_data[quant_channel], pos_mult, neg_mult); + } } } // namespace kernels diff --git a/compiler/luci-interpreter/src/kernels/Prelu.h b/compiler/luci-interpreter/src/kernels/Prelu.h index c7911a63f..08cb0eaa5 100644 --- a/compiler/luci-interpreter/src/kernels/Prelu.h +++ b/compiler/luci-interpreter/src/kernels/Prelu.h @@ -18,17 +18,22 @@ #define LUCI_INTERPRETER_KERNELS_PRELU_H #include "core/Kernel.h" +#include <vector> namespace luci_interpreter { namespace kernels { +class ChannelQuantMultipliers; + class Prelu : public Kernel { public: Prelu(const Tensor *input, const Tensor *alpha, Tensor *output); + ~Prelu(); + const Tensor *input() const { return _inputs[0]; } const Tensor *alpha() const { return _inputs[1]; } Tensor *output() const { return _outputs[0]; } @@ -42,8 +47,8 @@ private: void evalQuantizedS16() const; private: - int32_t _output_multiplier_alpha = 0; - int32_t _output_shift_alpha = 0; + std::vector<ChannelQuantMultipliers> _alpha_multipliers; + // TODO merge this into one ChannelQuantMultiplier object int32_t _output_multiplier_identity = 0; int32_t _output_shift_identity = 0; }; diff --git a/compiler/luci-interpreter/src/kernels/Prelu.test.cpp b/compiler/luci-interpreter/src/kernels/Prelu.test.cpp index 30702c826..9d9adf66f 100644 --- a/compiler/luci-interpreter/src/kernels/Prelu.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Prelu.test.cpp @@ -52,18 +52,18 @@ TEST(PreluTest, FloatSimple) /*output_shape=*/{2, 3}, /*input_data=*/ { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 }, /*alpha_data=*/ { - 0.0f, 0.5f, 0.1f, // Row 1 - 0.0f, 0.5f, 0.1f, // Row 2 + 0.0f, 0.5f, 0.1f, // Row 1 + 0.0f, 0.5f, 0.1f, // Row 2 }, /*output_data=*/ { - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -0.5f, -0.2f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -0.5f, -0.2f, // Row 2 }); SUCCEED(); @@ -75,19 +75,19 @@ TEST(PreluTest, FloatBroadcast) /*output_shape=*/{1, 2, 2, 3}, /*input_data=*/ { - 0.0f, 0.0f, 0.0f, // Row 1, Column 1 - 1.0f, 1.0f, 1.0f, // Row 1, Column 2 - -1.0f, -1.0f, -1.0f, // Row 2, Column 1 - -2.0f, -2.0f, -2.0f, // Row 2, Column 2 + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 1.0f, 1.0f, 1.0f, // Row 1, Column 2 + -1.0f, -1.0f, -1.0f, // Row 2, Column 1 + -2.0f, -2.0f, -2.0f, // Row 2, Column 2 }, /*alpha_data=*/ {0.0f, 1.0f, 2.0f}, /*output_data=*/ { - 0.0f, 0.0f, 0.0f, // Row 1, Column 1 - 1.0f, 1.0f, 1.0f, // Row 1, Column 2 - 0.0f, -1.0f, -2.0f, // Row 2, Column 1 - 0.0f, -2.0f, -4.0f, // Row 2, Column 2 + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 1.0f, 1.0f, 1.0f, // Row 1, Column 2 + 0.0f, -1.0f, -2.0f, // Row 2, Column 1 + 0.0f, -2.0f, -4.0f, // Row 2, Column 2 }); SUCCEED(); @@ -104,10 +104,10 @@ TEST(PreluTest, Uint8Simple) float kQuantizedTolerance = GetTolerance(-1.0, 1.0); std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-1.0f, 1.0f); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first, - quant_param.second, input_data); - Tensor alpha_tensor = makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first, - quant_param.second, alpha_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first, quant_param.second, input_data); + Tensor alpha_tensor = + makeInputTensor<DataType::U8>({1, 2, 3, 1}, quant_param.first, quant_param.second, alpha_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); @@ -124,33 +124,33 @@ TEST(PreluTest, Uint8Simple) TEST(PreluTest, Uint8Broadcast) { std::vector<float> input_data{ - 0.0f, 0.0f, 0.0f, // Row 1, Column 1 - 0.5f, 0.5f, 0.5f, // Row 1, Column 2 - -1.0f, -1.0f, -1.0f, // Row 2, Column 1 - -0.25f, -0.25f, -0.25f, // Row 2, Column 2 + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + -1.0f, -1.0f, -1.0f, // Row 2, Column 1 + -0.25f, -0.25f, -0.25f, // Row 2, Column 2 }; std::vector<float> alpha_data{0.0f, 0.5f, -0.5f}; std::vector<float> ref_output_data{ - 0.0f, 0.0f, 0.0f, // Row 1, Column 1 - 0.5f, 0.5f, 0.5f, // Row 1, Column 2 - 0.0f, -0.5f, 0.5f, // Row 2, Column 1 - 0.0f, -0.125f, 0.125f // Row 2, Column 2 + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + 0.0f, -0.5f, 0.5f, // Row 2, Column 1 + 0.0f, -0.125f, 0.125f // Row 2, Column 2 }; std::vector<float> ref_quant_output_data{ - 128, 128, 128, // Row 1, Column 1 - 192, 192, 192, // Row 1, Column 2 - 128, 64, 192, // Row 2, Column 1 - 128, 112, 144 // Row 2, Column 2 + 128, 128, 128, // Row 1, Column 1 + 192, 192, 192, // Row 1, Column 2 + 128, 64, 192, // Row 2, Column 1 + 128, 112, 144 // Row 2, Column 2 }; float kQuantizedTolerance = 2 * (1. / 256); const float kMin = -1; const float kMax = 127.f / 128.f; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(kMin, kMax); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 3}, quant_param.first, - quant_param.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 2, 3}, quant_param.first, quant_param.second, input_data); Tensor alpha_tensor = - makeInputTensor<DataType::U8>({1, 1, 3}, quant_param.first, quant_param.second, alpha_data); + makeInputTensor<DataType::U8>({1, 1, 3}, quant_param.first, quant_param.second, alpha_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); @@ -164,42 +164,114 @@ TEST(PreluTest, Uint8Broadcast) ::testing::ElementsAreArray(ref_quant_output_data)); } -TEST(PreluTest, SInt16Simple) +TEST(PreluTest, SInt16_LWQ_NEG) { - std::vector<float> input_data{-0.8f, 0.2f, 0.9f, 0.7f, 0.1f, -0.4f}; - std::vector<float> alpha_data{0.5f, 0.5f, 0.5f, 0.25f, 1.0f, 0.25f}; - std::vector<float> ref_output_data{-0.4f, 0.2f, 0.9f, 0.7f, 0.1f, -0.1f}; + // Rewrite this test in case layer-wise quantization for sint16 is supported + std::vector<float> input_data(6); // data is not important + std::vector<float> alpha_data(6); Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 3, 1}, 0.1, 0, input_data); Tensor alpha_tensor = makeInputTensor<DataType::S16>({1, 2, 3, 1}, 0.1, 0, alpha_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0); Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PreluTest, SInt16_CWQ_Simple) +{ + std::vector<float> input_data{-0.8f, 0.2f, 0.9f, -0.7f, 0.1f, -0.4f}; + std::vector<float> alpha_data{0.5f, 0.25f}; + std::vector<float> ref_output_data{-0.4f, 0.2f, 0.9f, -0.175f, 0.1f, -0.1f}; + + std::vector<float> alpha_scales{0.05f, 0.025f}; + std::vector<int32_t> zerop{0, 0}; + Tensor input_tensor = makeInputTensor<DataType::S16>({1, 1, 3, 2}, 0.1, 0, input_data); + Tensor alpha_tensor = makeInputTensor<DataType::S16>({2}, alpha_scales, zerop, 0, alpha_data); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.025, 0); + + Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); kernel.configure(); kernel.execute(); - EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 2, 3, 1})); + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 3, 2})); + EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(PreluTest, SInt16_CWQ_spatial_alpha_NEG) +{ + std::vector<float> input_data(6); // data is not important + std::vector<float> alpha_data(6); + + std::vector<float> alpha_scales{0.25f, 0.05f}; + std::vector<int32_t> zerop{0, 0}; + Tensor input_tensor = makeInputTensor<DataType::S16>({1, 1, 3, 2}, 0.1, 0, input_data); + Tensor alpha_tensor = + makeInputTensor<DataType::S16>({1, 1, 3, 2}, alpha_scales, zerop, 3, alpha_data); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0); + + Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PreluTest, SInt16_CWQ_wrong_dim_quant_NEG) +{ + std::vector<float> input_data(6); // data is not important + std::vector<float> alpha_data(6); + + std::vector<float> alpha_scales{0.25f}; + std::vector<int32_t> zerop{0}; + Tensor input_tensor = makeInputTensor<DataType::S16>({1, 1, 3, 2}, 0.1, 0, input_data); + Tensor alpha_tensor = + makeInputTensor<DataType::S16>({1, 1, 1, 2}, alpha_scales, zerop, 1, alpha_data); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.1, 0); + + Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PreluTest, SInt16_CWQ_uneven_shape1) +{ + std::vector<float> input_data{-0.8f, 0.2f, 0.9f, -0.7f, 0.1f, -0.4f}; + std::vector<float> alpha_data{0.5f, 0.25f}; + std::vector<float> ref_output_data{-0.4f, 0.2f, 0.9f, -0.175f, 0.1f, -0.1f}; + + std::vector<float> alpha_scales{0.05f, 0.025f}; + std::vector<int32_t> zerop{0, 0}; + Tensor input_tensor = makeInputTensor<DataType::S16>({1, 1, 3, 2}, 0.1, 0, input_data); + Tensor alpha_tensor = + makeInputTensor<DataType::S16>({1, 1, 2}, alpha_scales, zerop, 2, alpha_data); + Tensor output_tensor = makeOutputTensor(DataType::S16, 0.025, 0); + + Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + kernel.configure(); + kernel.execute(); + + EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray({1, 1, 3, 2})); EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data)); } -TEST(PreluTest, SInt16Broadcast) +TEST(PreluTest, SInt16_CWQ_uneven_shape2) { std::vector<float> input_data{ - 0.0f, 0.0f, 0.0f, // Row 1, Column 1 - 0.5f, 0.5f, 0.5f, // Row 1, Column 2 - -1.0f, -1.0f, -1.0f, // Row 2, Column 1 - -0.25f, -0.25f, -0.25f, // Row 2, Column 2 + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + -1.0f, -1.0f, -1.0f, // Row 2, Column 1 + -0.25f, -0.25f, -0.25f, // Row 2, Column 2 }; std::vector<float> alpha_data{0.0f, 0.5f, -0.5f}; std::vector<float> ref_output_data{ - 0.0f, 0.0f, 0.0f, // Row 1, Column 1 - 0.5f, 0.5f, 0.5f, // Row 1, Column 2 - 0.0f, -0.5f, 0.5f, // Row 2, Column 1 - 0.0f, -0.125f, 0.125f // Row 2, Column 2 + 0.0f, 0.0f, 0.0f, // Row 1, Column 1 + 0.5f, 0.5f, 0.5f, // Row 1, Column 2 + 0.0f, -0.5f, 0.5f, // Row 2, Column 1 + 0.0f, -0.125f, 0.125f // Row 2, Column 2 }; + std::vector<float> alpha_scales{1.f, 0.05f, 0.1f}; + std::vector<int32_t> zerop{0, 0, 0}; Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 2, 3}, 0.01, 0, input_data); - Tensor alpha_tensor = makeInputTensor<DataType::S16>({1, 1, 3}, 0.1, 0, alpha_data); + Tensor alpha_tensor = + makeInputTensor<DataType::S16>({1, 1, 1, 3}, alpha_scales, zerop, 3, alpha_data); Tensor output_tensor = makeOutputTensor(DataType::S16, 0.001, 0); Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); @@ -241,6 +313,43 @@ TEST(PreluTest, Invalid_Input_Type_NEG) EXPECT_ANY_THROW(kernel.execute()); } +TEST(PreluTest, Input_Output_U8_CWQ_NEG) +{ + std::vector<float> scales{1.f, 1.f}; + std::vector<int32_t> zerop{0, 0}; + std::vector<float> dummy_data(4, 0.f); + Tensor input_tensor = makeInputTensor<DataType::U8>({2, 2}, scales, zerop, 0, dummy_data); + Tensor alpha_tensor = makeInputTensor<DataType::U8>({2, 2}, scales, zerop, 0, dummy_data); + Tensor output_tensor = makeInputTensor<DataType::U8>({2, 2}, scales, zerop, 0, dummy_data); + + Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PreluTest, Input_Output_S16_CWQ_NEG) +{ + std::vector<float> scales{1.f, 1.f}; + std::vector<int32_t> zerop{0, 0}; + std::vector<float> dummy_data(4, 0.f); + Tensor input_tensor = makeInputTensor<DataType::S16>({2, 2}, scales, zerop, 0, dummy_data); + Tensor alpha_tensor = makeInputTensor<DataType::S16>({2, 2}, scales, zerop, 0, dummy_data); + Tensor output_tensor = makeInputTensor<DataType::S16>({2, 2}, scales, zerop, 0, dummy_data); + + Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +TEST(PreluTest, Mixing_U8_S16_NEG) +{ + std::vector<float> dummy_data(4, 0.f); + Tensor input_tensor = makeInputTensor<DataType::U8>({2, 2}, 1.f, 0, dummy_data); + Tensor alpha_tensor = makeInputTensor<DataType::S16>({2, 2}, 1.f, 0, dummy_data); + Tensor output_tensor = makeInputTensor<DataType::U8>({2, 2}, 1.f, 0, dummy_data); + + Prelu kernel(&input_tensor, &alpha_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + } // namespace } // namespace kernels } // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Relu.cpp b/compiler/luci-interpreter/src/kernels/Relu.cpp index a2e02d708..b5acf1d60 100644 --- a/compiler/luci-interpreter/src/kernels/Relu.cpp +++ b/compiler/luci-interpreter/src/kernels/Relu.cpp @@ -82,7 +82,7 @@ void Relu::evalQuantized() const params.output_shift = _output_shift; params.quantized_activation_min = - std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset); + std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset); params.quantized_activation_max = static_cast<int32_t>(std::numeric_limits<uint8_t>::max()); tflite::optimized_ops::ReluX(params, getTensorShape(input()), getTensorData<uint8_t>(input()), @@ -103,7 +103,7 @@ void Relu::evalQuantizedS16() const { const int32_t input_val = input_data[i]; int32_t output_val = - tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier, _output_shift); + tflite::MultiplyByQuantizedMultiplier(input_val, _output_multiplier, _output_shift); output_val = std::max(output_val, output_min); output_val = std::min(output_val, output_max); output_data[i] = static_cast<int16_t>(output_val); diff --git a/compiler/luci-interpreter/src/kernels/Relu.test.cpp b/compiler/luci-interpreter/src/kernels/Relu.test.cpp index cabefa733..6623a5b77 100644 --- a/compiler/luci-interpreter/src/kernels/Relu.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Relu.test.cpp @@ -30,13 +30,13 @@ using namespace testing; TEST(ReluTest, FloatSimple) { std::vector<float> input_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, -1.0f, -2.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, -1.0f, -2.0f, // Row 2 }; std::vector<float> ref_output_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 1.0f, 0.0f, 0.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 1.0f, 0.0f, 0.0f, // Row 2 }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input_data); @@ -54,16 +54,16 @@ TEST(ReluTest, FloatSimple) TEST(ReluTest, Uint8Quantized) { std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 7, 1, // + 0, -6, 2, 4, // + 3, -2, 7, 1, // }; // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. const float f_min = (-128.0 / 128.0) * 8; const float f_max = (127.0 / 128.0) * 8; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(f_min, f_max); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, - quant_param.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); Relu kernel(&input_tensor, &output_tensor); @@ -79,8 +79,8 @@ TEST(ReluTest, Uint8Quantized) TEST(ReluTest, Uint8Requantized) { std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 7, 1, // + 0, -6, 2, 4, // + 3, -2, 7, 1, // }; // Choose min / max in such a way that there are exactly 256 units to avoid rounding errors. @@ -90,8 +90,8 @@ TEST(ReluTest, Uint8Requantized) const float out_max = (255.0 / 256.0) * 8; std::pair<float, int32_t> quant_input = quantizationParams<uint8_t>(in_min, in_max); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first, - quant_input.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first, quant_input.second, input_data); std::pair<float, int32_t> quant_output = quantizationParams<uint8_t>(out_min, out_max); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second); @@ -109,12 +109,12 @@ TEST(ReluTest, Uint8Requantized) TEST(ReluTest, SInt16) { std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 7, 1, // + 0, -6, 2, 4, // + 3, -2, 7, 1, // }; std::vector<float> ref_output_data{ - 0, 0, 2, 4, // - 3, 0, 7, 1, // + 0, 0, 2, 4, // + 3, 0, 7, 1, // }; Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 4, 1}, 0.5, 0, input_data); diff --git a/compiler/luci-interpreter/src/kernels/Relu6.cpp b/compiler/luci-interpreter/src/kernels/Relu6.cpp index 1046ef27b..fa7aa504a 100644 --- a/compiler/luci-interpreter/src/kernels/Relu6.cpp +++ b/compiler/luci-interpreter/src/kernels/Relu6.cpp @@ -75,10 +75,10 @@ void Relu6::evalQuantized() const params.output_shift = _output_shift; params.quantized_activation_min = - std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset); + std::max(static_cast<int32_t>(std::numeric_limits<uint8_t>::min()), params.output_offset); params.quantized_activation_max = - std::min(static_cast<int32_t>(std::numeric_limits<uint8_t>::max()), - params.output_offset + static_cast<int32>(roundf(6.f / output()->scale()))); + std::min(static_cast<int32_t>(std::numeric_limits<uint8_t>::max()), + params.output_offset + static_cast<int32>(roundf(6.f / output()->scale()))); tflite::optimized_ops::ReluX(params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(output()), getTensorData<uint8_t>(output())); diff --git a/compiler/luci-interpreter/src/kernels/Relu6.test.cpp b/compiler/luci-interpreter/src/kernels/Relu6.test.cpp index a7f104d85..fe991389a 100644 --- a/compiler/luci-interpreter/src/kernels/Relu6.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Relu6.test.cpp @@ -30,13 +30,13 @@ using namespace testing; TEST(Relu6Test, FloatSimple) { std::vector<float> input_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 7.0f, -1.0f, -2.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 7.0f, -1.0f, -2.0f, // Row 2 }; std::vector<float> ref_output_data{ - 0.0f, 1.0f, 3.0f, // Row 1 - 6.0f, 0.0f, 0.0f, // Row 2 + 0.0f, 1.0f, 3.0f, // Row 1 + 6.0f, 0.0f, 0.0f, // Row 2 }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 3}, input_data); @@ -59,13 +59,13 @@ TEST(Relu6Test, Uint8Quantized) const float tolerance = (f_max - f_min) / 255.0; std::vector<float> input_data{ - 0, -6, 2, 8, // - -2, 3, 7, 1, // + 0, -6, 2, 8, // + -2, 3, 7, 1, // }; std::pair<float, int32_t> quant_param = quantizationParams<uint8_t>(f_min, f_max); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, - quant_param.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_param.first, quant_param.second, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.second); Relu6 kernel(&input_tensor, &output_tensor); @@ -89,13 +89,13 @@ TEST(Relu6Test, Uint8Requantized) const float tolerance = (in_max - in_min) / 255.0; std::vector<float> input_data{ - 0, -6, 2, 8, // - -2, 3, 7, 1, // + 0, -6, 2, 8, // + -2, 3, 7, 1, // }; std::pair<float, int32_t> quant_input = quantizationParams<uint8_t>(in_min, in_max); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first, - quant_input.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 4, 1}, quant_input.first, quant_input.second, input_data); std::pair<float, int32_t> quant_output = quantizationParams<uint8_t>(out_min, out_max); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_output.first, quant_output.second); diff --git a/compiler/luci-interpreter/src/kernels/Reshape.cpp b/compiler/luci-interpreter/src/kernels/Reshape.cpp index d88b5392a..61d3300b2 100644 --- a/compiler/luci-interpreter/src/kernels/Reshape.cpp +++ b/compiler/luci-interpreter/src/kernels/Reshape.cpp @@ -65,7 +65,7 @@ static void resolveUnknownDimension(const Shape &input_shape, Shape *output_shap } Reshape::Reshape(const Tensor *input, const Tensor *shape, Tensor *output) - : Kernel({input, shape}, {output}) + : Kernel({input, shape}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp b/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp index 9385855cf..0e9bcc920 100644 --- a/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp +++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.cpp @@ -28,7 +28,7 @@ namespace kernels ResizeBilinear::ResizeBilinear(const Tensor *input, const Tensor *size, Tensor *output, const ResizeBilinearParams ¶ms) - : KernelWithParams<ResizeBilinearParams>({input, size}, {output}, params) + : KernelWithParams<ResizeBilinearParams>({input, size}, {output}, params) { } @@ -57,14 +57,13 @@ void ResizeBilinear::execute() const { case DataType::FLOAT32: tflite::optimized_ops::ResizeBilinear( - op_params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(size()), - getTensorData<int32_t>(size()), getTensorShape(output()), getTensorData<float>(output())); + op_params, getTensorShape(input()), getTensorData<float>(input()), getTensorShape(size()), + getTensorData<int32_t>(size()), getTensorShape(output()), getTensorData<float>(output())); break; case DataType::U8: tflite::optimized_ops::ResizeBilinear( - op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), - getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(size()), + getTensorData<int32_t>(size()), getTensorShape(output()), getTensorData<uint8_t>(output())); break; default: throw std::runtime_error("Unsupported type."); diff --git a/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp b/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp index 51c1359da..68ef6e6c1 100644 --- a/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp +++ b/compiler/luci-interpreter/src/kernels/ResizeBilinear.test.cpp @@ -88,19 +88,19 @@ TYPED_TEST(ResizeBilinearTest, SimpleTest) { Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }, {3, 3}, { - 3, 5, 6, // - 7, 9, 10, // - 9, 11, 12, // - 4, 8, 10, // - 8, 12, 14, // - 10, 14, 16, // + 3, 5, 6, // + 7, 9, 10, // + 9, 11, 12, // + 4, 8, 10, // + 8, 12, 14, // + 10, 14, 16, // }, false, false); SUCCEED(); @@ -110,19 +110,19 @@ TEST(ResizeBilinearTest, HalfPixelCenterFloatTest) { Check<float>({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, { - 1, 2, // - 3, 4, // - 1, 2, // - 3, 4 // + 1, 2, // + 3, 4, // + 1, 2, // + 3, 4 // }, {3, 3}, { - 1, 1.5, 2, // - 2, 2.5, 3, // - 3, 3.5, 4, // - 1, 1.5, 2, // - 2, 2.5, 3, // - 3, 3.5, 4, // + 1, 1.5, 2, // + 2, 2.5, 3, // + 3, 3.5, 4, // + 1, 1.5, 2, // + 2, 2.5, 3, // + 3, 3.5, 4, // }, false, true); SUCCEED(); @@ -132,19 +132,19 @@ TEST(ResizeBilinearTest, HalfPixelCenterUint8Test) { Check<uint8_t>({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 12, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 12, 16 // }, {3, 3}, { - 2, 4, 6, // - 6, 7, 9, // - 9, 10, 12, // - 4, 7, 10, // - 8, 10, 13, // - 12, 14, 16, // + 2, 4, 6, // + 6, 7, 9, // + 9, 10, 12, // + 4, 7, 10, // + 8, 10, 13, // + 12, 14, 16, // }, false, true); SUCCEED(); @@ -153,10 +153,10 @@ TEST(ResizeBilinearTest, HalfPixelCenterUint8Test) TEST(ResizeBilinearTest, InputShapeInvalid_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }); Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -172,10 +172,10 @@ TEST(ResizeBilinearTest, InputShapeInvalid_NEG) TEST(ResizeBilinearTest, SizeShapeInvalid_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }); Tensor size_tensor = makeInputTensor<DataType::S32>({2, 1}, {3, 3}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -191,10 +191,10 @@ TEST(ResizeBilinearTest, SizeShapeInvalid_NEG) TEST(ResizeBilinearTest, SizeDimInvalid_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }); Tensor size_tensor = makeInputTensor<DataType::S32>({3}, {3, 3, 1}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -210,10 +210,10 @@ TEST(ResizeBilinearTest, SizeDimInvalid_NEG) TEST(ResizeBilinearTest, InvalidParams_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }); Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp index e4ad8f742..c52264997 100644 --- a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp +++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.cpp @@ -30,7 +30,7 @@ namespace kernels ResizeNearestNeighbor::ResizeNearestNeighbor(const Tensor *input, const Tensor *size, Tensor *output, const ResizeNearestNeighborParams ¶ms) - : KernelWithParams<ResizeNearestNeighborParams>({input, size}, {output}, params) + : KernelWithParams<ResizeNearestNeighborParams>({input, size}, {output}, params) { } @@ -57,15 +57,13 @@ void ResizeNearestNeighbor::execute() const { case DataType::FLOAT32: tflite::reference_ops::ResizeNearestNeighbor( - op_params, getTensorShape(input()), getTensorData<int32_t>(input()), - getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()), - getTensorData<int32_t>(output())); + op_params, getTensorShape(input()), getTensorData<int32_t>(input()), getTensorShape(size()), + getTensorData<int32_t>(size()), getTensorShape(output()), getTensorData<int32_t>(output())); break; case DataType::U8: tflite::optimized_ops::ResizeNearestNeighbor( - op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), - getTensorShape(size()), getTensorData<int32_t>(size()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), getTensorShape(size()), + getTensorData<int32_t>(size()), getTensorShape(output()), getTensorData<uint8_t>(output())); break; default: throw std::runtime_error("Unsupported type."); diff --git a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp index 9a804cca7..0b36a29af 100644 --- a/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp +++ b/compiler/luci-interpreter/src/kernels/ResizeNearestNeighbor.test.cpp @@ -59,10 +59,10 @@ void Check<uint8_t>(std::initializer_list<int32_t> input_shape, bool half_pixel_centers) { std::pair<float, int32_t> quant_param = - quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f, - std::max(input_data) > 0 ? std::max(input_data) : 0.f); + quantizationParams<uint8_t>(std::min(input_data) < 0 ? std::min(input_data) : 0.f, + std::max(input_data) > 0 ? std::max(input_data) : 0.f); Tensor input_tensor = - makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data); + makeInputTensor<DataType::U8>(input_shape, quant_param.first, quant_param.second, input_data); Tensor size_tensor = makeInputTensor<DataType::S32>(size_shape, size_data); Tensor output_tensor = makeOutputTensor(DataType::U8, quant_param.first, quant_param.first); @@ -90,19 +90,19 @@ TYPED_TEST(ResizeNearestNeighborTest, SimpleTest) { Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }, {3, 3}, { - 3, 3, 6, // - 3, 3, 6, // - 9, 9, 12, // - 4, 4, 10, // - 4, 4, 10, // - 10, 10, 16, // + 3, 3, 6, // + 3, 3, 6, // + 9, 9, 12, // + 4, 4, 10, // + 4, 4, 10, // + 10, 10, 16, // }, false, false); } @@ -111,19 +111,19 @@ TYPED_TEST(ResizeNearestNeighborTest, AlignCenterTest) { Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }, {3, 3}, { - 3, 6, 6, // - 9, 12, 12, // - 9, 12, 12, // - 4, 10, 10, // - 10, 16, 16, // - 10, 16, 16, // + 3, 6, 6, // + 9, 12, 12, // + 9, 12, 12, // + 4, 10, 10, // + 10, 16, 16, // + 10, 16, 16, // }, true, false); } @@ -132,19 +132,19 @@ TYPED_TEST(ResizeNearestNeighborTest, HalfPixelCenterTest) { Check<TypeParam>({2, 2, 2, 1}, {2}, {2, 3, 3, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }, {3, 3}, { - 3, 6, 6, // - 9, 12, 12, // - 9, 12, 12, // - 4, 10, 10, // - 10, 16, 16, // - 10, 16, 16, // + 3, 6, 6, // + 9, 12, 12, // + 9, 12, 12, // + 4, 10, 10, // + 10, 16, 16, // + 10, 16, 16, // }, false, true); } @@ -152,10 +152,10 @@ TYPED_TEST(ResizeNearestNeighborTest, HalfPixelCenterTest) TEST(ResizeNearestNeighborTest, InputShapeInvalid_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }); Tensor size_tensor = makeInputTensor<DataType::S32>({2}, {3, 3}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -171,10 +171,10 @@ TEST(ResizeNearestNeighborTest, InputShapeInvalid_NEG) TEST(ResizeNearestNeighborTest, SizeShapeInvalid_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }); Tensor size_tensor = makeInputTensor<DataType::S32>({2, 1}, {3, 3}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -190,10 +190,10 @@ TEST(ResizeNearestNeighborTest, SizeShapeInvalid_NEG) TEST(ResizeNearestNeighborTest, SizeDimInvalid_NEG) { Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 2, 2, 1}, { - 3, 6, // - 9, 12, // - 4, 10, // - 10, 16 // + 3, 6, // + 9, 12, // + 4, 10, // + 10, 16 // }); Tensor size_tensor = makeInputTensor<DataType::S32>({3}, {3, 3, 1}); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); diff --git a/compiler/luci-interpreter/src/kernels/Reverse.cpp b/compiler/luci-interpreter/src/kernels/Reverse.cpp index a46308412..e9893fadc 100644 --- a/compiler/luci-interpreter/src/kernels/Reverse.cpp +++ b/compiler/luci-interpreter/src/kernels/Reverse.cpp @@ -25,7 +25,7 @@ namespace kernels { Reverse::Reverse(const Tensor *input, const Tensor *axes, Tensor *output) - : Kernel({input, axes}, {output}) + : Kernel({input, axes}, {output}) { } @@ -69,8 +69,8 @@ void Reverse::execute() const break; case DataType::U8: tflite::reference_ops::Reverse<uint8_t>( - axis_value, getTensorShape(input()), getTensorData<uint8_t>(input()), - getTensorShape(output()), getTensorData<uint8_t>(output())); + axis_value, getTensorShape(input()), getTensorData<uint8_t>(input()), + getTensorShape(output()), getTensorData<uint8_t>(output())); break; default: throw std::runtime_error("Unsupported output type"); diff --git a/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp b/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp index d33b800be..b93a04ddd 100644 --- a/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Rsqrt.test.cpp @@ -43,17 +43,17 @@ void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int TEST(RsqrtTest, SimpleRsqrt) { Check( - /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, - /*input_data=*/ - { - 5, 4, 8, 2, // - 6, 7.5, 9, 0.3, // - }, - /*output_data=*/ - { - 0.44721360, 0.5, 0.35355339, 0.70710678, // - 0.40824829, 0.36514837, 0.33333333, 1.8257419, // - }); + /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, + /*input_data=*/ + { + 5, 4, 8, 2, // + 6, 7.5, 9, 0.3, // + }, + /*output_data=*/ + { + 0.44721360, 0.5, 0.35355339, 0.70710678, // + 0.40824829, 0.36514837, 0.33333333, 1.8257419, // + }); } TEST(RsqrtTest, Input_Output_Type_NEG) diff --git a/compiler/luci-interpreter/src/kernels/Slice.cpp b/compiler/luci-interpreter/src/kernels/Slice.cpp index c4bc3c57c..626521815 100644 --- a/compiler/luci-interpreter/src/kernels/Slice.cpp +++ b/compiler/luci-interpreter/src/kernels/Slice.cpp @@ -29,7 +29,7 @@ namespace kernels const int max_dim = 4; Slice::Slice(const Tensor *input, const Tensor *begin, const Tensor *size, Tensor *output) - : Kernel({input, begin, size}, {output}) + : Kernel({input, begin, size}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/Softmax.cpp b/compiler/luci-interpreter/src/kernels/Softmax.cpp index 642c0ad75..8e29f53ee 100644 --- a/compiler/luci-interpreter/src/kernels/Softmax.cpp +++ b/compiler/luci-interpreter/src/kernels/Softmax.cpp @@ -30,7 +30,7 @@ namespace kernels { Softmax::Softmax(const Tensor *input, Tensor *output, const SoftmaxParams ¶ms) - : KernelWithParams<SoftmaxParams>({input}, {output}, params) + : KernelWithParams<SoftmaxParams>({input}, {output}, params) { } diff --git a/compiler/luci-interpreter/src/kernels/Softmax.test.cpp b/compiler/luci-interpreter/src/kernels/Softmax.test.cpp index d3d8209a5..c69a2f9cc 100644 --- a/compiler/luci-interpreter/src/kernels/Softmax.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Softmax.test.cpp @@ -51,15 +51,15 @@ void Check<uint8_t>(std::initializer_list<int32_t> input_shape, std::initializer_list<float> output_data) { std::pair<float, int32_t> input_quant_param = - quantizationParams<uint8_t>(std::min<float>(std::min<float>(input_data), 0.f), - std::max<float>(std::max<float>(input_data), 0.f)); + quantizationParams<uint8_t>(std::min<float>(std::min<float>(input_data), 0.f), + std::max<float>(std::max<float>(input_data), 0.f)); std::pair<float, int32_t> output_quant_param = - quantizationParams<uint8_t>(std::min<float>(std::min<float>(output_data), 0.f), - std::max<float>(std::max<float>(output_data), 0.f)); + quantizationParams<uint8_t>(std::min<float>(std::min<float>(output_data), 0.f), + std::max<float>(std::max<float>(output_data), 0.f)); Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first, input_quant_param.second, input_data); Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); SoftmaxParams params{}; params.beta = 0.1; @@ -84,16 +84,16 @@ TYPED_TEST(SoftmaxTest, Simple) { Check<TypeParam>({2, 1, 2, 3}, {2, 1, 2, 3}, { - 5, -9, 8, // - -7, 2, -4, // - 1, -2, 9, // - 3, -6, -1, // + 5, -9, 8, // + -7, 2, -4, // + 1, -2, 9, // + 3, -6, -1, // }, { - 0.38514, 0.09497, 0.51989, // - 0.20792, 0.51141, 0.28067, // - 0.25212, 0.18678, 0.56110, // - 0.48149, 0.19576, 0.32275, // + 0.38514, 0.09497, 0.51989, // + 0.20792, 0.51141, 0.28067, // + 0.25212, 0.18678, 0.56110, // + 0.48149, 0.19576, 0.32275, // }); } diff --git a/compiler/luci-interpreter/src/kernels/SpaceToBatchND.cpp b/compiler/luci-interpreter/src/kernels/SpaceToBatchND.cpp new file mode 100644 index 000000000..2f6a47925 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/SpaceToBatchND.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2019 The TensorFlow Authors. 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 "kernels/SpaceToBatchND.h" +#include "kernels/Utils.h" + +#include <tensorflow/lite/kernels/internal/optimized/optimized_ops.h> + +#include <stdexcept> + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +const int kInputMinDimensionNum = 3; +const int kInputMaxDimensionNum = 4; + +} // namespace + +SpaceToBatchND::SpaceToBatchND(const Tensor *input, const Tensor *block_shape, + const Tensor *paddings, Tensor *output) + : Kernel({input, block_shape, paddings}, {output}) +{ +} + +void SpaceToBatchND::configure() +{ + const auto *block_shape_data = block_shape()->data<int32_t>(); + const auto *paddings_data = paddings()->data<int32_t>(); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() >= kInputMinDimensionNum); + LUCI_INTERPRETER_CHECK(input()->shape().num_dims() <= kInputMaxDimensionNum); + LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); + + int spatial_dims_num = input()->shape().num_dims() - 2; + + LUCI_INTERPRETER_CHECK(block_shape()->shape().num_dims() == 1); + LUCI_INTERPRETER_CHECK(block_shape()->shape().dim(0) == spatial_dims_num); + + LUCI_INTERPRETER_CHECK(paddings()->shape().num_dims() == 2); + LUCI_INTERPRETER_CHECK(paddings()->shape().dim(0) == spatial_dims_num); + LUCI_INTERPRETER_CHECK(paddings()->shape().dim(1) == 2); + + Shape output_shape = Shape(input()->shape().num_dims()); + int output_batch_size = input()->shape().dim(0); + for (int i = 0; i < spatial_dims_num; ++i) + { + int final_dim_size = + (input()->shape().dim(i + 1) + paddings_data[i * 2] + paddings_data[i * 2 + 1]); + LUCI_INTERPRETER_CHECK(final_dim_size % block_shape_data[i] == 0); + output_shape.dim(i + 1) = final_dim_size / block_shape_data[i]; + output_batch_size = output_batch_size * block_shape_data[i]; + } + output_shape.dim(0) = output_batch_size; + output_shape.dim(input()->shape().num_dims() - 1) = + input()->shape().dim(input()->shape().num_dims() - 1); + output()->resize(output_shape); +} + +void SpaceToBatchND::execute() const +{ + switch (input()->element_type()) + { + tflite::SpaceToBatchParams op_params; + case DataType::FLOAT32: + op_params.output_offset = 0; + tflite::optimized_ops::SpaceToBatchND( + op_params, getTensorShape(input()), getTensorData<float>(input()), + getTensorShape(block_shape()), getTensorData<int32_t>(block_shape()), + getTensorShape(paddings()), getTensorData<int32_t>(paddings()), getTensorShape(output()), + getTensorData<float>(output())); + break; + case DataType::U8: + op_params.output_offset = output()->zero_point(); + tflite::optimized_ops::SpaceToBatchND( + op_params, getTensorShape(input()), getTensorData<uint8_t>(input()), + getTensorShape(block_shape()), getTensorData<int32_t>(block_shape()), + getTensorShape(paddings()), getTensorData<int32_t>(paddings()), getTensorShape(output()), + getTensorData<uint8_t>(output())); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/SpaceToBatchND.h b/compiler/luci-interpreter/src/kernels/SpaceToBatchND.h new file mode 100644 index 000000000..0893003bb --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/SpaceToBatchND.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 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 LUCI_INTERPRETER_KERNELS_SPACETOBATCHND_H +#define LUCI_INTERPRETER_KERNELS_SPACETOBATCHND_H + +#include "core/Kernel.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class SpaceToBatchND : public Kernel +{ +public: + SpaceToBatchND(const Tensor *input, const Tensor *block_shape, const Tensor *paddings, + Tensor *output); + + const Tensor *input() const { return _inputs[0]; } + const Tensor *block_shape() const { return _inputs[1]; } + const Tensor *paddings() const { return _inputs[2]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SPACETOBATCHND_H diff --git a/compiler/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp b/compiler/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp new file mode 100644 index 000000000..a6ec6f23f --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/SpaceToBatchND.test.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 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 "kernels/SpaceToBatchND.h" +#include "kernels/TestUtils.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +template <typename T> +void Check(std::initializer_list<int32_t> input_shape, + std::initializer_list<int32_t> block_shape_shape, + std::initializer_list<int32_t> paddings_shape, + std::initializer_list<int32_t> output_shape, std::initializer_list<float> input_data, + std::initializer_list<int32_t> block_shape_data, + std::initializer_list<int32_t> paddings_data, std::initializer_list<float> output_data) +{ + constexpr DataType element_type = getElementType<T>(); + Tensor input_tensor = makeInputTensor<element_type>(input_shape, input_data); + Tensor block_shape_tensor = makeInputTensor<DataType::S32>(block_shape_shape, block_shape_data); + Tensor paddings_tensor = makeInputTensor<DataType::S32>(paddings_shape, paddings_data); + Tensor output_tensor = makeOutputTensor(element_type); + + SpaceToBatchND kernel(&input_tensor, &block_shape_tensor, &paddings_tensor, &output_tensor); + kernel.configure(); + kernel.execute(); + + EXPECT_THAT(extractTensorData<T>(output_tensor), ::testing::ElementsAreArray(output_data)); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template <> +void Check<uint8_t>( + std::initializer_list<int32_t> input_shape, std::initializer_list<int32_t> block_shape_shape, + std::initializer_list<int32_t> paddings_shape, std::initializer_list<int32_t> output_shape, + std::initializer_list<float> input_data, std::initializer_list<int32_t> block_shape_data, + std::initializer_list<int32_t> paddings_data, std::initializer_list<float> output_data) +{ + std::pair<float, int32_t> input_quant_param = + quantizationParams<uint8_t>(std::min(input_data), std::max(input_data)); + Tensor input_tensor = makeInputTensor<DataType::U8>(input_shape, input_quant_param.first, + input_quant_param.second, input_data); + Tensor block_shape_tensor = makeInputTensor<DataType::S32>(block_shape_shape, block_shape_data); + Tensor paddings_tensor = makeInputTensor<DataType::S32>(paddings_shape, paddings_data); + Tensor output_tensor = + makeOutputTensor(DataType::U8, input_quant_param.first, input_quant_param.second); + + SpaceToBatchND kernel(&input_tensor, &block_shape_tensor, &paddings_tensor, &output_tensor); + kernel.configure(); + kernel.execute(); + + EXPECT_THAT(dequantizeTensorData(output_tensor), + FloatArrayNear(output_data, output_tensor.scale())); + EXPECT_THAT(extractTensorShape(output_tensor), output_shape); +} + +template <typename T> class SpaceToBatchNDTest : public ::testing::Test +{ +}; + +using DataTypes = ::testing::Types<float, uint8_t>; +TYPED_TEST_CASE(SpaceToBatchNDTest, DataTypes); + +TYPED_TEST(SpaceToBatchNDTest, Simple) +{ + Check<TypeParam>(/*input_shape=*/{1, 5, 2, 1}, /*block_shape_shape=*/{2}, + /*paddings_shape=*/{2, 2}, + /*output_shape=*/{6, 2, 2, 1}, + /*input_data=*/{-1.0, 0.2, -0.3, 0.4, -0.5, 0.6, -0.7, 0.8, -0.9, 1.0}, + /*block_shape_data=*/{3, 2}, /*paddings_data=*/{1, 0, 2, 0}, + /*output_data=*/{0, 0, 0, -0.5, 0, 0, 0, 0.6, 0, -1.0, 0, -0.7, + 0, 0.2, 0, 0.8, 0, -0.3, 0, -0.9, 0, 0.4, 0, 1.0}); +} + +TEST(SpaceToBatchNDTest, Invalid_Shape_NEG) +{ + Tensor input_tensor = + makeInputTensor<DataType::FLOAT32>({1, 3, 3, 1}, {1, 2, 3, 4, 5, 6, 7, 8, 9}); + Tensor block_shape_tensor = makeInputTensor<DataType::S32>({2}, {2, 2}); + Tensor paddings_tensor = makeInputTensor<DataType::S32>({2, 2}, {0, 0, 0, 0}); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SpaceToBatchND kernel(&input_tensor, &block_shape_tensor, &paddings_tensor, &output_tensor); + EXPECT_ANY_THROW(kernel.configure()); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp b/compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp index 6a5bd7cf8..fc999372a 100644 --- a/compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp +++ b/compiler/luci-interpreter/src/kernels/SpaceToDepth.cpp @@ -24,7 +24,7 @@ namespace kernels { SpaceToDepth::SpaceToDepth(const Tensor *input, Tensor *output, const SpaceToDepthParams ¶ms) - : KernelWithParams<SpaceToDepthParams>({input}, {output}, params) + : KernelWithParams<SpaceToDepthParams>({input}, {output}, params) { } diff --git a/compiler/luci-interpreter/src/kernels/Split.cpp b/compiler/luci-interpreter/src/kernels/Split.cpp index 325b1c22f..0da0f3779 100644 --- a/compiler/luci-interpreter/src/kernels/Split.cpp +++ b/compiler/luci-interpreter/src/kernels/Split.cpp @@ -26,7 +26,7 @@ namespace kernels { Split::Split(const Tensor *axis, const Tensor *input, std::vector<Tensor *> outputs) - : Kernel({axis, input}, std::move(outputs)) + : Kernel({axis, input}, std::move(outputs)) { } diff --git a/compiler/luci-interpreter/src/kernels/Split.test.cpp b/compiler/luci-interpreter/src/kernels/Split.test.cpp index 2147d15c1..c558928e8 100644 --- a/compiler/luci-interpreter/src/kernels/Split.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Split.test.cpp @@ -72,44 +72,48 @@ TYPED_TEST(SplitTest, FourDimensional) Check<TypeParam>(/*axis=*/0, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, { - {1, 2, 3, 4, 5, 6, 7, 8}, // - {9, 10, 11, 12, 13, 14, 15, 16}, // + {1, 2, 3, 4, 5, 6, 7, 8}, // + {9, 10, 11, 12, 13, 14, 15, 16}, // }); Check<TypeParam>( - /*axis=*/1, /*num_splits=*/2, {2, 2, 2, 2}, {2, 1, 2, 2}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, { - {1, 2, 3, 4, 9, 10, 11, 12}, // - {5, 6, 7, 8, 13, 14, 15, 16}, // - }); + /*axis=*/1, /*num_splits=*/2, {2, 2, 2, 2}, {2, 1, 2, 2}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 2, 3, 4, 9, 10, 11, 12}, // + {5, 6, 7, 8, 13, 14, 15, 16}, // + }); Check<TypeParam>( - /*axis=*/2, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 1, 2}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, { - {1, 2, 5, 6, 9, 10, 13, 14}, // - {3, 4, 7, 8, 11, 12, 15, 16}, // - }); + /*axis=*/2, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 1, 2}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 2, 5, 6, 9, 10, 13, 14}, // + {3, 4, 7, 8, 11, 12, 15, 16}, // + }); Check<TypeParam>( - /*axis=*/3, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 2, 1}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, { - {1, 3, 5, 7, 9, 11, 13, 15}, // - {2, 4, 6, 8, 10, 12, 14, 16}, // - }); + /*axis=*/3, /*num_splits=*/2, {2, 2, 2, 2}, {2, 2, 2, 1}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 3, 5, 7, 9, 11, 13, 15}, // + {2, 4, 6, 8, 10, 12, 14, 16}, // + }); } TYPED_TEST(SplitTest, OneDimensional) { Check<TypeParam>( - /*axis=*/0, /*num_splits=*/8, {8}, {1}, {1, 2, 3, 4, 5, 6, 7, 8}, - {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}}); + /*axis=*/0, /*num_splits=*/8, {8}, {1}, {1, 2, 3, 4, 5, 6, 7, 8}, + {{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}}); } TYPED_TEST(SplitTest, NegativeAxis) { Check<TypeParam>( - /*axis=*/-4, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, { - {1, 2, 3, 4, 5, 6, 7, 8}, // - {9, 10, 11, 12, 13, 14, 15, 16}, - }); + /*axis=*/-4, /*num_splits=*/2, {2, 2, 2, 2}, {1, 2, 2, 2}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + { + {1, 2, 3, 4, 5, 6, 7, 8}, // + {9, 10, 11, 12, 13, 14, 15, 16}, + }); } } // namespace diff --git a/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp b/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp index 504db4493..e40a91e97 100644 --- a/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Sqrt.test.cpp @@ -43,17 +43,17 @@ void Check(std::initializer_list<int32_t> input_shape, std::initializer_list<int TEST(SqrtTest, SimpleSqrt) { Check( - /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, - /*input_data=*/ - { - 0, 8, 2, 4, // - 3, 7, 10, 0.3, // - }, - /*output_data=*/ - { - 0.0, 2.8284271, 1.4142136, 2, // - 1.7320508, 2.6457513, 3.1622777, 0.54772256, // - }); + /*input_shape=*/{1, 2, 4, 1}, /*output_shape=*/{1, 2, 4, 1}, + /*input_data=*/ + { + 0, 8, 2, 4, // + 3, 7, 10, 0.3, // + }, + /*output_data=*/ + { + 0.0, 2.8284271, 1.4142136, 2, // + 1.7320508, 2.6457513, 3.1622777, 0.54772256, // + }); } TEST(SqrtTest, Input_Output_Type_NEG) diff --git a/compiler/luci-interpreter/src/kernels/SquaredDifference.cpp b/compiler/luci-interpreter/src/kernels/SquaredDifference.cpp new file mode 100644 index 000000000..3bafeba4a --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/SquaredDifference.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2018 The TensorFlow Authors. 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 "kernels/SquaredDifference.h" + +#include "kernels/Utils.h" + +#include "kernels/BinaryOpCommon.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +SquaredDifference::SquaredDifference(const Tensor *input1, const Tensor *input2, Tensor *output) + : Kernel({input1, input2}, {output}) +{ +} + +void SquaredDifference::configure() +{ + LUCI_INTERPRETER_CHECK(input1()->element_type() == input2()->element_type()) + LUCI_INTERPRETER_CHECK(input1()->element_type() == output()->element_type()) + output()->resize(calculateShapeForBroadcast(input1()->shape(), input2()->shape())); +} + +void SquaredDifference::execute() const +{ + switch (input1()->element_type()) + { + case DataType::FLOAT32: + evalSquaredDifference<float>(); + break; + default: + throw std::runtime_error("Unsupported type."); + } +} + +template <typename T> inline void SquaredDifference::evalSquaredDifference() const +{ + BinaryOpBroadcastSlow(getTensorShape(input1()), getTensorData<T>(input1()), + getTensorShape(input2()), getTensorData<T>(input2()), + getTensorShape(output()), getTensorData<T>(output()), [](T x, T y) { + const T difference = x - y; + return difference * difference; + }); +} + +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/SquaredDifference.h b/compiler/luci-interpreter/src/kernels/SquaredDifference.h new file mode 100644 index 000000000..9327caf93 --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/SquaredDifference.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 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 LUCI_INTERPRETER_KERNELS_SQUAREDDIFFERENCE_H +#define LUCI_INTERPRETER_KERNELS_SQUAREDDIFFERENCE_H + +#include "core/Kernel.h" +#include "core/KernelParams.h" + +namespace luci_interpreter +{ +namespace kernels +{ + +class SquaredDifference : public Kernel +{ +public: + SquaredDifference(const Tensor *input1, const Tensor *input2, Tensor *output); + + const Tensor *input1() const { return _inputs[0]; } + const Tensor *input2() const { return _inputs[1]; } + Tensor *output() const { return _outputs[0]; } + + void configure() override; + void execute() const override; + +private: + template <typename T> inline void evalSquaredDifference() const; +}; + +} // namespace kernels +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_KERNELS_SQUAREDDIFFERENCE_H diff --git a/compiler/luci-interpreter/src/kernels/SquaredDifference.test.cpp b/compiler/luci-interpreter/src/kernels/SquaredDifference.test.cpp new file mode 100644 index 000000000..a72eaadfa --- /dev/null +++ b/compiler/luci-interpreter/src/kernels/SquaredDifference.test.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2017 The TensorFlow Authors. 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 "kernels/SquaredDifference.h" +#include "kernels/TestUtils.h" + +namespace luci_interpreter +{ +namespace kernels +{ +namespace +{ + +using namespace testing; + +TEST(SquaredDifferenceTest, Float) +{ + Shape input_shape{3, 1, 2}; + std::vector<float> input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44}; + std::vector<float> input_data2{-1.0, 0.0, 1.0, 12.0, -3.0, -1.43}; + Tensor input_tensor1 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data1); + Tensor input_tensor2 = makeInputTensor<DataType::FLOAT32>(input_shape, input_data2); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SquaredDifference kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + kernel.execute(); + + std::vector<float> ref_output_data{4.0, 0.0, 4.0, 1.0, 1.0, 0.0001}; + EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); +} + +TEST(SquaredDifferenceTest, FloatBroadcast) +{ + Shape input_shape1{3, 1, 2}; + Shape input_shape2{1}; + std::vector<float> input_data1{1.0, 0.0, -1.0, 11.0, -2.0, -1.44}; + std::vector<float> input_data2{1.0}; + Tensor input_tensor1 = makeInputTensor<DataType::FLOAT32>(input_shape1, input_data1); + Tensor input_tensor2 = makeInputTensor<DataType::FLOAT32>(input_shape2, input_data2); + Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + + SquaredDifference kernel(&input_tensor1, &input_tensor2, &output_tensor); + kernel.configure(); + kernel.execute(); + + std::vector<float> ref_output_data{0.0, 1.0, 4.0, 100.0, 9.0, 5.9536}; + EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); +} + +} // namespace +} // namespace kernels +} // namespace luci_interpreter diff --git a/compiler/luci-interpreter/src/kernels/Squeeze.cpp b/compiler/luci-interpreter/src/kernels/Squeeze.cpp index ce43ef789..4a75518c7 100644 --- a/compiler/luci-interpreter/src/kernels/Squeeze.cpp +++ b/compiler/luci-interpreter/src/kernels/Squeeze.cpp @@ -27,7 +27,7 @@ namespace kernels { Squeeze::Squeeze(const Tensor *input, Tensor *output, const SqueezeParams ¶ms) - : KernelWithParams<SqueezeParams>({input}, {output}, params) + : KernelWithParams<SqueezeParams>({input}, {output}, params) { } diff --git a/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp b/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp index ff9fb09d2..1c81893b9 100644 --- a/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Squeeze.test.cpp @@ -56,12 +56,12 @@ TYPED_TEST_CASE(SqueezeTest, DataTypes); TYPED_TEST(SqueezeTest, TotalTest) { Check<TypeParam>( - /*input_shape=*/{1, 24, 1}, /*output_shape=*/{24}, - /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, - /*output_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, - {-1, 0}); + /*input_shape=*/{1, 24, 1}, /*output_shape=*/{24}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, + /*output_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}, + {-1, 0}); } } // namespace diff --git a/compiler/luci-interpreter/src/kernels/StridedSlice.cpp b/compiler/luci-interpreter/src/kernels/StridedSlice.cpp index 679485439..37b0dd8c5 100644 --- a/compiler/luci-interpreter/src/kernels/StridedSlice.cpp +++ b/compiler/luci-interpreter/src/kernels/StridedSlice.cpp @@ -31,7 +31,7 @@ namespace kernels StridedSlice::StridedSlice(const Tensor *input, const Tensor *begin, const Tensor *end, const Tensor *strides, Tensor *output, const StridedSliceParams ¶ms) - : KernelWithParams<StridedSliceParams>({input, begin, end, strides}, {output}, params) + : KernelWithParams<StridedSliceParams>({input, begin, end, strides}, {output}, params) { } @@ -82,7 +82,7 @@ void StridedSlice::configure() assert(stride != 0); int32_t begin = ::tflite::strided_slice::StartForAxis(op_params, getTensorShape(input()), idx); int32_t end = - ::tflite::strided_slice::StopForAxis(op_params, getTensorShape(input()), idx, begin); + ::tflite::strided_slice::StopForAxis(op_params, getTensorShape(input()), idx, begin); const bool shrink_axis = params().shrink_axis_mask & (1 << idx); if (shrink_axis) diff --git a/compiler/luci-interpreter/src/kernels/Sub.cpp b/compiler/luci-interpreter/src/kernels/Sub.cpp index dd9c1102f..3c7588d62 100644 --- a/compiler/luci-interpreter/src/kernels/Sub.cpp +++ b/compiler/luci-interpreter/src/kernels/Sub.cpp @@ -28,7 +28,7 @@ namespace kernels { Sub::Sub(const Tensor *input1, const Tensor *input2, Tensor *output, const SubParams ¶ms) - : KernelWithParams<SubParams>({input1, input2}, {output}, params) + : KernelWithParams<SubParams>({input1, input2}, {output}, params) { } @@ -64,13 +64,13 @@ void Sub::evalFloat() const params.float_activation_max = activation_max; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { tflite::reference_ops::BroadcastSubSlow( - params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), - getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); + params, getTensorShape(input1()), getTensorData<float>(input1()), getTensorShape(input2()), + getTensorData<float>(input2()), getTensorShape(output()), getTensorData<float>(output())); } else { @@ -118,14 +118,13 @@ void Sub::evalQuantized() const params.quantized_activation_max = activation_max; const bool need_broadcast = tflite::reference_ops::ProcessBroadcastShapes( - getTensorShape(input1()), getTensorShape(input2()), ¶ms); + getTensorShape(input1()), getTensorShape(input2()), ¶ms); if (need_broadcast) { tflite::reference_ops::BroadcastSubSlow( - params, getTensorShape(input1()), getTensorData<uint8_t>(input1()), - getTensorShape(input2()), getTensorData<uint8_t>(input2()), getTensorShape(output()), - getTensorData<uint8_t>(output())); + params, getTensorShape(input1()), getTensorData<uint8_t>(input1()), getTensorShape(input2()), + getTensorData<uint8_t>(input2()), getTensorShape(output()), getTensorData<uint8_t>(output())); } else { diff --git a/compiler/luci-interpreter/src/kernels/Sub.test.cpp b/compiler/luci-interpreter/src/kernels/Sub.test.cpp index 9f77fe7e0..f560ceb36 100644 --- a/compiler/luci-interpreter/src/kernels/Sub.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Sub.test.cpp @@ -49,25 +49,25 @@ TEST(SubTest, Uint8) vector<float> test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f}; vector<vector<int32_t>> output_shapes = {{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; vector<vector<float>> output_data = { - {-0.5f, 2.0f, 0.1f, 1.8f, -1.3f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, -0.1f, -0.4f, - 0.6f, -1.4f, 1.2f, -1.6f, -0.2f, -2.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, - -1.8f, -0.3f, -1.2f, -0.5f, -2.6f, -0.9f, 0.5f, -2.5f, 1.1f, -2.7f, -0.3f, -3.0f}, - {-0.5f, 2.0f, 1.3f, 0.0f, -0.2f, -2.0f, 1.0f, 2.5f, -1.2f, -0.5f, -0.3f, -3.0f}, - {-0.5f, 2.1f, -0.6f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, - 0.6f, -1.3f, 0.5f, -1.4f, 1.2f, -0.7f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, - -2.1f, -0.5f, -2.6f, -1.0f, -2.5f, -0.9f, 0.2f, -2.7f, -0.3f, -3.0f, -0.2f, -3.0f}, - {-0.5f, 2.1f, 0.6f, 0.2f, 1.2f, -0.7f, 0.7f, 2.3f, -2.6f, -1.0f, -0.2f, -3.0f}}; + {-0.5f, 2.0f, 0.1f, 1.8f, -1.3f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, -0.1f, -0.4f, + 0.6f, -1.4f, 1.2f, -1.6f, -0.2f, -2.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, + -1.8f, -0.3f, -1.2f, -0.5f, -2.6f, -0.9f, 0.5f, -2.5f, 1.1f, -2.7f, -0.3f, -3.0f}, + {-0.5f, 2.0f, 1.3f, 0.0f, -0.2f, -2.0f, 1.0f, 2.5f, -1.2f, -0.5f, -0.3f, -3.0f}, + {-0.5f, 2.1f, -0.6f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, + 0.6f, -1.3f, 0.5f, -1.4f, 1.2f, -0.7f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, + -2.1f, -0.5f, -2.6f, -1.0f, -2.5f, -0.9f, 0.2f, -2.7f, -0.3f, -3.0f, -0.2f, -3.0f}, + {-0.5f, 2.1f, 0.6f, 0.2f, 1.2f, -0.7f, 0.7f, 2.3f, -2.6f, -1.0f, -0.2f, -3.0f}}; float kQuantizedTolerance = GetTolerance(-3.f, 3.f); pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-3.f, 3.f); for (size_t i = 0; i < output_data.size(); ++i) { Tensor input1_tensor = - makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); + makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); Tensor input2_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first, quant_param.second, test_data); Tensor output_tensor = - makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); + makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); SubParams params{}; params.activation = Activation::NONE; @@ -93,9 +93,9 @@ TEST(SubTest, Uint8) Tensor input1_tensor = makeInputTensor<DataType::U8>(test_shapes[i], quant_param.first, quant_param.second, test_data); Tensor input2_tensor = - makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); + makeInputTensor<DataType::U8>(base_shape, quant_param.first, quant_param.second, base_data); Tensor output_tensor = - makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); + makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second); SubParams params{}; params.activation = Activation::NONE; @@ -116,14 +116,14 @@ TEST(SubTest, Float) vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}}; vector<vector<int32_t>> output_shapes{{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}}; vector<vector<float>> test_outputs = { - {0.0f, 2.0f, 0.1f, 1.8f, 0.0f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, 0.0f, 0.0f, - 0.6f, 0.0f, 1.2f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.1f, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.0f, 1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.1f, 0.0f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, - 0.6f, 0.0f, 0.5f, 0.0f, 1.2f, 0.0f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 2.1f, 0.6f, 0.2f, 1.2f, 0.0f, 0.7f, 2.3f, 0.0f, 0.0f, 0.0f, 0.0f}}; + {0.0f, 2.0f, 0.1f, 1.8f, 0.0f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, 0.0f, 0.0f, + 0.6f, 0.0f, 1.2f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.1f, 0.0f, 0.0f, 0.0f}, + {0.0f, 2.0f, 1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 2.1f, 0.0f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f, + 0.6f, 0.0f, 0.5f, 0.0f, 1.2f, 0.0f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 2.1f, 0.6f, 0.2f, 1.2f, 0.0f, 0.7f, 2.3f, 0.0f, 0.0f, 0.0f, 0.0f}}; vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f, 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f}; @@ -142,7 +142,7 @@ TEST(SubTest, Float) kernel.execute(); EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f)) - << "With shape number " << i; + << "With shape number " << i; EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i])); } diff --git a/compiler/luci-interpreter/src/kernels/Tanh.test.cpp b/compiler/luci-interpreter/src/kernels/Tanh.test.cpp index 17b50f259..ef727d6eb 100644 --- a/compiler/luci-interpreter/src/kernels/Tanh.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Tanh.test.cpp @@ -31,8 +31,8 @@ TEST(TanhTest, Float) { Shape input_shape{1, 2, 4, 1}; std::vector<float> input_data{ - 0, -6, 2, 4, // - 3, -2, 10, 1, // + 0, -6, 2, 4, // + 3, -2, 10, 1, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -42,8 +42,8 @@ TEST(TanhTest, Float) kernel.execute(); std::vector<float> ref_output_data{ - 0, -0.9999877, 0.9640275, 0.999329, // - 0.99505475, -0.9640275, 1, 0.7615941, // + 0, -0.9999877, 0.9640275, 0.999329, // + 0.99505475, -0.9640275, 1, 0.7615941, // }; EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data)); } @@ -56,41 +56,41 @@ TEST(TanhTest, Uint8) std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(8 * kMin, 8 * kMax); std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(kMin, kMax); std::vector<float> input_data{ - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // }; Tensor input_tensor = makeInputTensor<DataType::U8>({2, 6, 4, 1}, input_quant_param.first, input_quant_param.second, input_data); Tensor output_tensor = - makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); + makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second); Tanh kernel(&input_tensor, &output_tensor); kernel.configure(); kernel.execute(); std::vector<float> ref_output_data{ - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // - 0.0, -0.999987, 0.964027, 0.999329, // - -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // + 0.0, -0.999987, 0.964027, 0.999329, // + -0.999329, -0.96402, 0.99999, 0.76159, // }; std::vector<int32_t> ref_output_shape{2, 6, 4, 1}; EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data, kTanhTolerance)); @@ -100,18 +100,18 @@ TEST(TanhTest, Uint8) TEST(TanhTest, InputTypeInvalid_NEG) { std::vector<int64_t> input_data{ - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // }; Tensor input_tensor = makeInputTensor<DataType::S64>({2, 6, 4, 1}, input_data); Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); @@ -123,18 +123,18 @@ TEST(TanhTest, InputTypeInvalid_NEG) TEST(TanhTest, InputOutputMismatch_NEG) { std::vector<float> input_data{ - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // - 0, -6, 2, 4, // - -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // + 0, -6, 2, 4, // + -4, -2, 8, 1, // }; Tensor input_tensor = makeInputTensor<DataType::FLOAT32>({2, 6, 4, 1}, input_data); Tensor output_tensor = makeOutputTensor(DataType::U8); diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.cpp b/compiler/luci-interpreter/src/kernels/TestUtils.cpp index c3c0b5a7d..831dc4247 100644 --- a/compiler/luci-interpreter/src/kernels/TestUtils.cpp +++ b/compiler/luci-interpreter/src/kernels/TestUtils.cpp @@ -84,7 +84,7 @@ std::vector<float> dequantizeTensorData(const Tensor &tensor) float scale = tensor.scales()[channel]; size_t offset = inner_dims_size * (quant_dim_size * outer_it + channel); std::vector<float> part_dequantized_data = - dequantize(data.data() + offset, inner_dims_size, scale, 0); + dequantize(data.data() + offset, inner_dims_size, scale, 0); dequantized_data.insert(dequantized_data.end(), part_dequantized_data.begin(), part_dequantized_data.end()); } diff --git a/compiler/luci-interpreter/src/kernels/TestUtils.h b/compiler/luci-interpreter/src/kernels/TestUtils.h index 1f17e39e1..c4c73d546 100644 --- a/compiler/luci-interpreter/src/kernels/TestUtils.h +++ b/compiler/luci-interpreter/src/kernels/TestUtils.h @@ -59,7 +59,7 @@ Tensor makeInputTensor(const Shape &shape, float scale, int32_t zero_point, using NativeT = typename DataTypeImpl<DT>::Type; Tensor tensor(DT, shape, {{scale}, {zero_point}}, ""); std::vector<NativeT> quantized_data = - quantize<NativeT>(data.data(), data.size(), scale, zero_point); + quantize<NativeT>(data.data(), data.size(), scale, zero_point); tensor.writeData(quantized_data.data(), quantized_data.size() * sizeof(NativeT)); return tensor; } @@ -108,7 +108,7 @@ Tensor makeInputTensor(const Shape &shape, const std::vector<float> &scales, float scale = scales[channel]; size_t offset = inner_dims_size * (quant_dim_size * outer_it + channel); std::vector<NativeT> part_quantized_data = - quantize<NativeT>(data.data() + offset, inner_dims_size, scale, zero_point); + quantize<NativeT>(data.data() + offset, inner_dims_size, scale, zero_point); quantized_data.insert(quantized_data.end(), part_quantized_data.begin(), part_quantized_data.end()); } @@ -172,7 +172,7 @@ std::vector<T> quantize(const float *data, size_t num_elements, float scale, int { const auto &f = data[i]; q.push_back(static_cast<T>( - std::max<float>(q_min, std::min<float>(q_max, std::round(zero_point + (f / scale)))))); + std::max<float>(q_min, std::min<float>(q_max, std::round(zero_point + (f / scale)))))); } return q; } @@ -233,8 +233,8 @@ template <typename T> std::pair<float, int32_t> quantizationParams(float f_min, const float zero_point_from_max_error = std::abs(qmax_double) + std::abs(f_max / scale); const float zero_point_double = zero_point_from_min_error < zero_point_from_max_error - ? zero_point_from_min - : zero_point_from_max; + ? zero_point_from_min + : zero_point_from_max; // Now we need to nudge the zero point to be an integer // (our zero points are integer, and this is motivated by the requirement diff --git a/compiler/luci-interpreter/src/kernels/Transpose.cpp b/compiler/luci-interpreter/src/kernels/Transpose.cpp index 8265d9937..c1a11cdb0 100644 --- a/compiler/luci-interpreter/src/kernels/Transpose.cpp +++ b/compiler/luci-interpreter/src/kernels/Transpose.cpp @@ -29,7 +29,7 @@ namespace kernels { Transpose::Transpose(const Tensor *input, const Tensor *perm, Tensor *output) - : Kernel({input, perm}, {output}) + : Kernel({input, perm}, {output}) { } diff --git a/compiler/luci-interpreter/src/kernels/Transpose.test.cpp b/compiler/luci-interpreter/src/kernels/Transpose.test.cpp index 1c99223a8..f0a915c35 100644 --- a/compiler/luci-interpreter/src/kernels/Transpose.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Transpose.test.cpp @@ -63,46 +63,47 @@ TYPED_TEST(TransposeTest, Small3D) TYPED_TEST(TransposeTest, Large4D) { Check<TypeParam>( - /*input_shape=*/{2, 3, 4, 5}, /*perm_shape=*/{4}, /*output_shape=*/{4, 2, 3, 5}, - /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, - /*perm_data=*/{2, 0, 1, 3}, - /*output_data=*/{0, 1, 2, 3, 4, 20, 21, 22, 23, 24, 40, 41, 42, 43, 44, - 60, 61, 62, 63, 64, 80, 81, 82, 83, 84, 100, 101, 102, 103, 104, - 5, 6, 7, 8, 9, 25, 26, 27, 28, 29, 45, 46, 47, 48, 49, - 65, 66, 67, 68, 69, 85, 86, 87, 88, 89, 105, 106, 107, 108, 109, - 10, 11, 12, 13, 14, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54, - 70, 71, 72, 73, 74, 90, 91, 92, 93, 94, 110, 111, 112, 113, 114, - 15, 16, 17, 18, 19, 35, 36, 37, 38, 39, 55, 56, 57, 58, 59, - 75, 76, 77, 78, 79, 95, 96, 97, 98, 99, 115, 116, 117, 118, 119}); + /*input_shape=*/{2, 3, 4, 5}, /*perm_shape=*/{4}, /*output_shape=*/{4, 2, 3, 5}, + /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, + /*perm_data=*/{2, 0, 1, 3}, + /*output_data=*/{0, 1, 2, 3, 4, 20, 21, 22, 23, 24, 40, 41, 42, 43, 44, + 60, 61, 62, 63, 64, 80, 81, 82, 83, 84, 100, 101, 102, 103, 104, + 5, 6, 7, 8, 9, 25, 26, 27, 28, 29, 45, 46, 47, 48, 49, + 65, 66, 67, 68, 69, 85, 86, 87, 88, 89, 105, 106, 107, 108, 109, + 10, 11, 12, 13, 14, 30, 31, 32, 33, 34, 50, 51, 52, 53, 54, + 70, 71, 72, 73, 74, 90, 91, 92, 93, 94, 110, 111, 112, 113, 114, + 15, 16, 17, 18, 19, 35, 36, 37, 38, 39, 55, 56, 57, 58, 59, + 75, 76, 77, 78, 79, 95, 96, 97, 98, 99, 115, 116, 117, 118, 119}); } TYPED_TEST(TransposeTest, Large2D) { Check<TypeParam>( - /*input_shape=*/{10, 12}, /*perm_shape=*/{2}, /*output_shape=*/{12, 10}, - /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, - /*perm_data=*/{1, 0}, - /*output_data=*/{ - 0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 1, 13, 25, 37, 49, 61, 73, 85, 97, 109, - 2, 14, 26, 38, 50, 62, 74, 86, 98, 110, 3, 15, 27, 39, 51, 63, 75, 87, 99, 111, - 4, 16, 28, 40, 52, 64, 76, 88, 100, 112, 5, 17, 29, 41, 53, 65, 77, 89, 101, 113, - 6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 7, 19, 31, 43, 55, 67, 79, 91, 103, 115, - 8, 20, 32, 44, 56, 68, 80, 92, 104, 116, 9, 21, 33, 45, 57, 69, 81, 93, 105, 117, - 10, 22, 34, 46, 58, 70, 82, 94, 106, 118, 11, 23, 35, 47, 59, 71, 83, 95, 107, 119}); + /*input_shape=*/{10, 12}, /*perm_shape=*/{2}, /*output_shape=*/{12, 10}, + /*input_data=*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}, + /*perm_data=*/{1, 0}, + /*output_data=*/{0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 1, 13, 25, 37, 49, + 61, 73, 85, 97, 109, 2, 14, 26, 38, 50, 62, 74, 86, 98, 110, + 3, 15, 27, 39, 51, 63, 75, 87, 99, 111, 4, 16, 28, 40, 52, + 64, 76, 88, 100, 112, 5, 17, 29, 41, 53, 65, 77, 89, 101, 113, + 6, 18, 30, 42, 54, 66, 78, 90, 102, 114, 7, 19, 31, 43, 55, + 67, 79, 91, 103, 115, 8, 20, 32, 44, 56, 68, 80, 92, 104, 116, + 9, 21, 33, 45, 57, 69, 81, 93, 105, 117, 10, 22, 34, 46, 58, + 70, 82, 94, 106, 118, 11, 23, 35, 47, 59, 71, 83, 95, 107, 119}); } } // namespace diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp index 491ae51ae..0c70756b2 100644 --- a/compiler/luci-interpreter/src/kernels/TransposeConv.cpp +++ b/compiler/luci-interpreter/src/kernels/TransposeConv.cpp @@ -31,7 +31,7 @@ namespace kernels TransposeConv::TransposeConv(const Tensor *output_shape, const Tensor *filter, const Tensor *input, const Tensor *bias, Tensor *output, const TransposeConvParams ¶ms) - : KernelWithParams<TransposeConvParams>({output_shape, filter, input, bias}, {output}, params) + : KernelWithParams<TransposeConvParams>({output_shape, filter, input, bias}, {output}, params) { } @@ -63,23 +63,23 @@ void TransposeConv::configure() const int32_t output_width = out_shape.dim(2); const int32_t unused_output_height = - computeOutputSize(params().padding, output_height, filter_height, params().stride_height, 1); + computeOutputSize(params().padding, output_height, filter_height, params().stride_height, 1); const int32_t unused_output_width = - computeOutputSize(params().padding, output_width, filter_width, params().stride_width, 1); + computeOutputSize(params().padding, output_width, filter_width, params().stride_width, 1); _padding_height = - computePadding(params().stride_height, 1, output_height, filter_height, unused_output_height); + computePadding(params().stride_height, 1, output_height, filter_height, unused_output_height); _padding_width = - computePadding(params().stride_width, 1, output_width, filter_width, unused_output_width); + computePadding(params().stride_width, 1, output_width, filter_width, unused_output_width); if (input()->element_type() == DataType::U8 || input()->element_type() == DataType::S16) { DataType scratch_data_type = - input()->element_type() == DataType::S16 ? DataType::S64 : DataType::S32; + input()->element_type() == DataType::S16 ? DataType::S64 : DataType::S32; _scratch_tensor = - std::make_unique<Tensor>(scratch_data_type, output()->shape(), AffineQuantization{}, ""); + std::make_unique<Tensor>(scratch_data_type, output()->shape(), AffineQuantization{}, ""); const std::vector<double> real_multipliers = - getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); + getQuantizedConvolutionMultiplers(input()->scale(), filter()->scales(), output()->scale()); _quant_multipliers = quantizeMultipliers(real_multipliers); } @@ -210,12 +210,12 @@ void TransposeConv::evalQuantizedPerChannel() const for (int32_t out_c = 0; out_c < output_depth; ++out_c) { const uint8_t input_val = - input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; const uint8_t filter_val = - filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] += - static_cast<int32_t>(input_val - input()->zero_point()) * - static_cast<int32_t>(filter_val - filter()->zero_points()[out_c]); + static_cast<int32_t>(input_val - input()->zero_point()) * + static_cast<int32_t>(filter_val - filter()->zero_points()[out_c]); } } } @@ -236,7 +236,7 @@ void TransposeConv::evalQuantizedPerChannel() const } int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( - acc, output_multipliers[out_c].multiplier, output_multipliers[out_c].shift); + acc, output_multipliers[out_c].multiplier, output_multipliers[out_c].shift); scaled_acc += output()->zero_point(); scaled_acc = std::max(scaled_acc, activation_min); @@ -302,11 +302,11 @@ void TransposeConv::evalQuantizedS16() const for (int32_t out_c = 0; out_c < output_depth; ++out_c) { const int16_t input_val = - input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; + input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)]; const int16_t filter_val = - filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; + filter_data[calcOffset(filter_shape, out_c, filter_y, filter_x, in_c)]; scratch_data[calcOffset(output_shape, batch, out_y, out_x, out_c)] += - static_cast<int64_t>(input_val) * static_cast<int64_t>(filter_val); + static_cast<int64_t>(input_val) * static_cast<int64_t>(filter_val); } } } @@ -326,7 +326,7 @@ void TransposeConv::evalQuantizedS16() const acc += bias_data[out_c]; } int32_t scaled_acc = tflite::MultiplyByQuantizedMultiplier( - acc, output_multipliers[out_c].multiplier, output_multipliers[out_c].shift); + acc, output_multipliers[out_c].multiplier, output_multipliers[out_c].shift); scaled_acc = std::max(scaled_acc, activation_min); scaled_acc = std::min(scaled_acc, activation_max); diff --git a/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp index b1309c128..9bcb015c1 100644 --- a/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp +++ b/compiler/luci-interpreter/src/kernels/TransposeConv.test.cpp @@ -37,7 +37,7 @@ void Check(std::initializer_list<int32_t> output_shape_shape, { constexpr DataType element_type = getElementType<T>(); Tensor output_shape_tensor = - makeInputTensor<DataType::S32>(output_shape_shape, output_shape_data); + makeInputTensor<DataType::S32>(output_shape_shape, output_shape_data); Tensor weight_tensor = makeInputTensor<element_type>(weight_shape, weight_data); Tensor input_data_tensor = makeInputTensor<element_type>(input_shape, input_data); Tensor output_tensor = makeOutputTensor(element_type); @@ -68,13 +68,13 @@ void Check(std::initializer_list<int32_t> output_shape_shape, TEST(TransposeConvTest, FloatSimple) { Check<float, float>( - /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 1}, /*input_shape=*/{1, 4, 4, 1}, - /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1}, - /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9}, - /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, - /*bias_data=*/{}, - /*output_data=*/{29, 62, 83, 75, 99, 192, 237, 198, 207, 372, 417, 330, 263, 446, 485, 365}, - /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1); + /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 1}, /*input_shape=*/{1, 4, 4, 1}, + /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1}, + /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + /*bias_data=*/{}, + /*output_data=*/{29, 62, 83, 75, 99, 192, 237, 198, 207, 372, 417, 330, 263, 446, 485, 365}, + /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1); SUCCEED(); } @@ -82,15 +82,15 @@ TEST(TransposeConvTest, FloatSimple) TEST(TransposeConvTest, FloatTwoFiltersTest) { Check<float, float>( - /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 2}, /*input_shape=*/{1, 4, 4, 2}, - /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1}, - /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, - /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, - /*bias_data=*/{}, - /*output_data=*/ - {184, 412, 568, 528, 678, 1347, 1689, 1434, 1494, 2715, 3057, 2442, 1968, 3352, 3652, 2760}, - /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1); + /*output_shape_shape=*/{4}, /*weight_shape=*/{1, 3, 3, 2}, /*input_shape=*/{1, 4, 4, 2}, + /*bias_shape=*/{}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 4, 4, 1}, + /*weight_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, + /*bias_data=*/{}, + /*output_data=*/ + {184, 412, 568, 528, 678, 1347, 1689, 1434, 1494, 2715, 3057, 2442, 1968, 3352, 3652, 2760}, + /*params.padding=*/luci::Padding::SAME, /*stride_height=*/1, /*stride_width=*/1); SUCCEED(); } @@ -98,16 +98,16 @@ TEST(TransposeConvTest, FloatTwoFiltersTest) TEST(TransposeConvTest, SimpleBiasTest) { Check<float, float>( - /*output_shape_shape=*/{4}, /*weight_shape=*/{2, 3, 3, 1}, - /*input_shape=*/{1, 2, 2, 1}, - /*bias_shape=*/{2}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 5, 5, 2}, - /*weight_data=*/{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18}, - /*input_data=*/{1, 2, 3, 4}, - /*bias_data=*/{3, 4}, - /*output_data=*/{4, 6, 6, 8, 10, 14, 9, 12, 13, 16, 10, 12, 12, 14, 28, 32, 21, - 24, 25, 28, 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, 24, 28, 30, 34, - 64, 72, 39, 44, 47, 52, 42, 46, 48, 52, 106, 114, 63, 68, 71, 76}, - /*params.padding=*/luci::Padding::VALID, /*stride_height=*/2, /*stride_width=*/2); + /*output_shape_shape=*/{4}, /*weight_shape=*/{2, 3, 3, 1}, + /*input_shape=*/{1, 2, 2, 1}, + /*bias_shape=*/{2}, /*output_shape=*/{1, 4, 4, 1}, /*output_shape_data=*/{1, 5, 5, 2}, + /*weight_data=*/{1, 3, 5, 7, 9, 11, 13, 15, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18}, + /*input_data=*/{1, 2, 3, 4}, + /*bias_data=*/{3, 4}, + /*output_data=*/{4, 6, 6, 8, 10, 14, 9, 12, 13, 16, 10, 12, 12, 14, 28, 32, 21, + 24, 25, 28, 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, 24, 28, 30, 34, + 64, 72, 39, 44, 47, 52, 42, 46, 48, 52, 106, 114, 63, 68, 71, 76}, + /*params.padding=*/luci::Padding::VALID, /*stride_height=*/2, /*stride_width=*/2); SUCCEED(); } @@ -119,11 +119,11 @@ TEST(TransposeConvTest, UInt8) std::vector<float> bias_data{3, 4}; std::vector<int32_t> output_shape_data{1, 5, 5, 2}; std::vector<float> ref_output_data{ - 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // - 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // - 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // - 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // - 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // }; // Choose quantization parameters carefully. @@ -131,12 +131,12 @@ TEST(TransposeConvTest, UInt8) auto filter_quant = quantizationParams<uint8_t>(-24.0, 39.75); // s = 1 / 4, zp = 96 auto output_quant = quantizationParams<uint8_t>(-64.0, 191.0); // s = 1, zp = 64 - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 1}, input_quant.first, - input_quant.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 2, 1}, input_quant.first, input_quant.second, input_data); Tensor filter_tensor = makeInputTensor<DataType::U8>({2, 3, 3, 1}, filter_quant.first, filter_quant.second, filter_data); Tensor bias_tensor = - makeInputTensor<DataType::S32>({2}, input_quant.first * filter_quant.first, 0, bias_data); + makeInputTensor<DataType::S32>({2}, input_quant.first * filter_quant.first, 0, bias_data); Tensor output_shape_tensor = makeInputTensor<DataType::S32>({4}, output_shape_data); Tensor output_tensor = makeOutputTensor(DataType::U8, output_quant.first, output_quant.second); @@ -162,11 +162,11 @@ TEST(TransposeConvTest, UInt8_CWQ) std::vector<float> bias_data{3, 4}; std::vector<int32_t> output_shape_data{1, 5, 5, 2}; std::vector<float> ref_output_data{ - 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // - 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // - 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // - 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // - 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // }; // Choose quantization parameters carefully. @@ -190,12 +190,12 @@ TEST(TransposeConvTest, UInt8_CWQ) bias_scales.push_back(filter_quant_params[i].first * input_quant.first); std::vector<int32_t> zerop(output_channels, 0); - Tensor input_tensor = makeInputTensor<DataType::U8>({1, 2, 2, 1}, input_quant.first, - input_quant.second, input_data); + Tensor input_tensor = + makeInputTensor<DataType::U8>({1, 2, 2, 1}, input_quant.first, input_quant.second, input_data); Tensor filter_tensor = makeInputTensor<DataType::U8>({output_channels, 3, 3, 1}, filter_scales, filter_zerops, 0, filter_data); Tensor bias_tensor = - makeInputTensor<DataType::S32>({output_channels}, bias_scales, zerop, 0, bias_data); + makeInputTensor<DataType::S32>({output_channels}, bias_scales, zerop, 0, bias_data); Tensor output_shape_tensor = makeInputTensor<DataType::S32>({4}, output_shape_data); Tensor output_tensor = makeOutputTensor(DataType::U8, output_quant.first, output_quant.second); @@ -220,11 +220,11 @@ TEST(TransposeConvTest, SInt16) std::vector<float> bias_data{3, 4}; std::vector<int32_t> output_shape_data{1, 5, 5, 2}; std::vector<float> ref_output_data{ - 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // - 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // - 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // - 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // - 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // }; Tensor input_tensor = makeInputTensor<DataType::S16>({1, 2, 2, 1}, 0.25, 0, input_data); @@ -260,11 +260,11 @@ TEST(TransposeConvTest, SInt16_CWQ_weights) std::vector<float> bias_data{3, 4}; std::vector<float> ref_output_data{ - 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // - 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // - 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // - 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // - 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // + 4, 6, 6, 8, 10, 14, 9, 12, 13, 16, // + 10, 12, 12, 14, 28, 32, 21, 24, 25, 28, // + 19, 24, 27, 32, 65, 76, 45, 52, 57, 64, // + 24, 28, 30, 34, 64, 72, 39, 44, 47, 52, // + 42, 46, 48, 52, 106, 114, 63, 68, 71, 76, // }; const float input_scale = 0.25; @@ -275,7 +275,7 @@ TEST(TransposeConvTest, SInt16_CWQ_weights) Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, input_scale, 0, input_data); Tensor filter_tensor = - makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 0, filter_data); + makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 0, filter_data); Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, bias_scales, zerop, 0, bias_data); Tensor output_shape_tensor = makeInputTensor<DataType::S32>({4}, output_shape_data); Tensor output_tensor = makeOutputTensor(DataType::S16, output_scale, 0); diff --git a/compiler/luci-interpreter/src/kernels/Unpack.cpp b/compiler/luci-interpreter/src/kernels/Unpack.cpp index 834b79926..9127241c0 100644 --- a/compiler/luci-interpreter/src/kernels/Unpack.cpp +++ b/compiler/luci-interpreter/src/kernels/Unpack.cpp @@ -29,7 +29,7 @@ namespace kernels { Unpack::Unpack(const Tensor *input, std::vector<Tensor *> outputs, const UnpackParams ¶ms) - : KernelWithParams<UnpackParams>({input}, std::move(outputs), params) + : KernelWithParams<UnpackParams>({input}, std::move(outputs), params) { } diff --git a/compiler/luci-interpreter/src/kernels/Unpack.test.cpp b/compiler/luci-interpreter/src/kernels/Unpack.test.cpp index f70c5847a..6d611e12e 100644 --- a/compiler/luci-interpreter/src/kernels/Unpack.test.cpp +++ b/compiler/luci-interpreter/src/kernels/Unpack.test.cpp @@ -121,11 +121,11 @@ TYPED_TEST(UnpackTest, ThreeDimensionsTwoOutputs) TYPED_TEST(UnpackTest, FiveDimensionsTwoOutputs) { Check<TypeParam>( - /*axis=*/2, /*input_shape=*/{2, 2, 2, 2, 1}, - /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, - /*exp_output_shape=*/{{2, 2, 2, 1}, {2, 2, 2, 1}}, - /*exp_output_data=*/ - {{1, 2, 5, 6, 9, 10, 13, 14}, {3, 4, 7, 8, 11, 12, 15, 16}}); + /*axis=*/2, /*input_shape=*/{2, 2, 2, 2, 1}, + /*input_data=*/{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + /*exp_output_shape=*/{{2, 2, 2, 1}, {2, 2, 2, 1}}, + /*exp_output_data=*/ + {{1, 2, 5, 6, 9, 10, 13, 14}, {3, 4, 7, 8, 11, 12, 15, 16}}); } TYPED_TEST(UnpackTest, VectorToScalar) diff --git a/compiler/luci-interpreter/src/kernels/Utils.h b/compiler/luci-interpreter/src/kernels/Utils.h index 4b5e72917..817a42f83 100644 --- a/compiler/luci-interpreter/src/kernels/Utils.h +++ b/compiler/luci-interpreter/src/kernels/Utils.h @@ -108,6 +108,8 @@ inline double getQuantizedConvolutionMultipler(float input_scale, float filter_s return input_product_scale / static_cast<double>(output_scale); } +// TODO rename getQuantizedConvolutionMultiplers to something more general +// it is used for non conv operators too inline std::vector<double> getQuantizedConvolutionMultiplers(float input_scale, const std::vector<float> &filter_scale, float output_scale) @@ -118,7 +120,7 @@ inline std::vector<double> getQuantizedConvolutionMultiplers(float input_scale, for (size_t i = 0; i < n; ++i) { effective_output_scales.push_back( - getQuantizedConvolutionMultipler(input_scale, filter_scale[i], output_scale)); + getQuantizedConvolutionMultipler(input_scale, filter_scale[i], output_scale)); } return effective_output_scales; } @@ -149,6 +151,7 @@ public: BroadcastableWrapper(const std::vector<T> &v) : _v(v), _stride(v.size() == 1 ? 0 : 1) {} T operator[](int idx) { return _v[idx * _stride]; } + private: const std::vector<T> &_v; int _stride; @@ -236,7 +239,7 @@ public: // Build with the tensors in 'tensor_list'. explicit VectorOfQuantizedTensors(const std::vector<TensorT *> &tensor_list) - : VectorOfTensors<uint8_t, is_const>(tensor_list) + : VectorOfTensors<uint8_t, is_const>(tensor_list) { for (TensorT *tensor : tensor_list) { diff --git a/compiler/luci-interpreter/src/loader/CMakeLists.txt b/compiler/luci-interpreter/src/loader/CMakeLists.txt index d99485d06..20a6f03cd 100644 --- a/compiler/luci-interpreter/src/loader/CMakeLists.txt +++ b/compiler/luci-interpreter/src/loader/CMakeLists.txt @@ -1,5 +1,3 @@ -nnas_find_package(GTest REQUIRED) - set(SOURCES GraphLoader.h GraphLoader.cpp @@ -16,6 +14,12 @@ target_link_libraries(luci_interpreter_loader PUBLIC luci_lang luci_interpreter_core PRIVATE luci_interpreter_kernels nncc_common) +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + set(TEST_SOURCES KernelBuilder.test.cpp) GTest_AddTest(luci_interpreter_loader_test ${TEST_SOURCES}) diff --git a/compiler/luci-interpreter/src/loader/GraphLoader.cpp b/compiler/luci-interpreter/src/loader/GraphLoader.cpp index 09e923597..bc44c7efa 100644 --- a/compiler/luci-interpreter/src/loader/GraphLoader.cpp +++ b/compiler/luci-interpreter/src/loader/GraphLoader.cpp @@ -107,11 +107,11 @@ bool isTensorProducingNode(const luci::CircleNode *node) } // namespace GraphLoader::GraphLoader( - const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir, - const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph, - std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor) - : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir), - _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor) + const loco::Graph *graph, RuntimeGraph *runtime_graph, RuntimeToIR &runtime_to_ir, + const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph, + std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor) + : _graph(graph), _runtime_graph(runtime_graph), _runtime_to_ir(runtime_to_ir), + _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor) { } diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.cpp b/compiler/luci-interpreter/src/loader/KernelBuilder.cpp index 7b723e88a..913a062d7 100644 --- a/compiler/luci-interpreter/src/loader/KernelBuilder.cpp +++ b/compiler/luci-interpreter/src/loader/KernelBuilder.cpp @@ -19,6 +19,7 @@ #include "kernels/Add.h" #include "kernels/ArgMax.h" #include "kernels/AveragePool2D.h" +#include "kernels/BatchToSpaceND.h" #include "kernels/Concatenation.h" #include "kernels/Conv2D.h" #include "kernels/DepthToSpace.h" @@ -50,7 +51,9 @@ #include "kernels/Mean.h" #include "kernels/Minimum.h" #include "kernels/Mul.h" +#include "kernels/Neg.h" #include "kernels/NotEqual.h" +#include "kernels/Pack.h" #include "kernels/Pad.h" #include "kernels/Pow.h" #include "kernels/Prelu.h" @@ -63,12 +66,14 @@ #include "kernels/Rsqrt.h" #include "kernels/Slice.h" #include "kernels/Softmax.h" +#include "kernels/SpaceToBatchND.h" #include "kernels/SpaceToDepth.h" #include "kernels/Split.h" #include "kernels/StridedSlice.h" #include "kernels/Sqrt.h" -#include "kernels/Sub.h" +#include "kernels/SquaredDifference.h" #include "kernels/Squeeze.h" +#include "kernels/Sub.h" #include "kernels/Tanh.h" #include "kernels/Unpack.h" #include "kernels/Transpose.h" @@ -134,6 +139,11 @@ RuntimeGraph *KernelBuilder::getRuntimeGraph(const loco::Graph *graph) const return runtime_graph; } +std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleNode *) +{ + throw std::invalid_argument("Unsupported operator."); +} + std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAdd *node) { assert(node->arity() == 2); @@ -179,6 +189,18 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleAveragePool2D *no return std::make_unique<kernels::AveragePool2D>(input, output, params); } +std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleBatchToSpaceND *node) +{ + assert(node->arity() == 3); + + const Tensor *input = getInputTensor(node->input()); + const Tensor *block_shape = getInputTensor(node->block_shape()); + const Tensor *crops = getInputTensor(node->crops()); + Tensor *output = getOutputTensor(node); + + return std::make_unique<kernels::BatchToSpaceND>(input, block_shape, crops, output); +} + std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConcatenation *node) { std::vector<const Tensor *> inputs(node->numValues()); @@ -190,6 +212,7 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleConcatenation *no ConcatenationParams params{}; params.axis = node->axis(); + params.activation = node->fusedActivationFunction(); return std::make_unique<kernels::Concatenation>(std::move(inputs), output, params); } @@ -598,6 +621,16 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleMul *node) return std::make_unique<kernels::Mul>(input1, input2, output, params); } +std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleNeg *node) +{ + assert(node->arity() == 1); + + const Tensor *input = getInputTensor(node->x()); + Tensor *output = getOutputTensor(node); + + return std::make_unique<kernels::Neg>(input, output); +} + std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleNotEqual *node) { assert(node->arity() == 2); @@ -614,6 +647,24 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleOutput *) throw std::runtime_error("Output node cannot be executed."); } +std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePack *node) +{ + assert(node->arity() == node->values_count()); + + std::vector<const Tensor *> inputs(node->values_count()); + for (uint32_t i = 0; i < node->values_count(); ++i) + { + inputs[i] = getInputTensor(node->values(i)); + } + Tensor *output = getOutputTensor(node); + + PackParams params{}; + params.axis = node->axis(); + params.values_count = node->values_count(); + + return std::make_unique<kernels::Pack>(std::move(inputs), output, params); +} + std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CirclePad *node) { assert(node->arity() == 2); @@ -735,20 +786,6 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleRsqrt *node) return std::make_unique<kernels::Rsqrt>(input, output); } -std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSub *node) -{ - assert(node->arity() == 2); - - const Tensor *input1 = getInputTensor(node->x()); - const Tensor *input2 = getInputTensor(node->y()); - Tensor *output = getOutputTensor(node); - - SubParams params{}; - params.activation = node->fusedActivationFunction(); - - return std::make_unique<kernels::Sub>(input1, input2, output, params); -} - std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSlice *node) { assert(node->arity() == 3); @@ -775,6 +812,20 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSoftmax *node) return std::make_unique<kernels::Softmax>(input, output, params); } +std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSpaceToBatchND *node) +{ + assert(node->arity() == 3); + + const Tensor *input = getInputTensor(node->input()); + const Tensor *block_shape = getInputTensor(node->block_shape()); + const Tensor *paddings = getInputTensor(node->paddings()); + + Tensor *output = getOutputTensor(node); + + return std::make_unique<kernels::SpaceToBatchND>(input, block_shape, paddings, output); + ; +} + std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSpaceToDepth *node) { assert(node->arity() == 1); @@ -812,6 +863,17 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqrt *node) return std::make_unique<kernels::Sqrt>(input, output); } +std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSquaredDifference *node) +{ + assert(node->arity() == 2); + + const Tensor *input1 = getInputTensor(node->x()); + const Tensor *input2 = getInputTensor(node->y()); + Tensor *output = getOutputTensor(node); + + return std::make_unique<kernels::SquaredDifference>(input1, input2, output); +} + std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSqueeze *node) { assert(node->arity() == 1); @@ -846,6 +908,20 @@ std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleStridedSlice *nod return std::make_unique<kernels::StridedSlice>(input, begin, end, strides, output, params); } +std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleSub *node) +{ + assert(node->arity() == 2); + + const Tensor *input1 = getInputTensor(node->x()); + const Tensor *input2 = getInputTensor(node->y()); + Tensor *output = getOutputTensor(node); + + SubParams params{}; + params.activation = node->fusedActivationFunction(); + + return std::make_unique<kernels::Sub>(input1, input2, output, params); +} + std::unique_ptr<Kernel> KernelBuilder::visit(const luci::CircleTanh *node) { assert(node->arity() == 1); diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.h b/compiler/luci-interpreter/src/loader/KernelBuilder.h index 1546ba01b..6f482b29e 100644 --- a/compiler/luci-interpreter/src/loader/KernelBuilder.h +++ b/compiler/luci-interpreter/src/loader/KernelBuilder.h @@ -33,15 +33,17 @@ class KernelBuilder : public luci::CircleNodeVisitor<std::unique_ptr<Kernel>> { public: KernelBuilder( - const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph, - const std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor) - : _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor) + const std::unordered_map<const loco::Graph *, RuntimeGraph *> &graph_to_runtime_graph, + const std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor) + : _graph_to_runtime_graph(graph_to_runtime_graph), _node_to_tensor(node_to_tensor) { } + std::unique_ptr<Kernel> visit(const luci::CircleNode *node) override; std::unique_ptr<Kernel> visit(const luci::CircleAdd *node) override; std::unique_ptr<Kernel> visit(const luci::CircleArgMax *node) override; std::unique_ptr<Kernel> visit(const luci::CircleAveragePool2D *node) override; + std::unique_ptr<Kernel> visit(const luci::CircleBatchToSpaceND *node) override; std::unique_ptr<Kernel> visit(const luci::CircleConcatenation *node) override; std::unique_ptr<Kernel> visit(const luci::CircleConv2D *node) override; std::unique_ptr<Kernel> visit(const luci::CircleConst *node) override; @@ -75,8 +77,10 @@ public: std::unique_ptr<Kernel> visit(const luci::CircleMean *node) override; std::unique_ptr<Kernel> visit(const luci::CircleMinimum *node) override; std::unique_ptr<Kernel> visit(const luci::CircleMul *node) override; + std::unique_ptr<Kernel> visit(const luci::CircleNeg *node) override; std::unique_ptr<Kernel> visit(const luci::CircleNotEqual *node) override; std::unique_ptr<Kernel> visit(const luci::CircleOutput *node) override; + std::unique_ptr<Kernel> visit(const luci::CirclePack *node) override; std::unique_ptr<Kernel> visit(const luci::CirclePad *node) override; std::unique_ptr<Kernel> visit(const luci::CirclePow *node) override; std::unique_ptr<Kernel> visit(const luci::CirclePRelu *node) override; @@ -87,14 +91,16 @@ public: std::unique_ptr<Kernel> visit(const luci::CircleResizeNearestNeighbor *node) override; std::unique_ptr<Kernel> visit(const luci::CircleReverseV2 *node) override; std::unique_ptr<Kernel> visit(const luci::CircleRsqrt *node) override; - std::unique_ptr<Kernel> visit(const luci::CircleSub *node) override; std::unique_ptr<Kernel> visit(const luci::CircleSlice *node) override; std::unique_ptr<Kernel> visit(const luci::CircleSoftmax *node) override; + std::unique_ptr<Kernel> visit(const luci::CircleSpaceToBatchND *node) override; std::unique_ptr<Kernel> visit(const luci::CircleSpaceToDepth *node) override; std::unique_ptr<Kernel> visit(const luci::CircleSplit *node) override; std::unique_ptr<Kernel> visit(const luci::CircleStridedSlice *node) override; std::unique_ptr<Kernel> visit(const luci::CircleSqrt *node) override; + std::unique_ptr<Kernel> visit(const luci::CircleSquaredDifference *node) override; std::unique_ptr<Kernel> visit(const luci::CircleSqueeze *node) override; + std::unique_ptr<Kernel> visit(const luci::CircleSub *node) override; std::unique_ptr<Kernel> visit(const luci::CircleTanh *node) override; std::unique_ptr<Kernel> visit(const luci::CircleTranspose *node) override; std::unique_ptr<Kernel> visit(const luci::CircleTransposeConv *node) override; diff --git a/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp index c49a05a49..b49085325 100644 --- a/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp +++ b/compiler/luci-interpreter/src/loader/KernelBuilder.test.cpp @@ -50,6 +50,7 @@ #include <kernels/Mean.h> #include <kernels/Minimum.h> #include <kernels/Mul.h> +#include <kernels/Neg.h> #include <kernels/NotEqual.h> #include <kernels/Pad.h> #include <kernels/Pow.h> @@ -66,9 +67,10 @@ #include <kernels/SpaceToDepth.h> #include <kernels/Split.h> #include <kernels/Sqrt.h> -#include <kernels/Sub.h> +#include <kernels/SquaredDifference.h> #include <kernels/Squeeze.h> #include <kernels/StridedSlice.h> +#include <kernels/Sub.h> #include <kernels/Tanh.h> #include <kernels/Transpose.h> #include <kernels/TransposeConv.h> @@ -216,6 +218,7 @@ TEST_F(KernelBuilderTest, Concatenation) checkTensor(kernel->input(1), input2); checkTensor(kernel->output(), op); EXPECT_THAT(kernel->params().axis, Eq(op->axis())); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); } TEST_F(KernelBuilderTest, Conv2D) @@ -776,6 +779,20 @@ TEST_F(KernelBuilderTest, Mul) EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); } +TEST_F(KernelBuilderTest, Neg) +{ + auto *input = createInputNode(); + + auto *op = createNode<luci::CircleNeg>(); + op->x(input); + + auto kernel = buildKernel<kernels::Neg>(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input(), input); + checkTensor(kernel->output(), op); +} + TEST_F(KernelBuilderTest, NotEqual) { auto *x_input = createInputNode(); @@ -1052,24 +1069,21 @@ TEST_F(KernelBuilderTest, Sqrt) checkTensor(kernel->output(), op); } -TEST_F(KernelBuilderTest, Sub) +TEST_F(KernelBuilderTest, SquaredDifference) { auto *input1 = createInputNode(); auto *input2 = createInputNode(); - auto *op = createNode<luci::CircleSub>(); + auto *op = createNode<luci::CircleSquaredDifference>(); op->x(input1); op->y(input2); - op->fusedActivationFunction(luci::FusedActFunc::RELU); - - auto kernel = buildKernel<kernels::Sub>(op); + auto kernel = buildKernel<kernels::SquaredDifference>(op); ASSERT_THAT(kernel, NotNull()); checkTensor(kernel->input1(), input1); checkTensor(kernel->input2(), input2); checkTensor(kernel->output(), op); - EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); } TEST_F(KernelBuilderTest, Squeeze) @@ -1123,6 +1137,26 @@ TEST_F(KernelBuilderTest, StridedSlice) EXPECT_THAT(kernel->params().shrink_axis_mask, Eq(op->shrink_axis_mask())); } +TEST_F(KernelBuilderTest, Sub) +{ + auto *input1 = createInputNode(); + auto *input2 = createInputNode(); + + auto *op = createNode<luci::CircleSub>(); + op->x(input1); + op->y(input2); + + op->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto kernel = buildKernel<kernels::Sub>(op); + ASSERT_THAT(kernel, NotNull()); + + checkTensor(kernel->input1(), input1); + checkTensor(kernel->input2(), input2); + checkTensor(kernel->output(), op); + EXPECT_THAT(kernel->params().activation, Eq(op->fusedActivationFunction())); +} + TEST_F(KernelBuilderTest, Tanh) { auto *input = createInputNode(); diff --git a/compiler/luci-interpreter/src/loader/ModuleLoader.cpp b/compiler/luci-interpreter/src/loader/ModuleLoader.cpp index b9a2ae0a9..ff211bf09 100644 --- a/compiler/luci-interpreter/src/loader/ModuleLoader.cpp +++ b/compiler/luci-interpreter/src/loader/ModuleLoader.cpp @@ -24,8 +24,8 @@ namespace luci_interpreter ModuleLoader::ModuleLoader(const luci::Module *module, RuntimeModule *runtime_module, RuntimeToIR &runtime_to_ir, std::unordered_map<const loco::Node *, Tensor *> &node_to_tensor) - : _module(module), _runtime_module(runtime_module), _runtime_to_ir(runtime_to_ir), - _node_to_tensor(node_to_tensor) + : _module(module), _runtime_module(runtime_module), _runtime_to_ir(runtime_to_ir), + _node_to_tensor(node_to_tensor) { } diff --git a/compiler/luci-pass-value-test/.gitignore b/compiler/luci-pass-value-test/.gitignore new file mode 100644 index 000000000..8dbfa9012 --- /dev/null +++ b/compiler/luci-pass-value-test/.gitignore @@ -0,0 +1 @@ +/test.local.lst diff --git a/compiler/luci-pass-value-test/CMakeLists.txt b/compiler/luci-pass-value-test/CMakeLists.txt new file mode 100644 index 000000000..2d2befe57 --- /dev/null +++ b/compiler/luci-pass-value-test/CMakeLists.txt @@ -0,0 +1,44 @@ +unset(TEST_DEPS) +unset(LUCI_PASS_VALUE_TESTS) + +get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) + +macro(addeval RECIPE PASS_OPTION) + list(APPEND LUCI_PASS_VALUE_TESTS ${RECIPE}) + + set(CIRCLE_FILE "${RECIPE}.circle") + set(CIRCLE_PATH "${ARTIFACTS_BIN_PATH}/${CIRCLE_FILE}") + + set(PASS_CIRCLE_FILE "${RECIPE}.pass.circle") + set(PASS_CIRCLE_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PASS_CIRCLE_FILE}") + + set(DASH_PASS_OPTION "--${PASS_OPTION}") + + # Generate optimized .circle + add_custom_command(OUTPUT ${PASS_CIRCLE_OUTPUT_PATH} + COMMAND $<TARGET_FILE:circle2circle> ${DASH_PASS_OPTION} ${CIRCLE_PATH} ${PASS_CIRCLE_OUTPUT_PATH} + DEPENDS $<TARGET_FILE:circle2circle> ${CIRCLE_PATH} + COMMENT "Generate ${PASS_CIRCLE_FILE} with ${DASH_PASS_OPTION}" + ) + + # depends + list(APPEND TEST_DEPS ${PASS_CIRCLE_OUTPUT_PATH}) + +endmacro(addeval) + +# Read "test.lst" +include("test.lst") +# Read "test.local.lst" if exists +include("test.local.lst" OPTIONAL) + +add_custom_target(luci_pass_value_test_files ALL DEPENDS ${TEST_DEPS}) +add_dependencies(luci_pass_value_test_files common_artifacts_deps) + +add_test(NAME luci_pass_value_test + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/eval_driver.sh" + "${CMAKE_CURRENT_BINARY_DIR}" + "${ARTIFACTS_BIN_PATH}" + "${NNCC_OVERLAY_DIR}/venv_2_3_0" + "$<TARGET_FILE:luci_eval_driver>" + ${LUCI_PASS_VALUE_TESTS} +) diff --git a/compiler/luci-pass-value-test/README.md b/compiler/luci-pass-value-test/README.md new file mode 100644 index 000000000..f09619da6 --- /dev/null +++ b/compiler/luci-pass-value-test/README.md @@ -0,0 +1,20 @@ +# luci-pass-value-test + +`luci-pass-value-test` validates execution result values of tflite model and +circle model generated with specific optimization. + +The test proceeds as follows: + +Step 0: Use tflite and circle file in 'common-artifacts' folder as the source model. + - tflite file is used as to generate reference execution result + - circle file is used as source of optimization to apply + +Step 1: Run circle2circle with given optimization option to produce transformed circle. + - "modelfile.circle" -> circle2circle -> "modelfile.pass.circle" + +Step 2: Run TFLite interpreter and luci-interpreter for the source tflite and circle, respectively. + (with the same input tensors filled with random values) + - "modelfile.tflite" ------> TFLite interpreter -> Execution result 1 + - "modelfile.pass.circle" -> luci-interpreter ---> Execution result 2 + +Step 3: Compare the execution result 1 and 2. Test is PASSED if results are sames. diff --git a/compiler/luci-pass-value-test/eval_driver.sh b/compiler/luci-pass-value-test/eval_driver.sh new file mode 100755 index 000000000..848b6419a --- /dev/null +++ b/compiler/luci-pass-value-test/eval_driver.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# This script verifies the tflite and circle execution result values +# +# HOW TO USE +# +# ./eval_driver.sh <path/to/bin_dir> <path/to/work_dir> <path/to/venv_dir> <path/to/intp_dir> +# <TEST 1> <TEST 2> ... +# bin_dir : build directory of luci-pass-value-test (ex: build/compiler/luci-pass-value-test) +# work_dir : artifacts directoy where test materials exist +# venv_dir : python virtual environment home directory +# intp_dir : path to luci_eval_driver from luci-eval-driver + +VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VERIFY_SCRIPT_PATH="${VERIFY_SOURCE_PATH}/eval_result_verifier.py" +BINDIR="$1"; shift +WORKDIR="$1"; shift +VIRTUALENV="$1"; shift +INTERPRETER_DRIVER_PATH="$1"; shift + +TESTED=() +PASSED=() +FAILED=() + +for TESTCASE in "$@"; do + TESTED+=("${TESTCASE}") + + TESTCASE_TFLITE_FILE="${WORKDIR}/${TESTCASE}.tflite" + TESTCASE_CIRCLE_FILE="${BINDIR}/${TESTCASE}.pass.circle" + TEST_RESULT_FILE="${BINDIR}/${TESTCASE}" + + PASSED_TAG="${TEST_RESULT_FILE}.passed" + rm -f "${PASSED_TAG}" + + cat > "${TEST_RESULT_FILE}.log" <( + exec 2>&1 + set -ex + + source "${VIRTUALENV}/bin/activate" + + "${VIRTUALENV}/bin/python" "${VERIFY_SCRIPT_PATH}" \ + --driver "${INTERPRETER_DRIVER_PATH}" \ + --tflite "${TESTCASE_TFLITE_FILE}" \ + --circle "${TESTCASE_CIRCLE_FILE}" + + if [[ $? -eq 0 ]]; then + touch "${PASSED_TAG}" + fi + ) + + if [[ -f "${PASSED_TAG}" ]]; then + PASSED+=("${TESTCASE}") + else + FAILED+=("${TESTCASE}") + fi +done + +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/compiler/luci-pass-value-test/eval_result_verifier.py b/compiler/luci-pass-value-test/eval_result_verifier.py new file mode 100644 index 000000000..c6005edfc --- /dev/null +++ b/compiler/luci-pass-value-test/eval_result_verifier.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +import numpy as np +import tensorflow as tf +import subprocess +import argparse +import traceback + +# +# This script was copied from luci-value-test with input arguments are tflite and circle path +# +parser = argparse.ArgumentParser() +parser.add_argument('--driver', type=str, required=True) +parser.add_argument('--tflite', type=str, required=True) +parser.add_argument('--circle', type=str, required=True) +args = parser.parse_args() + +driver = args.driver +tflite_model = args.tflite +circle_model = args.circle + +# Build TFLite interpreter. +interpreter = tf.lite.Interpreter(tflite_model) +interpreter.allocate_tensors() + +# Generate random input data. +num_inputs = len(interpreter.get_input_details()) +for i in range(num_inputs): + input_details = interpreter.get_input_details()[i] + if input_details["dtype"] == np.float32: + input_data = np.array( + np.random.random_sample(input_details["shape"]), input_details["dtype"]) + elif input_details["dtype"] == np.uint8: + input_data = np.array( + np.random.randint(0, 256, size=input_details["shape"]), + input_details["dtype"]) + elif input_details["dtype"] == np.bool_: + input_data = np.array( + np.random.choice(a=[True, False], size=input_details["shape"]), + input_details["dtype"]) + else: + raise SystemExit("Unsupported input dtype") + + interpreter.set_tensor(input_details["index"], input_data) + input_data.tofile(circle_model + ".input" + str(i)) + +# Do inference +interpreter.invoke() + +# Execute luci interpreter. +subprocess.run( + [ + driver, circle_model, + str(num_inputs), circle_model + ".input", circle_model + ".output" + ], + check=True) + +# Compare the results. +for idx in range(len(interpreter.get_output_details())): + output_details = interpreter.get_output_details()[idx] + output_data = np.fromfile(circle_model + ".output" + str(idx), + output_details["dtype"]) + shape_file = open(circle_model + ".output" + str(idx) + ".shape", 'r') + output_shape = [int(i) for i in shape_file.read().split(',')] + luci_output_data = np.reshape(output_data, output_shape) + try: + if output_details["dtype"] == np.uint8: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=0, + atol=0) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + elif output_details["dtype"] == np.float32: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=1.e-5, + atol=1.e-5) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + elif output_details["dtype"] == np.int64: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=0, + atol=0) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + elif output_details["dtype"] == np.int32: + if np.allclose( + luci_output_data, + interpreter.get_tensor( + interpreter.get_output_details()[idx]["index"]), + rtol=0, + atol=0) == False: + raise SystemExit("Execution result of " + tflite_model + + " does not match with " + circle_model) + else: + raise SystemExit("Unsupported data type: ", output_details["dtype"]) + except: + print(traceback.format_exc()) + quit(255) + +quit(0) diff --git a/compiler/luci-pass-value-test/requires.cmake b/compiler/luci-pass-value-test/requires.cmake new file mode 100644 index 000000000..d977a51b6 --- /dev/null +++ b/compiler/luci-pass-value-test/requires.cmake @@ -0,0 +1,7 @@ +require("common-artifacts") +require("luci-interpreter") +require("safemain") +require("oops") +require("loco") +require("luci-value-test") +require("luci-eval-driver") diff --git a/compiler/luci-pass-value-test/test.lst b/compiler/luci-pass-value-test/test.lst new file mode 100644 index 000000000..e607904cb --- /dev/null +++ b/compiler/luci-pass-value-test/test.lst @@ -0,0 +1,30 @@ +# +# Format: +# addeval(MODEL PASS) +# MODEL: tflite model file name in build/compiler/common-artifacts folder. +# PASS: Optimization Pass to test. Supports only one Pass for now. +# + +# addeval(Net_Preactivation_BN_000 fuse_preactivation_batchnorm) : value diff exist +# --> https://github.com/Samsung/ONE/issues/5782 +addeval(Net_Conv_Add_Mul_000 fuse_batchnorm_with_conv) +addeval(Net_Conv_Add_Mul_000 fuse_batchnorm_with_conv) +addeval(Net_Conv_Add_Mul_001 fuse_batchnorm_with_conv) +addeval(Net_Conv_Add_Mul_002 fuse_batchnorm_with_conv) +addeval(Net_Conv_Min_Max_000 transform_min_max_to_relu6) +addeval(Net_Conv_Relu6_000 fuse_activation_function) +addeval(Net_DwConv_BN_000 fuse_batchnorm_with_dwconv) +addeval(Net_DwConv_BN_001 fuse_batchnorm_with_dwconv) +addeval(Net_Reshape_Neg_000 forward_reshape_to_unaryop) +addeval(Net_Reshape_Reshape_000 remove_redundant_reshape) +addeval(Net_Squeeze_Squeeze_000 substitute_squeeze_to_reshape) +addeval(Net_TConv_Add_000 fuse_add_with_tconv) +addeval(Net_TConv_Add_001 fuse_add_with_tconv) +addeval(Net_TConv_Add_002 fuse_add_with_tconv) +addeval(Net_TConv_BN_000 fuse_batchnorm_with_tconv) +addeval(Net_TConv_BN_001 fuse_batchnorm_with_tconv) +addeval(Net_TConv_BN_002 fuse_batchnorm_with_tconv) +addeval(Net_InstanceNorm_001 fuse_instnorm) +addeval(Net_InstanceNorm_002 fuse_instnorm) +addeval(Net_InstanceNorm_003 fuse_instnorm) +addeval(Net_StridedSlice_StridedSlice_000 remove_unnecessary_strided_slice) diff --git a/compiler/luci-value-test/.gitignore b/compiler/luci-value-test/.gitignore new file mode 100644 index 000000000..8dbfa9012 --- /dev/null +++ b/compiler/luci-value-test/.gitignore @@ -0,0 +1 @@ +/test.local.lst diff --git a/compiler/luci-value-test/CMakeLists.txt b/compiler/luci-value-test/CMakeLists.txt index ec7463409..124f120d4 100644 --- a/compiler/luci-value-test/CMakeLists.txt +++ b/compiler/luci-value-test/CMakeLists.txt @@ -12,8 +12,6 @@ include("test.local.lst" OPTIONAL) # Generate dependencies add_custom_target(luci_eval_testfiles ALL DEPENDS ${TESTFILES}) -add_subdirectory(tester) - get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR) add_test(NAME luci_value_test @@ -21,5 +19,6 @@ add_test(NAME luci_value_test "${CMAKE_CURRENT_BINARY_DIR}" "${ARTIFACTS_BIN_PATH}" "${NNCC_OVERLAY_DIR}/venv_2_3_0" + "$<TARGET_FILE:luci_eval_driver>" ${LUCI_VALUE_TESTS} ) diff --git a/compiler/luci-value-test/evalverify.sh b/compiler/luci-value-test/evalverify.sh index 12c9a459a..01c4bce46 100755 --- a/compiler/luci-value-test/evalverify.sh +++ b/compiler/luci-value-test/evalverify.sh @@ -14,7 +14,7 @@ VERIFY_SCRIPT_PATH="${VERIFY_SOURCE_PATH}/luci_eval_verifier.py" BINDIR="$1"; shift WORKDIR="$1"; shift VIRTUALENV="$1"; shift -INTERPRETER_DRIVER_PATH="${BINDIR}/tester/luci_eval_tester" +INTERPRETER_DRIVER_PATH="$1"; shift TESTED=() PASSED=() diff --git a/compiler/luci-value-test/luci_eval_verifier.py b/compiler/luci-value-test/luci_eval_verifier.py index 7a2cebb91..f6b0620d8 100755 --- a/compiler/luci-value-test/luci_eval_verifier.py +++ b/compiler/luci-value-test/luci_eval_verifier.py @@ -9,7 +9,7 @@ import traceback # This script compares the execution result of luci-interpreter with that of TFLite interpreter # # Basic usage: -# eval_verifier.py --driver build/compiler/luci-value-test/tester/luci_eval_tester +# eval_verifier.py --driver build/compiler/luci-eval-driver/luci_eval_driver # --model inception_v3 parser = argparse.ArgumentParser() parser.add_argument('--driver', type=str, required=True) diff --git a/compiler/luci-value-test/requires.cmake b/compiler/luci-value-test/requires.cmake index f8af5f27e..e1a0f8367 100644 --- a/compiler/luci-value-test/requires.cmake +++ b/compiler/luci-value-test/requires.cmake @@ -4,3 +4,4 @@ require("luci-interpreter") require("safemain") require("oops") require("loco") +require("luci-eval-driver") diff --git a/compiler/luci-value-test/test.lst b/compiler/luci-value-test/test.lst index 0e5231eca..edf329aff 100644 --- a/compiler/luci-value-test/test.lst +++ b/compiler/luci-value-test/test.lst @@ -155,6 +155,7 @@ addeval(Split_000) #addeval(Square_000) #addeval(SquaredDifference_000) addeval(Squeeze_000) +addeval(Squeeze_001) addeval(StridedSlice_000) addeval(StridedSlice_001) addeval(StridedSlice_002) diff --git a/compiler/luci-value-test/tester/CMakeLists.txt b/compiler/luci-value-test/tester/CMakeLists.txt deleted file mode 100644 index f2a4ff4b6..000000000 --- a/compiler/luci-value-test/tester/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ - -set(SRCS_EVAL_TESTER - src/EvalTester.cpp - ) - -add_executable(luci_eval_tester ${SRCS_EVAL_TESTER}) -target_link_libraries(luci_eval_tester PRIVATE oops) -target_link_libraries(luci_eval_tester PRIVATE loco) -target_link_libraries(luci_eval_tester PRIVATE luci_import) -target_link_libraries(luci_eval_tester PRIVATE luci_export) -target_link_libraries(luci_eval_tester PRIVATE luci_lang) -target_link_libraries(luci_eval_tester PRIVATE luci_interpreter) -target_link_libraries(luci_eval_tester PRIVATE safemain) diff --git a/compiler/luci/CMakeLists.txt b/compiler/luci/CMakeLists.txt index 214a1bbf2..3771176f0 100644 --- a/compiler/luci/CMakeLists.txt +++ b/compiler/luci/CMakeLists.txt @@ -1,8 +1,11 @@ add_subdirectory(env) add_subdirectory(log) add_subdirectory(lang) +add_subdirectory(testhelper) add_subdirectory(service) add_subdirectory(pass) +add_subdirectory(profile) +add_subdirectory(partition) add_subdirectory(logex) add_subdirectory(import) add_subdirectory(export) diff --git a/compiler/luci/env/include/luci/UserSettings.h b/compiler/luci/env/include/luci/UserSettings.h index bcfd16071..b56bd65e2 100644 --- a/compiler/luci/env/include/luci/UserSettings.h +++ b/compiler/luci/env/include/luci/UserSettings.h @@ -32,6 +32,7 @@ struct UserSettings Undefined, MuteWarnings, DisableValidation, + ProfilingDataGen, }; static UserSettings *settings(); diff --git a/compiler/luci/env/src/UserSettings.cpp b/compiler/luci/env/src/UserSettings.cpp index 27dec762d..b4c661190 100644 --- a/compiler/luci/env/src/UserSettings.cpp +++ b/compiler/luci/env/src/UserSettings.cpp @@ -30,6 +30,7 @@ public: private: bool _MuteWarnings{false}; bool _DisableValidation{false}; + bool _ProfilingDataGen{false}; }; void UserSettingsImpl::set(const Key key, bool value) @@ -42,6 +43,9 @@ void UserSettingsImpl::set(const Key key, bool value) case Key::DisableValidation: _DisableValidation = value; break; + case Key::ProfilingDataGen: + _ProfilingDataGen = value; + break; default: throw std::runtime_error("Invalid key in boolean set"); break; @@ -56,6 +60,8 @@ bool UserSettingsImpl::get(const Key key) const return _MuteWarnings; case Key::DisableValidation: return _DisableValidation; + case Key::ProfilingDataGen: + return _ProfilingDataGen; default: throw std::runtime_error("Invalid key in boolean get"); break; diff --git a/compiler/luci/env/src/UserSettings.test.cpp b/compiler/luci/env/src/UserSettings.test.cpp index 8d9d1875b..899c0c2a1 100644 --- a/compiler/luci/env/src/UserSettings.test.cpp +++ b/compiler/luci/env/src/UserSettings.test.cpp @@ -51,6 +51,18 @@ TEST(UserSettings, DisableValidation) ASSERT_TRUE(settings->get(luci::UserSettings::Key::DisableValidation)); } +TEST(UserSettings, ProfilingDataGen) +{ + auto settings = luci::UserSettings::settings(); + ASSERT_NE(nullptr, settings); + + settings->set(luci::UserSettings::Key::ProfilingDataGen, false); + ASSERT_FALSE(settings->get(luci::UserSettings::Key::ProfilingDataGen)); + + settings->set(luci::UserSettings::Key::ProfilingDataGen, true); + ASSERT_TRUE(settings->get(luci::UserSettings::Key::ProfilingDataGen)); +} + TEST(UserSettings, undefined_set_NEG) { auto settings = luci::UserSettings::settings(); diff --git a/compiler/luci/export/CMakeLists.txt b/compiler/luci/export/CMakeLists.txt index fe4382ecd..01f737110 100644 --- a/compiler/luci/export/CMakeLists.txt +++ b/compiler/luci/export/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries(luci_export PRIVATE mio_circle) target_link_libraries(luci_export PRIVATE luci_env) target_link_libraries(luci_export PRIVATE luci_log) target_link_libraries(luci_export PRIVATE luci_logex) +target_link_libraries(luci_export PRIVATE luci_profile) target_link_libraries(luci_export PRIVATE nncc_common) target_link_libraries(luci_export PRIVATE locop) target_link_libraries(luci_export PRIVATE oops) diff --git a/compiler/luci/export/include/luci/CircleFileExpContract.h b/compiler/luci/export/include/luci/CircleFileExpContract.h index eeaf2d9bb..8ef1b5e0c 100644 --- a/compiler/luci/export/include/luci/CircleFileExpContract.h +++ b/compiler/luci/export/include/luci/CircleFileExpContract.h @@ -33,7 +33,7 @@ struct CircleFileExpContract : public luci::CircleExporter::Contract { public: CircleFileExpContract(luci::Module *module, const std::string &filename) - : _module(module), _filepath(filename) + : _module(module), _filepath(filename) { // NOTHING TO DO } diff --git a/compiler/luci/export/src/CircleExportMetadata.cpp b/compiler/luci/export/src/CircleExportMetadata.cpp new file mode 100644 index 000000000..ef905a882 --- /dev/null +++ b/compiler/luci/export/src/CircleExportMetadata.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021 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 "CircleExportMetadata.h" + +#include <luci/UserSettings.h> + +namespace +{ + +void write_u32(std::vector<uint8_t> &to, uint32_t value) +{ + to.emplace_back(0xFF & (value >> 0 * 8)); + to.emplace_back(0xFF & (value >> 1 * 8)); + to.emplace_back(0xFF & (value >> 2 * 8)); + to.emplace_back(0xFF & (value >> 3 * 8)); +} + +flatbuffers::Offset<circle::Metadata> metadata_offset(flatbuffers::FlatBufferBuilder &builder, + luci::SerializedModelData &md, + const std::vector<uint8_t> &data, + const std::string &metadata_name) +{ + auto buffer_id = static_cast<uint32_t>(md._buffers.size()); + md._buffers.push_back(circle::CreateBufferDirect(builder, &data)); + return circle::CreateMetadataDirect(builder, metadata_name.c_str(), buffer_id); +} + +} // namespace + +namespace luci +{ + +// 'source_table' is encoded to binary format. +const std::vector<uint8_t> CircleExportMetadata::encoded_source_table(void) +{ + std::vector<uint8_t> data; + + write_u32(data, _source_table.size()); + + for (auto &kv : _source_table) + { + const auto id = kv.first; + write_u32(data, id); + + const auto origin_name = kv.second; + const auto length = origin_name.length(); + write_u32(data, length + 1); // name + '\0 + + for (uint32_t i = 0; i < length; ++i) + { + data.emplace_back(origin_name.at(i)); + } + data.emplace_back('\0'); + } + + return data; +} + +// 'op_table' is encoded to binary format. +const std::vector<uint8_t> CircleExportMetadata::encoded_op_table(void) +{ + std::vector<uint8_t> data; + + write_u32(data, _op_table.size()); + + for (auto &kv : _op_table) + { + const auto id = kv.first; + write_u32(data, id); + + const auto origins = kv.second; + const auto node_num = origins.size(); + write_u32(data, node_num); + + for (auto origin : origins) + { + write_u32(data, origin); + } + } + + return data; +} + +} // namespace luci + +namespace luci +{ + +std::vector<flatbuffers::Offset<circle::Metadata>> +createCircleMetadataVector(flatbuffers::FlatBufferBuilder &builder, luci::SerializedModelData &md) +{ + std::vector<flatbuffers::Offset<circle::Metadata>> metadata_vec; + + auto settings = luci::UserSettings::settings(); + if (settings->get(luci::UserSettings::Key::ProfilingDataGen)) + { + metadata_vec.emplace_back( + metadata_offset(builder, md, md._metadata.encoded_source_table(), "ONE_source_table")); + + metadata_vec.emplace_back( + metadata_offset(builder, md, md._metadata.encoded_op_table(), "ONE_op_table")); + } + + return metadata_vec; +} + +} // namespace luci diff --git a/compiler/luci/export/src/CircleExportMetadata.h b/compiler/luci/export/src/CircleExportMetadata.h new file mode 100644 index 000000000..10cda421e --- /dev/null +++ b/compiler/luci/export/src/CircleExportMetadata.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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 __LUCI_CIRCLE_EXPORT_METADATA_H__ +#define __LUCI_CIRCLE_EXPORT_METADATA_H__ + +#include "SerializedData.h" + +#include <flatbuffers/flatbuffers.h> +#include <mio/circle/schema_generated.h> + +namespace luci +{ + +/** + * @brief Create Metadata corresponding to model metadata + */ +std::vector<flatbuffers::Offset<circle::Metadata>> +createCircleMetadataVector(flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md); + +} // namespace luci + +#endif // __LUCI_CIRCLE_EXPORT_METADATA_H__ diff --git a/compiler/luci/export/src/CircleExporterImpl.cpp b/compiler/luci/export/src/CircleExporterImpl.cpp index df7542797..7e218191c 100644 --- a/compiler/luci/export/src/CircleExporterImpl.cpp +++ b/compiler/luci/export/src/CircleExporterImpl.cpp @@ -16,10 +16,13 @@ #include "CircleExporterImpl.h" #include "Optimize.h" +#include "CircleExportMetadata.h" #include "CircleTensorExporter.h" #include "CircleOperationExporter.h" #include "CircleExporterUtils.h" +#include <luci/IR/CircleNodes.h> + #include <oops/InternalExn.h> #include <mio/circle/schema_generated.h> #include <flatbuffers/flatbuffers.h> @@ -27,46 +30,16 @@ #include <cassert> #include <unordered_map> #include <string> -#include <stdexcept> +#include <vector> namespace { -luci::CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index) -{ - for (uint32_t n = 0; n < g->nodes()->size(); ++n) - { - if (auto input = dynamic_cast<luci::CircleInput *>(g->nodes()->at(n))) - { - if (input->indexed() && input->index() == index) - { - return input; - } - } - } - return nullptr; -} - -luci::CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index) -{ - for (uint32_t n = 0; n < g->nodes()->size(); ++n) - { - if (auto output = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n))) - { - if (output->indexed() && output->index() == index) - { - return output; - } - } - } - return nullptr; -} - void registerGraphInputTensors(loco::Graph *graph, luci::SubGraphContext &ctx) { for (uint32_t n = 0; n < graph->inputs()->size(); ++n) { - auto node = input_node(graph, n); + auto node = luci::input_node(graph, n); assert(node != nullptr); ctx._inputs.push_back(luci::get_tensor_index(node)); } @@ -76,7 +49,7 @@ void registerGraphOutputTensors(loco::Graph *graph, luci::SubGraphContext &ctx) { for (uint32_t n = 0; n < graph->outputs()->size(); ++n) { - auto push = output_node(graph, n); + auto push = luci::output_node(graph, n); assert(push != nullptr); auto node = push->from(); assert(node != nullptr); @@ -113,7 +86,7 @@ encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode, else { operator_codes_vec[idx] = - CreateOperatorCode(builder, it.first.opcode, builder.CreateString(it.first.custom_code)); + CreateOperatorCode(builder, it.first.opcode, builder.CreateString(it.first.custom_code)); } } @@ -186,16 +159,16 @@ void CircleExporterImpl::exportGraph(loco::Graph *graph) std::string description_str = "nnpackage"; auto description = _builder.CreateString(description_str); + // Metadata + auto metadata_vec = createCircleMetadataVector(_builder, md); + auto metadata = _builder.CreateVector(std::vector<Offset<Metadata>>(metadata_vec)); + // create array of buffers auto buffers = _builder.CreateVector(md._buffers); - // empty metadata - std::vector<int> metadata_buffer_vec; - auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec); - // Model auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description, - buffers, metadata_buffer); + buffers, 0 /* metadata_buffer */, metadata); FinishModelBuffer(_builder, model_offset); } @@ -250,19 +223,19 @@ void CircleExporterImpl::exportModule(Module *module) std::string description_str = "nnpackage"; auto description = _builder.CreateString(description_str); + // Metadata + auto metadata_vec = createCircleMetadataVector(_builder, md); + auto metadata = _builder.CreateVector(std::vector<Offset<Metadata>>(metadata_vec)); + // create array of buffers auto buffers = _builder.CreateVector(md._buffers); - // empty metadata - std::vector<int> metadata_buffer_vec; - auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec); - // This version is taken from comment in fbs constexpr uint32_t version = 0; // Model auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description, - buffers, metadata_buffer); + buffers, 0 /* metadata_buffer */, metadata); FinishModelBuffer(_builder, model_offset); } diff --git a/compiler/luci/export/src/CircleExporterImpl.h b/compiler/luci/export/src/CircleExporterImpl.h index e5d5b5a00..069f62afd 100644 --- a/compiler/luci/export/src/CircleExporterImpl.h +++ b/compiler/luci/export/src/CircleExporterImpl.h @@ -22,8 +22,6 @@ #include "SerializedData.h" -#include "SerializedData.h" - #include <mio/circle/schema_generated.h> #include <loco.h> diff --git a/compiler/luci/export/src/CircleExporterUtils.cpp b/compiler/luci/export/src/CircleExporterUtils.cpp index 3715513e0..1b21fdd86 100644 --- a/compiler/luci/export/src/CircleExporterUtils.cpp +++ b/compiler/luci/export/src/CircleExporterUtils.cpp @@ -208,13 +208,13 @@ circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> * // // NOTE input and output 'feature' map are shape of NHWC bool same_padding_criterion_1 = - (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && - (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); + (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) && + (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1); // For same padding, rear padding is same or bigger than front padding by at most 1 bool same_padding_criterion_2 = - (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && - (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); + (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) && + (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1); if (same_padding_criterion_1 && same_padding_criterion_2) return circle::Padding_SAME; diff --git a/compiler/luci/export/src/CircleOperationExporter.cpp b/compiler/luci/export/src/CircleOperationExporter.cpp index 4343cf3c9..4bf674b9b 100644 --- a/compiler/luci/export/src/CircleOperationExporter.cpp +++ b/compiler/luci/export/src/CircleOperationExporter.cpp @@ -21,6 +21,7 @@ #include <luci/IR/CircleNode.h> #include <luci/IR/CircleNodes.h> #include <luci/IR/CircleNodeVisitor.h> +#include <luci/Profile/CircleNodeOrigin.h> #include <luci/UserSettings.h> #include <luci/Log.h> @@ -53,8 +54,8 @@ template <class CirclePool2D> void export_pool_2d(ExportContext &ctx, CirclePool2D *node, circle::BuiltinOperator builtin_op) { LUCI_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D || - builtin_op == circle::BuiltinOperator_L2_POOL_2D || - builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D, + builtin_op == circle::BuiltinOperator_L2_POOL_2D || + builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D, "Should be L2Pool, MaxPool or AvgPool"); LUCI_ASSERT(node->padding() != luci::Padding::UNDEFINED, "Padding is not set"); @@ -81,7 +82,7 @@ void export_node(ExportContext &ctx, loco::Node *node, circle::BuiltinOperator b circle::BuiltinOptions bot, flatbuffers::Offset<void> options_offset) { uint32_t op_idx = - ctx.md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version()); + ctx.md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version()); std::vector<int32_t> inputs_vec; std::vector<int32_t> outputs_vec{get_tensor_index(node)}; for (uint32_t i = 0; i < node->arity(); ++i) @@ -98,7 +99,7 @@ void export_node(ExportContext &ctx, loco::Node *node, circle::BuiltinOperator b void export_node(ExportContext &ctx, loco::Node *node, circle::BuiltinOperator bop) { uint32_t op_idx = - ctx.md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version()); + ctx.md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version()); std::vector<int32_t> inputs_vec; std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; for (uint32_t i = 0; i < node->arity(); ++i) @@ -152,7 +153,7 @@ void export_node(ExportContext &ctx, luci::CircleCast *node) void export_node(ExportContext &ctx, luci::CircleConcatenation *node) { uint32_t op_idx = - ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION, node->op_version()); + ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION, node->op_version()); std::vector<int32_t> inputs_vec; std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; @@ -171,6 +172,7 @@ void export_node(ExportContext &ctx, luci::CircleConcatenation *node) void export_node(ExportContext &ctx, luci::CircleCustom *node) { auto custom_outputs = loco::succs(node); + assert(custom_outputs.size() == node->numOutputs()); uint32_t op_idx = ctx.md.registerCustomOpcode(node->custom_code()); std::vector<int32_t> inputs_vec; @@ -260,9 +262,9 @@ void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV4 *node) uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_NON_MAX_SUPPRESSION_V4, node->op_version()); std::vector<int32_t> inputs_vec{ - get_tensor_index(node->boxes()), get_tensor_index(node->scores()), - get_tensor_index(node->max_output_size()), get_tensor_index(node->iou_threshold()), - get_tensor_index(node->score_threshold()), + get_tensor_index(node->boxes()), get_tensor_index(node->scores()), + get_tensor_index(node->max_output_size()), get_tensor_index(node->iou_threshold()), + get_tensor_index(node->score_threshold()), }; std::vector<int32_t> outputs_vec; @@ -290,8 +292,8 @@ void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV4 *node) auto outputs = ctx.builder.CreateVector(outputs_vec); auto options = CreateNonMaxSuppressionV4Options(ctx.builder); auto op_offset = - CreateOperator(ctx.builder, op_idx, inputs, outputs, - circle::BuiltinOptions_NonMaxSuppressionV4Options, options.Union()); + CreateOperator(ctx.builder, op_idx, inputs, outputs, + circle::BuiltinOptions_NonMaxSuppressionV4Options, options.Union()); ctx.gd._operators.push_back(op_offset); } @@ -303,9 +305,9 @@ void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV5 *node) uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_NON_MAX_SUPPRESSION_V5, node->op_version()); std::vector<int32_t> inputs_vec{ - get_tensor_index(node->boxes()), get_tensor_index(node->scores()), - get_tensor_index(node->max_output_size()), get_tensor_index(node->iou_threshold()), - get_tensor_index(node->score_threshold()), get_tensor_index(node->soft_nms_sigma()), + get_tensor_index(node->boxes()), get_tensor_index(node->scores()), + get_tensor_index(node->max_output_size()), get_tensor_index(node->iou_threshold()), + get_tensor_index(node->score_threshold()), get_tensor_index(node->soft_nms_sigma()), }; std::vector<int32_t> outputs_vec; @@ -333,15 +335,15 @@ void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV5 *node) auto outputs = ctx.builder.CreateVector(outputs_vec); auto options = CreateNonMaxSuppressionV5Options(ctx.builder); auto op_offset = - CreateOperator(ctx.builder, op_idx, inputs, outputs, - circle::BuiltinOptions_NonMaxSuppressionV5Options, options.Union()); + CreateOperator(ctx.builder, op_idx, inputs, outputs, + circle::BuiltinOptions_NonMaxSuppressionV5Options, options.Union()); ctx.gd._operators.push_back(op_offset); } void export_node(ExportContext &ctx, luci::CircleReverseV2 *node) { uint32_t op_idx = - ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_REVERSE_V2, node->op_version()); + ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_REVERSE_V2, node->op_version()); std::vector<int32_t> inputs_vec{get_tensor_index(node->tensor()), get_tensor_index(node->axis())}; std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))}; auto inputs = ctx.builder.CreateVector(inputs_vec); @@ -397,7 +399,7 @@ void export_node(ExportContext &ctx, luci::CircleSplitV *node) assert(int32_t(split_outs.size()) == node->num_split()); uint32_t op_idx = - ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT_V, node->op_version()); + ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT_V, node->op_version()); std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->size_splits()), get_tensor_index(node->split_dim())}; @@ -438,7 +440,7 @@ void export_node(ExportContext &ctx, luci::CircleTopKV2 *node) assert(outs_count == 2); uint32_t op_idx = - ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_TOPK_V2, node->op_version()); + ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_TOPK_V2, node->op_version()); std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->k())}; std::vector<int32_t> outputs_vec; @@ -475,7 +477,7 @@ void export_node(ExportContext &ctx, luci::CircleUnique *node) auto unique_outs = loco::succs(node); assert(int32_t(unique_outs.size()) == 2); uint32_t op_idx = - ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_UNIQUE, node->op_version()); + ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_UNIQUE, node->op_version()); std::vector<int32_t> inputs_vec{get_tensor_index(node->input())}; std::vector<int32_t> outputs_vec; @@ -526,7 +528,7 @@ void export_node(ExportContext &ctx, luci::CircleUnpack *node) } uint32_t op_idx = - ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_UNPACK, node->op_version()); + ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_UNPACK, node->op_version()); std::vector<int32_t> inputs_vec{get_tensor_index(node->value())}; std::vector<int32_t> outputs_vec; @@ -622,6 +624,7 @@ public: void visit(luci::CircleAveragePool2D *) final; void visit(luci::CircleBatchMatMul *) final; void visit(luci::CircleBatchToSpaceND *) final; + void visit(luci::CircleBidirectionalSequenceLSTM *) final; void visit(luci::CircleCast *) final; void visit(luci::CircleCeil *) final; void visit(luci::CircleConcatenation *) final; @@ -637,6 +640,7 @@ public: void visit(luci::CircleEqual *) final; void visit(luci::CircleExp *) final; void visit(luci::CircleExpandDims *) final; + void visit(luci::CircleFakeQuant *) final; void visit(luci::CircleFill *) final; void visit(luci::CircleFloor *) final; void visit(luci::CircleFloorDiv *) final; @@ -734,6 +738,7 @@ public: void visit(luci::CircleOutputDummy *) final {} void visit(luci::CircleOutputExclude *) final {} // Virtual for multiple-outputs + void visit(luci::CircleBidirectionalSequenceLSTMOut *) final {} void visit(luci::CircleCustomOut *) final {} void visit(luci::CircleIfOut *) final {} void visit(luci::CircleNonMaxSuppressionV4Out *) final {} @@ -782,8 +787,8 @@ void OperationExporter::visit(luci::CircleAbs *node) void OperationExporter::visit(luci::CircleAdd *node) { export_simple( - node, circle::BuiltinOperator_ADD, circle::BuiltinOptions_AddOptions, - CreateAddOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); + node, circle::BuiltinOperator_ADD, circle::BuiltinOptions_AddOptions, + CreateAddOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); } void OperationExporter::visit(luci::CircleAddN *node) { export_node(_ctx, node); } @@ -791,15 +796,15 @@ void OperationExporter::visit(luci::CircleAddN *node) { export_node(_ctx, node); void OperationExporter::visit(luci::CircleArgMax *node) { export_simple( - node, circle::BuiltinOperator_ARG_MAX, circle::BuiltinOptions_ArgMaxOptions, - CreateArgMaxOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union()); + node, circle::BuiltinOperator_ARG_MAX, circle::BuiltinOptions_ArgMaxOptions, + CreateArgMaxOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union()); } void OperationExporter::visit(luci::CircleArgMin *node) { export_simple( - node, circle::BuiltinOperator_ARG_MIN, circle::BuiltinOptions_ArgMinOptions, - CreateArgMinOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union()); + node, circle::BuiltinOperator_ARG_MIN, circle::BuiltinOptions_ArgMinOptions, + CreateArgMinOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union()); } void OperationExporter::visit(luci::CircleAveragePool2D *node) @@ -814,6 +819,48 @@ void OperationExporter::visit(luci::CircleBatchMatMul *node) CreateBatchMatMulOptions(_ctx.builder, node->adj_x(), node->adj_y()).Union()); } +void OperationExporter::visit(luci::CircleBidirectionalSequenceLSTM *node) +{ + auto bidi_lstm_outs = loco::succs(node); + assert((bidi_lstm_outs.size() == 1) || (bidi_lstm_outs.size() == 2)); + uint32_t op_idx = _ctx.md.registerBuiltinOpcode( + circle::BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, node->op_version()); + + std::vector<int32_t> inputs_vec{get_tensor_index(node->input())}; + std::vector<int32_t> outputs_vec; + + for (int32_t index = 0; index < 2; index++) + { + // store in order of index + bool found = false; + for (auto out : bidi_lstm_outs) + { + auto bidi_lstm_out = loco::must_cast<luci::CircleBidirectionalSequenceLSTMOut *>(out); + if (bidi_lstm_out->index() == index) + { + outputs_vec.push_back(get_tensor_index(bidi_lstm_out)); + found = true; + break; + } + } + if (!found) + { + INTERNAL_EXN("Invalid BidirectionalSequenceLSTM output"); + } + } + + auto inputs = _ctx.builder.CreateVector(inputs_vec); + auto outputs = _ctx.builder.CreateVector(outputs_vec); + auto options = CreateBidirectionalSequenceLSTMOptions( + _ctx.builder, to_circle_actfunc(node->fusedActivationFunction()), node->cell_clip(), + node->proj_clip(), node->merge_outputs(), node->time_major(), + node->asymmetric_quantize_inputs()); + auto op_offset = + CreateOperator(_ctx.builder, op_idx, inputs, outputs, + circle::BuiltinOptions_BidirectionalSequenceLSTMOptions, options.Union()); + _ctx.gd._operators.push_back(op_offset); +} + void OperationExporter::visit(luci::CircleCast *node) { export_node(_ctx, node); } void OperationExporter::visit(luci::CircleCeil *node) @@ -837,7 +884,7 @@ void OperationExporter::visit(luci::CircleConv2D *node) node->stride()->w(), node->stride()->h(), to_circle_actfunc(node->fusedActivationFunction()), node->dilation()->w(), node->dilation()->h()) - .Union()); + .Union()); } void OperationExporter::visit(luci::CircleCos *node) @@ -857,14 +904,13 @@ void OperationExporter::visit(luci::CircleDepthToSpace *node) void OperationExporter::visit(luci::CircleDepthwiseConv2D *node) { - export_simple(node, circle::BuiltinOperator_DEPTHWISE_CONV_2D, - circle::BuiltinOptions_DepthwiseConv2DOptions, - CreateDepthwiseConv2DOptions(_ctx.builder, getOpPadding(node->padding()), - node->stride()->w(), node->stride()->h(), - node->depthMultiplier(), - to_circle_actfunc(node->fusedActivationFunction()), - node->dilation()->w(), node->dilation()->h()) - .Union()); + export_simple( + node, circle::BuiltinOperator_DEPTHWISE_CONV_2D, circle::BuiltinOptions_DepthwiseConv2DOptions, + CreateDepthwiseConv2DOptions(_ctx.builder, getOpPadding(node->padding()), node->stride()->w(), + node->stride()->h(), node->depthMultiplier(), + to_circle_actfunc(node->fusedActivationFunction()), + node->dilation()->w(), node->dilation()->h()) + .Union()); } void OperationExporter::visit(luci::CircleDequantize *node) @@ -875,8 +921,8 @@ void OperationExporter::visit(luci::CircleDequantize *node) void OperationExporter::visit(luci::CircleDiv *node) { export_simple( - node, circle::BuiltinOperator_DIV, circle::BuiltinOptions_DivOptions, - CreateDivOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); + node, circle::BuiltinOperator_DIV, circle::BuiltinOptions_DivOptions, + CreateDivOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); } void OperationExporter::visit(luci::CircleElu *node) @@ -902,6 +948,14 @@ void OperationExporter::visit(luci::CircleExpandDims *node) CreateExpandDimsOptions(_ctx.builder).Union()); } +void OperationExporter::visit(luci::CircleFakeQuant *node) +{ + export_simple(node, circle::BuiltinOperator_FAKE_QUANT, circle::BuiltinOptions_FakeQuantOptions, + CreateFakeQuantOptions(_ctx.builder, node->min(), node->max(), node->num_bits(), + node->narrow_range()) + .Union()); +} + void OperationExporter::visit(luci::CircleFill *node) { export_simple(node, circle::BuiltinOperator_FILL, circle::BuiltinOptions_FillOptions, @@ -928,10 +982,10 @@ void OperationExporter::visit(luci::CircleFloorMod *node) void OperationExporter::visit(luci::CircleFullyConnected *node) { export_simple( - node, circle::BuiltinOperator_FULLY_CONNECTED, circle::BuiltinOptions_FullyConnectedOptions, - CreateFullyConnectedOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction()), - to_circle_weightsformat(node->weights_format())) - .Union()); + node, circle::BuiltinOperator_FULLY_CONNECTED, circle::BuiltinOptions_FullyConnectedOptions, + CreateFullyConnectedOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction()), + to_circle_weightsformat(node->weights_format())) + .Union()); } void OperationExporter::visit(luci::CircleGather *node) @@ -964,9 +1018,8 @@ void OperationExporter::visit(luci::CircleIf *node) { export_node(_ctx, node); } void OperationExporter::visit(luci::CircleL2Normalize *node) { export_simple( - node, circle::BuiltinOperator_L2_NORMALIZATION, circle::BuiltinOptions_L2NormOptions, - CreateL2NormOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())) - .Union()); + node, circle::BuiltinOperator_L2_NORMALIZATION, circle::BuiltinOptions_L2NormOptions, + CreateL2NormOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); } void OperationExporter::visit(luci::CircleL2Pool2D *node) @@ -998,7 +1051,7 @@ void OperationExporter::visit(luci::CircleLocalResponseNormalization *node) circle::BuiltinOptions_LocalResponseNormalizationOptions, CreateLocalResponseNormalizationOptions(_ctx.builder, node->radius(), node->bias(), node->alpha(), node->beta()) - .Union()); + .Union()); } void OperationExporter::visit(luci::CircleLog *node) @@ -1074,15 +1127,15 @@ void OperationExporter::visit(luci::CircleMinimum *node) void OperationExporter::visit(luci::CircleMirrorPad *node) { export_simple( - node, circle::BuiltinOperator_MIRROR_PAD, circle::BuiltinOptions_MirrorPadOptions, - CreateMirrorPadOptions(_ctx.builder, to_circle_mirrorpadmode(node->mode())).Union()); + node, circle::BuiltinOperator_MIRROR_PAD, circle::BuiltinOptions_MirrorPadOptions, + CreateMirrorPadOptions(_ctx.builder, to_circle_mirrorpadmode(node->mode())).Union()); } void OperationExporter::visit(luci::CircleMul *node) { export_simple( - node, circle::BuiltinOperator_MUL, circle::BuiltinOptions_MulOptions, - CreateMulOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); + node, circle::BuiltinOperator_MUL, circle::BuiltinOptions_MulOptions, + CreateMulOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); } void OperationExporter::visit(luci::CircleNeg *node) @@ -1190,7 +1243,7 @@ void OperationExporter::visit(luci::CircleReluN1To1 *node) void OperationExporter::visit(luci::CircleReshape *node) { auto new_shape = _ctx.builder.CreateVector<int32_t>( - node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); }); + node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); }); export_simple(node, circle::BuiltinOperator_RESHAPE, circle::BuiltinOptions_ReshapeOptions, CreateReshapeOptions(_ctx.builder, new_shape).Union()); @@ -1199,9 +1252,9 @@ void OperationExporter::visit(luci::CircleReshape *node) void OperationExporter::visit(luci::CircleResizeBilinear *node) { export_simple( - node, circle::BuiltinOperator_RESIZE_BILINEAR, circle::BuiltinOptions_ResizeBilinearOptions, - CreateResizeBilinearOptions(_ctx.builder, node->align_corners(), node->half_pixel_centers()) - .Union()); + node, circle::BuiltinOperator_RESIZE_BILINEAR, circle::BuiltinOptions_ResizeBilinearOptions, + CreateResizeBilinearOptions(_ctx.builder, node->align_corners(), node->half_pixel_centers()) + .Union()); } void OperationExporter::visit(luci::CircleResizeNearestNeighbor *node) @@ -1214,8 +1267,8 @@ void OperationExporter::visit(luci::CircleResizeNearestNeighbor *node) void OperationExporter::visit(luci::CircleReverseSequence *node) { export_simple( - node, circle::BuiltinOperator_REVERSE_SEQUENCE, circle::BuiltinOptions_ReverseSequenceOptions, - CreateReverseSequenceOptions(_ctx.builder, node->seq_axis(), node->batch_axis()).Union()); + node, circle::BuiltinOperator_REVERSE_SEQUENCE, circle::BuiltinOptions_ReverseSequenceOptions, + CreateReverseSequenceOptions(_ctx.builder, node->seq_axis(), node->batch_axis()).Union()); } void OperationExporter::visit(luci::CircleReverseV2 *node) { export_node(_ctx, node); } @@ -1334,14 +1387,14 @@ void OperationExporter::visit(luci::CircleStridedSlice *node) CreateStridedSliceOptions(_ctx.builder, node->begin_mask(), node->end_mask(), node->ellipsis_mask(), node->new_axis_mask(), node->shrink_axis_mask()) - .Union()); + .Union()); } void OperationExporter::visit(luci::CircleSub *node) { export_simple( - node, circle::BuiltinOperator_SUB, circle::BuiltinOptions_SubOptions, - CreateSubOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); + node, circle::BuiltinOperator_SUB, circle::BuiltinOptions_SubOptions, + CreateSubOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union()); } void OperationExporter::visit(luci::CircleSum *node) @@ -1375,7 +1428,7 @@ void OperationExporter::visit(luci::CircleTransposeConv *node) circle::BuiltinOptions_TransposeConvOptions, CreateTransposeConvOptions(_ctx.builder, getOpPadding(node->padding()), node->stride()->w(), node->stride()->h()) - .Union()); + .Union()); } void OperationExporter::visit(luci::CircleUnidirectionalSequenceLSTM *node) @@ -1383,10 +1436,10 @@ void OperationExporter::visit(luci::CircleUnidirectionalSequenceLSTM *node) export_simple(node, circle::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, circle::BuiltinOptions_UnidirectionalSequenceLSTMOptions, CreateUnidirectionalSequenceLSTMOptions( - _ctx.builder, to_circle_actfunc(node->fusedActivationFunction()), - node->cell_clip(), node->proj_clip(), node->time_major(), - node->asymmetric_quantize_inputs()) - .Union()); + _ctx.builder, to_circle_actfunc(node->fusedActivationFunction()), + node->cell_clip(), node->proj_clip(), node->time_major(), + node->asymmetric_quantize_inputs()) + .Union()); } void OperationExporter::visit(luci::CircleUnique *node) { export_node(_ctx, node); } @@ -1413,14 +1466,14 @@ void OperationExporter::visit(luci::CircleBCQFullyConnected *node) circle::BuiltinOptions_BCQFullyConnectedOptions, CreateBCQFullyConnectedOptions(_ctx.builder, node->weights_hidden_size(), to_circle_actfunc(node->fusedActivationFunction())) - .Union()); + .Union()); } void OperationExporter::visit(luci::CircleBCQGather *node) { export_simple( - node, circle::BuiltinOperator_BCQ_GATHER, circle::BuiltinOptions_BCQGatherOptions, - CreateBCQGatherOptions(_ctx.builder, node->input_hidden_size(), node->axis()).Union()); + node, circle::BuiltinOperator_BCQ_GATHER, circle::BuiltinOptions_BCQGatherOptions, + CreateBCQGatherOptions(_ctx.builder, node->input_hidden_size(), node->axis()).Union()); } void OperationExporter::visit(luci::CircleInstanceNorm *node) @@ -1429,7 +1482,7 @@ void OperationExporter::visit(luci::CircleInstanceNorm *node) circle::BuiltinOptions_InstanceNormOptions, CreateInstanceNormOptions(_ctx.builder, node->epsilon(), to_circle_actfunc(node->fusedActivationFunction())) - .Union()); + .Union()); } void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md, @@ -1439,7 +1492,19 @@ void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, Seria { ExportContext ctx{builder, md, gd}; OperationExporter exporter{ctx}; + + const auto ops_size = gd._operators.size(); + circle_node->accept(&exporter); + if (has_origin(circle_node) && ops_size != gd._operators.size()) + { + const auto node_id = gd._operators.size() - 1; + for (auto source : get_origin(circle_node)->sources()) + { + md._metadata.add_source_table(source->id(), source->name()); + md._metadata.add_op_table(node_id, source->id()); + } + } } else { diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp index 9bdfa0079..fefdf4e73 100644 --- a/compiler/luci/export/src/CircleTensorExporter.cpp +++ b/compiler/luci/export/src/CircleTensorExporter.cpp @@ -15,11 +15,9 @@ */ #include "CircleTensorExporter.h" -#include "TypeBridge.h" #include <luci/IR/CircleNodes.h> #include <luci/IR/CircleNodeVisitor.h> -#include <luci/IR/CircleShapeSignature.h> #include <luci/Service/CircleTypeInference.h> #include <luci/Service/CircleShapeInference.h> #include <luci/Log.h> @@ -38,10 +36,10 @@ namespace using namespace luci; -class CircleTensoInfo +class CircleTensorInfo { public: - CircleTensoInfo() = default; + CircleTensorInfo() = default; public: void name(const std::string &name) { _name = name; } @@ -54,9 +52,6 @@ public: const ShapeDescription &shape(void) const { return _shape; } void shape(const ShapeDescription &shape) { _shape = shape; } - const ShapeSignature &shape_signature(void) const { return _shape_signature; } - void shape_signature(const ShapeSignature &ss) { _shape_signature = ss; } - luci::ShapeStatus shape_status(void) const { return _shape_status; } void shape_status(luci::ShapeStatus ss) { _shape_status = ss; } @@ -75,7 +70,6 @@ private: circle::TensorType _dtype{circle::TensorType_FLOAT32}; ShapeDescription _shape{}; - ShapeSignature _shape_signature; luci::ShapeStatus _shape_status{luci::ShapeStatus::UNDEFINED}; luci::CircleConst *_content = nullptr; @@ -83,7 +77,29 @@ private: luci::SparsityParam *_sparsityparam = nullptr; }; -using CircleTensorContext = std::vector<CircleTensoInfo>; +class CircleTensorContext +{ +public: + CircleTensorContext() = default; + +public: + void emplace_back(CircleTensorInfo &ti) + { + assert(_names.find(ti.name()) == _names.end()); + _tis.emplace_back(ti); + _names.insert(ti.name()); + } + size_t size(void) const { return _tis.size(); } + std::vector<CircleTensorInfo>::iterator begin(void) { return _tis.begin(); } + std::vector<CircleTensorInfo>::iterator end(void) { return _tis.end(); } + +public: + bool exist(const std::string &name) const { return _names.find(name) != _names.end(); } + +private: + std::vector<CircleTensorInfo> _tis; + std::set<std::string> _names; +}; struct NoOpDetector final : public luci::CircleNodeMutableVisitor<bool> { @@ -102,17 +118,23 @@ void allocateCircleTensorInfo(CircleNode *node, CircleTensorContext &ctx) auto tensor_index = static_cast<CircleTensorIndex>(ctx.size()); // TODO Use Graph-level metadata for Input & Output - // auto tensor_name = "t_" + std::to_string(tensor_index); std::string tensor_name = node->name(); - if (tensor_name.empty()) - tensor_name = "t_" + std::to_string(tensor_index); + // NOTE tensor_name maybe empty. this assertion will alert when this happens. + // currently we require tensor should have a name. + // TODO if this breaks, fix the cause or permit empty tensor_name. + assert(!tensor_name.empty()); + if (ctx.exist(tensor_name)) + { + // NOTE this should assign unique name for a Tensor. + tensor_name = tensor_name + "_" + std::to_string(tensor_index); + assert(!ctx.exist(tensor_name)); + } INFO(l) << "[luci] Tensor for " << tensor_name << ": " << tensor_index << std::endl; - CircleTensoInfo tensor_info; + CircleTensorInfo tensor_info; tensor_info.name(tensor_name); tensor_info.dtype(to_circle_tensortype(node->dtype())); - tensor_info.shape_signature(node->shape_signature()); if (node->shape_status() == ShapeStatus::VALID) tensor_info.shape(to_shape_description(node)); tensor_info.shape_status(node->shape_status()); @@ -146,19 +168,55 @@ private: } public: + bool visit(luci::CircleBidirectionalSequenceLSTMOut *) final { return true; } + bool visit(luci::CircleCustomOut *) final { return true; } bool visit(luci::CircleIfOut *) final { return true; } + bool visit(luci::CircleNonMaxSuppressionV4Out *) final { return true; } + bool visit(luci::CircleNonMaxSuppressionV5Out *) final { return true; } bool visit(luci::CircleSplitOut *) final { return true; } bool visit(luci::CircleSplitVOut *) final { return true; } bool visit(luci::CircleTopKV2Out *) final { return true; } bool visit(luci::CircleUnpackOut *) final { return true; } + bool visit(luci::CircleUniqueOut *) final { return true; } bool visit(luci::CircleWhileOut *) final { return true; } + bool visit(luci::CircleBidirectionalSequenceLSTM *node) final + { + if (node->merge_outputs()) + { + store_outputs(node, 1); + } + else + { + store_outputs(node, 2); + } + return true; + } + + bool visit(luci::CircleCustom *node) final + { + store_outputs(node, node->numOutputs()); + return true; + } + bool visit(luci::CircleIf *node) final { store_outputs(node, node->output_count()); return true; } + bool visit(luci::CircleNonMaxSuppressionV4 *node) final + { + store_outputs(node, 2); + return true; + } + + bool visit(luci::CircleNonMaxSuppressionV5 *node) final + { + store_outputs(node, 3); + return true; + } + bool visit(luci::CircleSplit *node) final { store_outputs(node, uint32_t(node->num_split())); @@ -183,6 +241,12 @@ public: return true; } + bool visit(luci::CircleUnique *node) final + { + store_outputs(node, 2); + return true; + } + bool visit(luci::CircleWhile *node) final { store_outputs(node, node->output_count()); @@ -237,16 +301,26 @@ flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder, const ShapeDescription &shape) { assert(shape._rank_known && "unknown number of dimensions is not supported"); - return builder.CreateVector(shape._dims); + + std::vector<int32_t> encoded_shape; + encoded_shape.resize(shape._dims.size()); + for (uint32_t i = 0; i < shape._dims.size(); ++i) + encoded_shape.at(i) = shape._dims.at(i) == -1 ? 1 : shape._dims.at(i); + + return builder.CreateVector(encoded_shape); } flatbuffers::Offset<Vector<int32_t>> encodeShapeSignature(FlatBufferBuilder &builder, - const ShapeSignature &shape_signature) + const ShapeDescription &shape) { - if (shape_signature.rank() == 0) - return 0; + assert(shape._rank_known && "unknown number of dimensions is not supported"); + + // shape_signature is set if and only if at least one of dimensions are unknown. + for (uint32_t i = 0; i < shape._dims.size(); ++i) + if (shape._dims.at(i) == -1) + return builder.CreateVector(shape._dims); - return builder.CreateVector(shape_signature.as_vector()); + return flatbuffers::Offset<Vector<int32_t>>(); } flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder) @@ -343,14 +417,14 @@ encodeSparsityParameters(FlatBufferBuilder &builder, luci::SparsityParam *sparsi // array_segments auto circle_array_segments = to_circle_sparse_index_vector(builder, it.array_segments()); auto circle_array_segments_type = - to_circle_sparse_index_vector_type(it.array_segments().type()); + to_circle_sparse_index_vector_type(it.array_segments().type()); // array_indices auto circle_array_indices = to_circle_sparse_index_vector(builder, it.array_indices()); auto circle_array_indices_type = to_circle_sparse_index_vector_type(it.array_indices().type()); auto dim_metadata = circle::CreateDimensionMetadata( - builder, to_circle_dimensiontype(it.format()), it.dense_size(), circle_array_segments_type, - circle_array_segments, circle_array_indices_type, circle_array_indices); + builder, to_circle_dimensiontype(it.format()), it.dense_size(), circle_array_segments_type, + circle_array_segments, circle_array_indices_type, circle_array_indices); dim_metadata_vec.emplace_back(dim_metadata); } @@ -358,6 +432,18 @@ encodeSparsityParameters(FlatBufferBuilder &builder, luci::SparsityParam *sparsi &sparsityparam->block_map, &dim_metadata_vec); } +template <loco::DataType DT> bool has_same_elements(luci::CircleConst *lhs, luci::CircleConst *rhs) +{ + assert(lhs->dtype() == DT); + assert(rhs->dtype() == DT); + assert(lhs->size<DT>() == rhs->size<DT>()); + + for (uint32_t i = 0; i < lhs->size<DT>(); ++i) + if (lhs->at<DT>(i) != rhs->at<DT>(i)) + return false; + return true; +} + bool has_same_values(luci::CircleConst *lhs, luci::CircleConst *rhs) { if (lhs->dtype() != rhs->dtype()) @@ -373,34 +459,31 @@ bool has_same_values(luci::CircleConst *lhs, luci::CircleConst *rhs) switch (lhs->dtype()) { case loco::DataType::FLOAT32: - for (uint32_t i = 0; i < lhs->size<loco::DataType::FLOAT32>(); ++i) - if (lhs->at<loco::DataType::FLOAT32>(i) != rhs->at<loco::DataType::FLOAT32>(i)) - return false; - break; + return has_same_elements<loco::DataType::FLOAT32>(lhs, rhs); + + case loco::DataType::S8: + return has_same_elements<loco::DataType::S8>(lhs, rhs); + + case loco::DataType::S16: + return has_same_elements<loco::DataType::S16>(lhs, rhs); case loco::DataType::S32: - for (uint32_t i = 0; i < lhs->size<loco::DataType::S32>(); ++i) - if (lhs->at<loco::DataType::S32>(i) != rhs->at<loco::DataType::S32>(i)) - return false; - break; + return has_same_elements<loco::DataType::S32>(lhs, rhs); case loco::DataType::S64: - for (uint32_t i = 0; i < lhs->size<loco::DataType::S64>(); ++i) - if (lhs->at<loco::DataType::S64>(i) != rhs->at<loco::DataType::S64>(i)) - return false; - break; + return has_same_elements<loco::DataType::S64>(lhs, rhs); + + case loco::DataType::U8: + return has_same_elements<loco::DataType::U8>(lhs, rhs); case loco::DataType::BOOL: - for (uint32_t i = 0; i < lhs->size<loco::DataType::BOOL>(); ++i) - if (lhs->at<loco::DataType::BOOL>(i) != rhs->at<loco::DataType::BOOL>(i)) - return false; - break; + return has_same_elements<loco::DataType::BOOL>(lhs, rhs); default: - return false; + break; } - return true; + return false; } uint32_t get_buffer_id(FlatBufferBuilder &builder, SerializedModelData &md, luci::CircleConst *node) @@ -433,26 +516,28 @@ uint32_t get_buffer_id(FlatBufferBuilder &builder, SerializedModelData &md, luci } } -void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &builder, +void exportOpDefinedTensor(const CircleTensorInfo &info, FlatBufferBuilder &builder, SerializedModelData &md, SerializedGraphData &gd) { // Create and register output tensor shape flatbuffers::Offset<Vector<int32_t>> shape_offset; + flatbuffers::Offset<Vector<int32_t>> shape_signature_offset; if (info.shape_status() == ShapeStatus::VALID) + { shape_offset = encodeShape(builder, info.shape()); + shape_signature_offset = encodeShapeSignature(builder, info.shape()); + } auto quantparam = encodeQuantizationParameters(builder, info.quantparam()); auto sparsityparam = encodeSparsityParameters(builder, info.sparsityparam()); - auto shape_signature_offset = encodeShapeSignature(builder, info.shape_signature()); - auto buffer_id = get_buffer_id(builder, md, info.content()); auto name_offset = builder.CreateString(info.name()); auto tensor_offset = - CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, quantparam, - /*is_variable*/ false, sparsityparam, shape_signature_offset); + CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset, quantparam, + /*is_variable*/ false, sparsityparam, shape_signature_offset); gd._tensors.push_back(tensor_offset); } diff --git a/compiler/luci/export/src/Optimize.cpp b/compiler/luci/export/src/Optimize.cpp index 036a4a2f9..e59f15204 100644 --- a/compiler/luci/export/src/Optimize.cpp +++ b/compiler/luci/export/src/Optimize.cpp @@ -17,9 +17,8 @@ #include "Optimize.h" #include "ProgressReporter.h" -#include <luci/Pass/ShapeInferencePass.h> -#include <luci/Pass/ShapeSignatureInferencePass.h> -#include <luci/Pass/TypeInferencePass.h> +#include <luci/Pass/CircleShapeInferencePass.h> +#include <luci/Pass/CircleTypeInferencePass.h> #include <logo/Phase.h> @@ -33,9 +32,8 @@ void optimize(loco::Graph *g) logo::Phase phase; { // prepare type and shape before optimization - phase.emplace_back(std::make_unique<TypeInferencePass>()); - phase.emplace_back(std::make_unique<ShapeInferencePass>()); - phase.emplace_back(std::make_unique<ShapeSignatureInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleTypeInferencePass>()); // TODO add more optimization passes (with a knob) } diff --git a/compiler/luci/export/src/ProgressReporter.h b/compiler/luci/export/src/ProgressReporter.h index e91f42592..5d55bcd07 100644 --- a/compiler/luci/export/src/ProgressReporter.h +++ b/compiler/luci/export/src/ProgressReporter.h @@ -28,7 +28,7 @@ class ProgressReporter : public logo::PhaseEventListener { public: ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) - : _graph{graph}, _strategy{strategy} + : _graph{graph}, _strategy{strategy} { // DO NOTHING } diff --git a/compiler/luci/export/src/SerializedData.h b/compiler/luci/export/src/SerializedData.h index c41f50edd..df71e5c21 100644 --- a/compiler/luci/export/src/SerializedData.h +++ b/compiler/luci/export/src/SerializedData.h @@ -48,6 +48,37 @@ struct OpCode } }; +class CircleExportMetadata +{ +public: + void add_source_table(uint32_t source_id, std::string origin_name) + { + // Model with multiple subgraph may have different origin_name + // even if source_id is same. However, as we do not consider about + // multiple subgraph in profiling for now, just do not care those cases + // and support them correctly in the future. + _source_table.emplace(source_id, origin_name); + } + + void add_op_table(uint32_t node_id, uint32_t source_id) + { + // Model with multiple subgraph may have duplicated node id. + // For now, as we do not consider about multiple subgraph in profiling, + // just ignore those cases and support them in the future. + if (_op_table.find(node_id) == _op_table.end()) + _op_table.emplace(node_id, std::set<uint32_t>()); + _op_table.at(node_id).emplace(source_id); + } + +public: + const std::vector<uint8_t> encoded_source_table(void); + const std::vector<uint8_t> encoded_op_table(void); + +private: + std::map<uint32_t, std::string> _source_table; + std::map<uint32_t, std::set<uint32_t>> _op_table; +}; + } // namespace luci namespace std @@ -86,6 +117,7 @@ struct SerializedModelData final std::unordered_map<OpCode, uint32_t> _operator_codes; std::vector<flatbuffers::Offset<circle::Buffer>> _buffers; + CircleExportMetadata _metadata; // This is used for removing buffers with same values std::map<luci::CircleConst *, uint32_t> _cached_buffer_id; diff --git a/compiler/luci/export/src/TypeBridge.cpp b/compiler/luci/export/src/TypeBridge.cpp deleted file mode 100644 index 9ccd52376..000000000 --- a/compiler/luci/export/src/TypeBridge.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2020 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 "TypeBridge.h" - -#include "CircleExporterUtils.h" - -#include <luci/IR/CircleNodes.h> -#include <luci/IR/CircleNodeVisitor.h> -#include <luci/Service/CircleTypeInference.h> -#include <luci/Service/CircleShapeInference.h> - -#include <loco/Service/TypeInference.h> -#include <loco/Service/ShapeInference.h> - -namespace -{ - -/** - * @brief CopySelector will return condition of copy shape/type inference to node - */ -struct CopySelector final : public luci::CircleNodeVisitor<bool> -{ - // return false(don't copy) for nodes that provides shape/type from nature - bool visit(const luci::CircleInput *) final { return false; } - bool visit(const luci::CircleConst *) final { return false; } - - // default is copy attributes - bool visit(const luci::CircleNode *) { return true; } -}; - -} // namespace - -namespace luci -{ - -loco::TensorShape node_shape(CircleNode *node) -{ - loco::TensorShape shape; - - shape.rank(node->rank()); - for (uint32_t r = 0; r < node->rank(); ++r) - { - shape.dim(r) = loco::Dimension(node->dim(r).value()); - } - return shape; -} - -loco::DataType node_dtype(CircleNode *node) { return node->dtype(); } - -void copy_shape_dtype(loco::Graph *graph) -{ - /** - * @note We will iterate all the nodes in the graph to include dangle nodes - */ - auto nodes = graph->nodes(); - for (uint32_t n = 0; n < nodes->size(); ++n) - { - auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n)); - - CopySelector cs; - if (node->accept(&cs)) - { - // NOTE not all nodes have infered shape/dtype: multiple outs may not be - // visited when outputs are not used - // TODO fix shape inference traversal - // NOTE when loco supports multiple outputs in nature this issue should be - // resolved also - - if (loco::dtype_known(node)) - { - node->dtype(loco::dtype_get(node)); - } - - if (loco::shape_known(node)) - { - auto shape = loco::shape_get(node).as<loco::TensorShape>(); - node->rank(shape.rank()); - for (uint32_t r = 0; r < shape.rank(); ++r) - { - node->dim(r) = loco::Dimension(shape.dim(r).value()); - } - - // ShapeStatus should be update only when the status was UNDEFINED - if (node->shape_status() == ShapeStatus::UNDEFINED) - node->shape_status(ShapeStatus::VALID); - } - } - } -} - -} // namespace luci diff --git a/compiler/luci/import/CMakeLists.txt b/compiler/luci/import/CMakeLists.txt index 2ae00b837..642751ca6 100644 --- a/compiler/luci/import/CMakeLists.txt +++ b/compiler/luci/import/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(luci_import SHARED ${SOURCES}) target_include_directories(luci_import PRIVATE src) target_include_directories(luci_import PUBLIC include) target_link_libraries(luci_import PUBLIC luci_lang) +target_link_libraries(luci_import PUBLIC luci_profile) target_link_libraries(luci_import PUBLIC mio_circle) target_link_libraries(luci_import PRIVATE luci_env) target_link_libraries(luci_import PRIVATE luci_log) diff --git a/compiler/luci/import/include/luci/Import/CircleReader.h b/compiler/luci/import/include/luci/Import/CircleReader.h index 8e210dd77..b9697fb86 100644 --- a/compiler/luci/import/include/luci/Import/CircleReader.h +++ b/compiler/luci/import/include/luci/Import/CircleReader.h @@ -23,7 +23,6 @@ #include <luci/IR/AttrPadding.h> #include <luci/IR/CircleNode.h> #include <luci/IR/CircleQuantParam.h> -#include <luci/IR/CircleShapeSignature.h> #include <luci/IR/SparsityParam.h> #include <loco.h> @@ -64,6 +63,7 @@ private: using CircleTensors_t = std::vector<std::unique_ptr<circle::TensorT>>; using CircleOperators_t = std::vector<std::unique_ptr<circle::OperatorT>>; using CircleOperatorCodes_t = std::vector<std::unique_ptr<circle::OperatorCodeT>>; + using CircleMetadata_t = std::vector<std::unique_ptr<circle::MetadataT>>; using CircleSubGraphsPtr_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>; using CircleTensorsPtr_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>; @@ -79,6 +79,8 @@ public: const std::vector<int32_t> &inputs() const { return _current_subgraph->inputs; } const std::vector<int32_t> &outputs() const { return _current_subgraph->outputs; } const std::string &name() const { return _current_subgraph->name; } + const circle::DataFormat &data_format() const { return _current_subgraph->data_format; } + const CircleMetadata_t &metadata() const { return _model->metadata; } const CircleTensorsPtr_t *tensors_ptr() const { return _tensors_ptr; } diff --git a/compiler/luci/import/include/luci/Import/GraphBuilder.h b/compiler/luci/import/include/luci/Import/GraphBuilder.h index 548264dac..0db612652 100644 --- a/compiler/luci/import/include/luci/Import/GraphBuilder.h +++ b/compiler/luci/import/include/luci/Import/GraphBuilder.h @@ -33,7 +33,13 @@ class GraphBuilder : public GraphBuilderBase public: virtual ~GraphBuilder() = default; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; + // common validate method to check number of inputs and single output + bool validate(const ValidateArgs &args, size_t input_cnt) const + { + return (args.op.inputs.size() == input_cnt && args.op.outputs.size() == 1); + } + + CircleNode *build(const circle::OperatorT &op, GraphBuilderContext *context) const final; private: virtual CircleNode *build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderBase.h b/compiler/luci/import/include/luci/Import/GraphBuilderBase.h index a0cd008e0..ddd4445cd 100644 --- a/compiler/luci/import/include/luci/Import/GraphBuilderBase.h +++ b/compiler/luci/import/include/luci/Import/GraphBuilderBase.h @@ -19,6 +19,8 @@ #include "GraphBuilderContext.h" +#include <luci/IR/CircleNode.h> + #include <mio/circle/schema_generated.h> namespace luci @@ -38,7 +40,7 @@ struct GraphBuilderBase }; virtual bool validate(const ValidateArgs &) const = 0; - virtual void build(const circle::OperatorT &op, GraphBuilderContext *context) const = 0; + virtual CircleNode *build(const circle::OperatorT &op, GraphBuilderContext *context) const = 0; virtual ~GraphBuilderBase() = default; }; diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderContext.h b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h index 72e237abc..1673df43d 100644 --- a/compiler/luci/import/include/luci/Import/GraphBuilderContext.h +++ b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h @@ -71,7 +71,7 @@ class GraphBuilderContext public: GraphBuilderContext(loco::Graph *g, CircleReader *reader, IndexNodeFinder *nodefinder, IndexTensorOutputs *tensoroutputs) - : _g(g), _reader(reader), _indexnodefinder(nodefinder), _indextensoroutputs(tensoroutputs) + : _g(g), _reader(reader), _indexnodefinder(nodefinder), _indextensoroutputs(tensoroutputs) { // DO NOTHING } diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderMultiOutput.h b/compiler/luci/import/include/luci/Import/GraphBuilderMultiOutput.h new file mode 100644 index 000000000..6e8791b62 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/GraphBuilderMultiOutput.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 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 __LUCI_IMPORT_GRAPH_BUILDER_MULTI_OUTPUT_H__ +#define __LUCI_IMPORT_GRAPH_BUILDER_MULTI_OUTPUT_H__ + +#include "GraphBuilderContext.h" +#include "GraphBuilderBase.h" + +#include <mio/circle/schema_generated.h> + +namespace luci +{ + +/** + * @brief Base of general multiple outputs graph builder(e.g., CircleIfGraphBuilder) + */ +class GraphBuilderMultiOutput : public GraphBuilderBase +{ +public: + virtual ~GraphBuilderMultiOutput() = default; + + CircleNode *build(const circle::OperatorT &op, GraphBuilderContext *context) const final; + +protected: + struct BuildNodeArgs + { + BuildNodeArgs(const circle::OperatorT &o, GraphBuilderContext *c, + const std::vector<CircleNode *> &i) + : op(o), context(c), input_nodes(i) + { + } + + const circle::OperatorT &op; + GraphBuilderContext *context; + const std::vector<CircleNode *> &input_nodes; + }; + + struct BuildOutArgs + { + BuildOutArgs(CircleNode *nd, uint32_t n) : node(nd), index(n) {} + + CircleNode *node; + uint32_t index; + }; + +private: + virtual CircleNode *build_node(const BuildNodeArgs &) const = 0; + virtual CircleNode *build_out(const BuildOutArgs &) const = 0; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_GRAPH_BUILDER_MULTI_OUTPUT_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h index 28741064e..b084c7dbc 100644 --- a/compiler/luci/import/include/luci/Import/Nodes.h +++ b/compiler/luci/import/include/luci/Import/Nodes.h @@ -27,6 +27,7 @@ #include "Nodes/CircleBatchToSpaceND.h" #include "Nodes/CircleBCQFullyConnected.h" #include "Nodes/CircleBCQGather.h" +#include "Nodes/CircleBidirectionalSequenceLSTM.h" #include "Nodes/CircleCast.h" #include "Nodes/CircleCeil.h" #include "Nodes/CircleConcatenation.h" @@ -42,6 +43,7 @@ #include "Nodes/CircleEqual.h" #include "Nodes/CircleExp.h" #include "Nodes/CircleExpandDims.h" +#include "Nodes/CircleFakeQuant.h" #include "Nodes/CircleFill.h" #include "Nodes/CircleFloor.h" #include "Nodes/CircleFloorDiv.h" diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleBidirectionalSequenceLSTM.h b/compiler/luci/import/include/luci/Import/Nodes/CircleBidirectionalSequenceLSTM.h new file mode 100644 index 000000000..491517268 --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleBidirectionalSequenceLSTM.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_IMPORT_OP_CIRCLE_BIDIRECTIONALSEQUENCE_LSTM_H__ +#define __LUCI_IMPORT_OP_CIRCLE_BIDIRECTIONALSEQUENCE_LSTM_H__ + +#include "luci/Import/GraphBuilderMultiOutput.h" + +namespace luci +{ + +class CircleBidirectionalSequenceLSTMGraphBuilder : public GraphBuilderMultiOutput +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_BIDIRECTIONALSEQUENCE_LSTM_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h b/compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h index 65745be4b..f0d7e303d 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleCustom.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_CUSTOM_H__ #define __LUCI_IMPORT_OP_CIRCLE_CUSTOM_H__ -#include "luci/Import/GraphBuilder.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleCustomGraphBuilder : public GraphBuilderBase +class CircleCustomGraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFakeQuant.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFakeQuant.h new file mode 100644 index 000000000..9d9f7b07b --- /dev/null +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFakeQuant.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_IMPORT_OP_CIRCLE_FAKE_QUANT_H__ +#define __LUCI_IMPORT_OP_CIRCLE_FAKE_QUANT_H__ + +#include "luci/Import/GraphBuilder.h" + +namespace luci +{ + +class CircleFakeQuantGraphBuilder : public GraphBuilder +{ +public: + bool validate(const ValidateArgs &args) const final; + +private: + CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const final; +}; + +} // namespace luci + +#endif // __LUCI_IMPORT_OP_CIRCLE_FAKE_QUANT_H__ diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleIf.h b/compiler/luci/import/include/luci/Import/Nodes/CircleIf.h index 8faf09cae..94052f5be 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleIf.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleIf.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_IF_H__ #define __LUCI_IMPORT_OP_CIRCLE_IF_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleIfGraphBuilder : public GraphBuilderBase +class CircleIfGraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h index f193aae35..4e8388b3e 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV4.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V4_H__ #define __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V4_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleNonMaxSuppressionV4GraphBuilder : public GraphBuilderBase +class CircleNonMaxSuppressionV4GraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h index 62be0758e..4120a30eb 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleNonMaxSuppressionV5.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V5_H__ #define __LUCI_IMPORT_OP_CIRCLE_NON_MAX_SUPPRESSION_V5_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleNonMaxSuppressionV5GraphBuilder : public GraphBuilderBase +class CircleNonMaxSuppressionV5GraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h index 3395e40fd..5b45c9a9e 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSplit.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_SPLIT_H__ #define __LUCI_IMPORT_OP_CIRCLE_SPLIT_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleSplitGraphBuilder : public GraphBuilderBase +class CircleSplitGraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h index 3e53df362..de712f90c 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSplitV.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_SPLIT_V_H__ #define __LUCI_IMPORT_OP_CIRCLE_SPLIT_V_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleSplitVGraphBuilder : public GraphBuilderBase +class CircleSplitVGraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h index 8ec3f3311..b4ad97130 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTopKV2.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_TOPK_V2_H__ #define __LUCI_IMPORT_OP_CIRCLE_TOPK_V2_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleTopKV2GraphBuilder : public GraphBuilderBase +class CircleTopKV2GraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h b/compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h index ed5b5035d..40e75ec73 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleUnique.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_UNIQUE_H__ #define __LUCI_IMPORT_OP_CIRCLE_UNIQUE_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleUniqueGraphBuilder : public GraphBuilderBase +class CircleUniqueGraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h b/compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h index f1a21de22..0b623655f 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleUnpack.h @@ -17,17 +17,19 @@ #ifndef __LUCI_IMPORT_OP_CIRCLE_UNPACK_H__ #define __LUCI_IMPORT_OP_CIRCLE_UNPACK_H__ -#include "luci/Import/GraphBuilderBase.h" +#include "luci/Import/GraphBuilderMultiOutput.h" namespace luci { -class CircleUnpackGraphBuilder : public GraphBuilderBase +class CircleUnpackGraphBuilder : public GraphBuilderMultiOutput { public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; +private: + CircleNode *build_node(const BuildNodeArgs &) const final; + CircleNode *build_out(const BuildOutArgs &) const final; }; } // namespace luci diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h b/compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h index 68c56b3c6..69d23f823 100644 --- a/compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h +++ b/compiler/luci/import/include/luci/Import/Nodes/CircleWhile.h @@ -27,7 +27,7 @@ class CircleWhileGraphBuilder : public GraphBuilderBase public: bool validate(const ValidateArgs &args) const final; - void build(const circle::OperatorT &op, GraphBuilderContext *context) const final; + CircleNode *build(const circle::OperatorT &op, GraphBuilderContext *context) const final; }; } // namespace luci diff --git a/compiler/luci/import/src/CircleImportMetadata.cpp b/compiler/luci/import/src/CircleImportMetadata.cpp new file mode 100644 index 000000000..f68f3301a --- /dev/null +++ b/compiler/luci/import/src/CircleImportMetadata.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2021 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 "CircleImportMetadata.h" + +#include <vector> + +namespace +{ + +uint32_t read_u32(const std::vector<uint8_t> &buffer, uint32_t idx) +{ + uint32_t val = 0; + val += (buffer.at(idx + 0) << 0 * 8); + val += (buffer.at(idx + 1) << 1 * 8); + val += (buffer.at(idx + 2) << 2 * 8); + val += (buffer.at(idx + 3) << 3 * 8); + return val; +} + +} // namespace + +namespace +{ + +// 'source_table' is decoded to std::map<uint32_t, std::string> format. +const std::map<uint32_t, std::string> +decoded_source_table(const std::vector<uint8_t> &source_table_data) +{ + std::map<uint32_t, std::string> source_id_name_map; + uint32_t idx = 0; + + if (source_table_data.size() < 4) + throw std::runtime_error("Source table decode error : invalid entry number"); + + uint32_t entry_number = read_u32(source_table_data, idx); + idx += sizeof(uint32_t); + + while (idx < source_table_data.size()) + { + if (idx + 2 * sizeof(uint32_t) > source_table_data.size()) + throw std::runtime_error("Source table decode error : invalid entry item"); + + uint32_t id = read_u32(source_table_data, idx); + idx += sizeof(uint32_t); + + uint32_t length = read_u32(source_table_data, idx); + idx += sizeof(uint32_t); + + if (idx + sizeof(char) * length > source_table_data.size()) + throw std::runtime_error("Source table decode error : invalid entry data"); + + // The last character of name is '\0'. + // However, as std::string do not use '\0' for finding the end of string, + // we ignore the character and do not include it in the string. + std::string origin_name; + for (uint32_t j = 0; j < length - 1; ++j) + origin_name += source_table_data.at(idx + j); + assert(source_table_data.at(idx + length - 1) == '\0'); + idx += sizeof(char) * length; + + if (source_id_name_map.insert({id, origin_name}).second == false) + throw std::runtime_error("Source table decode error : duplicated origin ID"); + } + + if (idx != source_table_data.size()) + throw std::runtime_error("Source table decode error : data size invalid"); + + if (source_id_name_map.size() != entry_number) + throw std::runtime_error("Source table decode error : result size mismatch"); + + return source_id_name_map; +} + +// 'op_table' is decoded to std::map<uint32_t, std::set<uint32_t>> format. +const std::map<uint32_t, std::set<uint32_t>> +decoded_op_table(const std::vector<uint8_t> &op_table_data) +{ + std::map<uint32_t, std::set<uint32_t>> node_source_ids_map; + uint32_t idx = 0; + + if (op_table_data.size() < 4) + throw std::runtime_error("Op table decode error : invalid entry number"); + + uint32_t entry_number = read_u32(op_table_data, idx); + idx += sizeof(uint32_t); + + while (idx < op_table_data.size()) + { + if (idx + 2 * sizeof(uint32_t) > op_table_data.size()) + throw std::runtime_error("Op table decode error : invalid entry item"); + + uint32_t id = read_u32(op_table_data, idx); + idx += sizeof(uint32_t); + + uint32_t node_num = read_u32(op_table_data, idx); + idx += sizeof(uint32_t); + + if (idx + sizeof(uint32_t) * node_num > op_table_data.size()) + throw std::runtime_error("Source table decode error : invalid entry data"); + + std::set<uint32_t> source_ids; + for (uint32_t j = 0; j < node_num; ++j) + { + uint32_t origin = read_u32(op_table_data, idx); + idx += sizeof(uint32_t); + + source_ids.insert(origin); + } + + if (node_source_ids_map.insert({id, source_ids}).second == false) + throw std::runtime_error("Op table decode error : duplicated origin ID"); + } + + if (idx != op_table_data.size()) + throw std::runtime_error("Op table decode error : data size invalid"); + + if (node_source_ids_map.size() != entry_number) + throw std::runtime_error("Op table decode error : entry number invalid"); + + return node_source_ids_map; +} + +} // namespace + +namespace luci +{ + +CircleImportMetadata::CircleImportMetadata(const luci::CircleReader &reader) +{ + const auto &metadata = reader.metadata(); + for (uint32_t i = 0; i < metadata.size(); ++i) + { + const circle::MetadataT &meta = *metadata[i]; + + assert(meta.buffer < reader.buffers().size()); + const std::vector<uint8_t> &buffer = reader.buffers()[meta.buffer]->data; + + if (meta.name.compare("ONE_op_table") == 0) + _op_table = decoded_op_table(buffer); + else if (meta.name.compare("ONE_source_table") == 0) + _source_table = decoded_source_table(buffer); + } +} + +const OriginTable CircleImportMetadata::origin_table(void) +{ + OriginTable origin_table; + + if (_op_table.size() > 0 && _source_table.size() > 0) + { + for (auto &kv : _op_table) + { + const auto node_id = kv.first; + const auto &source_ids = kv.second; + + std::vector<std::shared_ptr<CircleNodeOrigin>> origins; + for (auto source_id : source_ids) + { + const auto source_name = _source_table.at(source_id); + origins.push_back(single_origin(source_id, source_name)); + } + + auto origin = composite_origin(origins); + origin_table.emplace(node_id, origin); + } + } + + return origin_table; +} + +} // namespace luci diff --git a/compiler/luci/import/src/CircleImportMetadata.h b/compiler/luci/import/src/CircleImportMetadata.h new file mode 100644 index 000000000..80176db94 --- /dev/null +++ b/compiler/luci/import/src/CircleImportMetadata.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 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 __LUCI_CIRCLE_IMPORT_METADATA_H__ +#define __LUCI_CIRCLE_IMPORT_METADATA_H__ + +#include "luci/Import/CircleReader.h" + +#include <luci/Profile/CircleNodeOrigin.h> + +#include <map> +#include <set> +#include <string> + +namespace luci +{ + +using OriginTable = std::map<uint32_t, std::shared_ptr<CircleNodeOrigin>>; + +class CircleImportMetadata +{ +public: + CircleImportMetadata() = delete; + + CircleImportMetadata(const luci::CircleReader &reader); + +public: + /** + * @brief Create origin table using _source_table and _op_table in CircleImportMetadata + * @note For creating origin table, both _op_table and _source_table should exist. + * If one of them does not exist, empty table is returned. + */ + const OriginTable origin_table(void); + +private: + // Decoded metadata is stored + std::map<uint32_t, std::string> _source_table; + std::map<uint32_t, std::set<uint32_t>> _op_table; +}; + +} // namespace luci + +#endif // __LUCI_CIRCLE_IMPORT_METADATA_H__ diff --git a/compiler/luci/import/src/CircleReader.cpp b/compiler/luci/import/src/CircleReader.cpp index b33c920b1..861c1bbe3 100644 --- a/compiler/luci/import/src/CircleReader.cpp +++ b/compiler/luci/import/src/CircleReader.cpp @@ -190,19 +190,19 @@ luci_sparse_index_vector(const circle::SparseIndexVectorUnion &sparse_index_vect case circle::SparseIndexVector_Int32Vector: { const auto const_vec_ptr = - static_cast<const void *>(&(sparse_index_vector.AsInt32Vector()->values)); + static_cast<const void *>(&(sparse_index_vector.AsInt32Vector()->values)); return SparseIndexVector{SparseIndexVectorType::I32, const_vec_ptr}; } case circle::SparseIndexVector_Uint16Vector: { const auto const_vec_ptr = - static_cast<const void *>(&(sparse_index_vector.AsUint16Vector()->values)); + static_cast<const void *>(&(sparse_index_vector.AsUint16Vector()->values)); return SparseIndexVector{SparseIndexVectorType::U16, const_vec_ptr}; } case circle::SparseIndexVector_Uint8Vector: { const auto const_vec_ptr = - static_cast<const void *>(&(sparse_index_vector.AsUint8Vector()->values)); + static_cast<const void *>(&(sparse_index_vector.AsUint8Vector()->values)); return SparseIndexVector{SparseIndexVectorType::U8, const_vec_ptr}; } default: @@ -262,15 +262,19 @@ void copy_tensor_attributes(const circle::TensorT &tensor, CircleNode *node) node->name(tensor_name(tensor)); node->dtype(luci_datatype(tensor.type)); + assert(tensor.shape_signature.size() == 0 || + tensor.shape_signature.size() == tensor.shape.size()); + std::vector<int32_t> dims = tensor.shape; // in NHWC node->rank(dims.size()); for (uint32_t r = 0; r < dims.size(); ++r) { - node->dim(r) = loco::Dimension(dims[r]); + if (tensor.shape_signature.size() > 0 && tensor.shape_signature.at(r) == -1) + node->dim(r).unset(); + else + node->dim(r).set(dims[r]); } - node->shape_signature(tensor.shape_signature); - const auto *quantization = tensor.quantization.get(); if (quantization != nullptr) { diff --git a/compiler/luci/import/src/GraphBuilder.cpp b/compiler/luci/import/src/GraphBuilder.cpp index 80a9f986a..356501c2f 100644 --- a/compiler/luci/import/src/GraphBuilder.cpp +++ b/compiler/luci/import/src/GraphBuilder.cpp @@ -21,7 +21,7 @@ namespace luci { -void GraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const +CircleNode *GraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const { LOGGER(l); @@ -47,7 +47,11 @@ void GraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *conte else { // If there is no tensor, insert CircleOutputExclude. - input_nodes.push_back(context->graph()->nodes()->create<luci::CircleOutputExclude>()); + auto *node = context->graph()->nodes()->create<luci::CircleOutputExclude>(); + // CircleOutputExclude doesn't need a type, but since all nodes must have a type, + // a dummy type is inserted. + node->dtype(loco::DataType::FLOAT32); + input_nodes.push_back(node); } } @@ -73,6 +77,8 @@ void GraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *conte { context->nodefinder()->enroll(outputs[0], node); } + + return node; } } // namespace luci diff --git a/compiler/luci/import/src/GraphBuilderMultiOutput.cpp b/compiler/luci/import/src/GraphBuilderMultiOutput.cpp new file mode 100644 index 000000000..9b42e997e --- /dev/null +++ b/compiler/luci/import/src/GraphBuilderMultiOutput.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 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/Import/GraphBuilderMultiOutput.h" + +#include <luci/Log.h> + +namespace luci +{ + +CircleNode *GraphBuilderMultiOutput::build(const circle::OperatorT &op, + GraphBuilderContext *context) const +{ + LOGGER(l); + + assert(context != nullptr); + + const std::vector<int32_t> &inputs = op.inputs; + const std::vector<int32_t> &outputs = op.outputs; + const auto &tensors = context->reader()->tensors(); + const auto &opcodes = context->reader()->opcodes(); + auto tensors_ptr = context->reader()->tensors_ptr(); + assert(tensors_ptr != nullptr); + + std::vector<CircleNode *> input_nodes; + for (const int32_t input_tensor_index : inputs) + { + if (input_tensor_index >= 0) + { + auto input = context->nodefinder()->node(input_tensor_index); + if (input == nullptr) + INFO(l) << "[luci] Warning: input node is null " << input_tensor_index << std::endl; + input_nodes.push_back(input); + } + else + { + // If there is no tensor, insert CircleOutputExclude. + auto *node = context->graph()->nodes()->create<luci::CircleOutputExclude>(); + // CircleOutputExclude doesn't need a type, but since all nodes must have a type, + // a dummy type is inserted. + node->dtype(loco::DataType::FLOAT32); + input_nodes.push_back(node); + } + } + + BuildNodeArgs bna(op, context, input_nodes); + auto *node = build_node(bna); + + uint32_t output_count = outputs.size(); + assert(output_count > 0); + { + // Let's use attributes from output 0 for this node + const circle::TensorT &output_tensor = *tensors[outputs[0]]; + node->name(tensor_name(output_tensor)); + node->dtype(luci_datatype(output_tensor.type)); + + // mark operator version + node->op_version(opcodes[op.opcode_index].get()->version); + + // NOTE We don't set quantization for multiple output nodes but to virtual outputs + } + + // Create virtual outputs of Virtual Output node(s) + for (uint32_t n = 0; n < output_count; ++n) + { + const circle::TensorT &output_tensor = *tensors[outputs[n]]; + + BuildOutArgs boa(node, n); + auto *nodeout = build_out(boa); + + copy_tensor_attributes(output_tensor, nodeout); + // mark shape_status + if (tensors_ptr->Get(outputs[n])->shape() == nullptr) + nodeout->shape_status(ShapeStatus::NOSHAPE); + else + nodeout->shape_status(ShapeStatus::VALID); + + context->nodefinder()->enroll(outputs[n], nodeout); + } + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp index d598d30f4..7f98aab78 100644 --- a/compiler/luci/import/src/GraphBuilderRegistry.cpp +++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp @@ -37,6 +37,7 @@ GraphBuilderRegistry::GraphBuilderRegistry() CIRCLE_NODE(BATCH_TO_SPACE_ND, CircleBatchToSpaceNDGraphBuilder); // 37 CIRCLE_NODE(BCQ_FULLY_CONNECTED, CircleBCQFullyConnectedGraphBuilder); // 253 CIRCLE_NODE(BCQ_GATHER, CircleBCQGatherGraphBuilder); // 252 + CIRCLE_NODE(BIDIRECTIONAL_SEQUENCE_LSTM, CircleBidirectionalSequenceLSTMGraphBuilder); // 52 CIRCLE_NODE(CAST, CircleCastGraphBuilder); // 53 CIRCLE_NODE(CEIL, CircleCeilGraphBuilder); // 104 CIRCLE_NODE(CUSTOM, CircleCustomGraphBuilder); // 32 @@ -51,6 +52,7 @@ GraphBuilderRegistry::GraphBuilderRegistry() CIRCLE_NODE(EQUAL, CircleEqualGraphBuilder); // 71 CIRCLE_NODE(EXP, CircleExpGraphBuilder); // 47 CIRCLE_NODE(EXPAND_DIMS, CircleExpandDimsGraphBuilder); // 70 + CIRCLE_NODE(FAKE_QUANT, CircleFakeQuantGraphBuilder); // 80 CIRCLE_NODE(FILL, CircleFillGraphBuilder); // 94 CIRCLE_NODE(FLOOR, CircleFloorGraphBuilder); // 8 CIRCLE_NODE(FLOOR_DIV, CircleFloorDivGraphBuilder); // 90 @@ -155,9 +157,7 @@ GraphBuilderRegistry::GraphBuilderRegistry() // BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN = 35, // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN = 46, // BuiltinOperator_DELEGATE = 51, - // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM = 52, // BuiltinOperator_ARG_MAX = 56, - // BuiltinOperator_FAKE_QUANT = 80, // BuiltinOperator_QUANTIZE = 114, // BuiltinOperator_HARD_SWISH = 117, // BuiltinOperator_DENSIFY = 124, diff --git a/compiler/luci/import/src/Importer.cpp b/compiler/luci/import/src/Importer.cpp index ab89f3587..193afffcb 100644 --- a/compiler/luci/import/src/Importer.cpp +++ b/compiler/luci/import/src/Importer.cpp @@ -15,6 +15,7 @@ */ #include "luci/Importer.h" +#include "CircleImportMetadata.h" #include "PostImport.h" #include "luci/Import/GraphBuilder.h" @@ -25,6 +26,8 @@ #include <luci/IR/Module.h> #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeID.h> +#include <luci/Profile/CircleNodeOrigin.h> #include <luci/Log.h> #include <luci/LogHelper.h> @@ -50,6 +53,7 @@ void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &r const auto &tensors = reader.tensors(); auto tensors_ptr = reader.tensors_ptr(); assert(tensors_ptr != nullptr); + auto circle_metadata = std::make_unique<luci::CircleImportMetadata>(reader); // build a cache to identify if a tensor is output of an operator // if this is set, we should not create a CircleConst for this tensor @@ -96,12 +100,20 @@ void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &r // Data type graph_input->dtype(input_node->dtype()); + assert(tensor.shape_signature.size() == 0 || + tensor.shape_signature.size() == tensor.shape.size()); + // Shape of GraphInput auto input_shape = std::make_unique<loco::TensorShape>(); const std::vector<int32_t> &input_dims = tensor.shape; // in NHWC input_shape->rank(input_dims.size()); for (uint32_t r = 0; r < input_dims.size(); ++r) - input_shape->dim(r) = loco::Dimension(input_dims[r]); + { + if (tensor.shape_signature.size() > 0 && tensor.shape_signature.at(r) == -1) + input_shape->dim(r).unset(); + else + input_shape->dim(r).set(input_dims[r]); + } graph_input->shape(std::move(input_shape)); } @@ -117,6 +129,7 @@ void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &r // Note that operators in model are stored in execution order. This means that when importing // an operator, its input operators have already been imported. We exploit this fact to set up // node's inputs right after creating the node. + auto origin_table = circle_metadata->origin_table(); for (uint32_t i = 0; i < operators.size(); ++i) { const circle::OperatorT &op = *operators[i]; @@ -130,7 +143,12 @@ void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &r throw oops::UserExn("Invalid operator", reader.opcode_name(op)); } - builder->build(op, &gb_context); + auto built_op = builder->build(op, &gb_context); + set_node_id(built_op, i); + if (origin_table.find(i) != origin_table.end()) + add_origin(built_op, origin_table.at(i)); + else + add_origin(built_op, luci::single_origin(i, built_op->name())); } else { @@ -169,19 +187,28 @@ void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &r // set the graph output name and node object auto graph_output = graph->outputs()->create(); std::string tname = luci::tensor_name(tensor); - graph_output->name("output_" + tname); + assert(tname.length() > 0); + graph_output->name(tname); luci::copy_tensor_attributes(tensor, output_node); // Set GraphInputOutputIndex for graph output_node->index(graph_output->index()); + assert(tensor.shape_signature.size() == 0 || + tensor.shape_signature.size() == tensor.shape.size()); + // Shape of Output auto output_shape = std::make_unique<loco::TensorShape>(); const std::vector<int32_t> &output_dims = tensor.shape; // in NHWC output_shape->rank(output_dims.size()); for (uint32_t r = 0; r < output_dims.size(); ++r) - output_shape->dim(r) = loco::Dimension(output_dims[r]); + { + if (tensor.shape_signature.size() > 0 && tensor.shape_signature.at(r) == -1) + output_shape->dim(r).unset(); + else + output_shape->dim(r).set(output_dims[r]); + } graph_output->shape(std::move(output_shape)); // Data type diff --git a/compiler/luci/import/src/Nodes/CircleAbs.cpp b/compiler/luci/import/src/Nodes/CircleAbs.cpp index 3556dc7fa..2a1601a21 100644 --- a/compiler/luci/import/src/Nodes/CircleAbs.cpp +++ b/compiler/luci/import/src/Nodes/CircleAbs.cpp @@ -24,11 +24,8 @@ namespace luci { bool CircleAbsGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - // TODO Support type check - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleAbsGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleAdd.cpp b/compiler/luci/import/src/Nodes/CircleAdd.cpp index b767d4af2..94cbdf081 100644 --- a/compiler/luci/import/src/Nodes/CircleAdd.cpp +++ b/compiler/luci/import/src/Nodes/CircleAdd.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleAddGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleAddGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleArgMax.cpp b/compiler/luci/import/src/Nodes/CircleArgMax.cpp index 10e8516f4..fd8a84289 100644 --- a/compiler/luci/import/src/Nodes/CircleArgMax.cpp +++ b/compiler/luci/import/src/Nodes/CircleArgMax.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleArgMaxGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleArgMaxGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleArgMin.cpp b/compiler/luci/import/src/Nodes/CircleArgMin.cpp index 5ff534dbb..63ca8db03 100644 --- a/compiler/luci/import/src/Nodes/CircleArgMin.cpp +++ b/compiler/luci/import/src/Nodes/CircleArgMin.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleArgMinGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleArgMinGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp index ad011f71f..a351cf5e7 100644 --- a/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp +++ b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp @@ -23,10 +23,7 @@ namespace luci bool CircleAveragePool2DGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleAveragePool2DGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp b/compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp index 16ecebd5c..4c86399ce 100644 --- a/compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp +++ b/compiler/luci/import/src/Nodes/CircleBCQFullyConnected.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleBCQFullyConnectedGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 5) - return false; - - return true; + return GraphBuilder::validate(args, 5); } CircleNode *CircleBCQFullyConnectedGraphBuilder::build_node(const circle::OperatorT &op, @@ -43,15 +40,6 @@ CircleNode *CircleBCQFullyConnectedGraphBuilder::build_node(const circle::Operat node->bias(inputs.at(3)); node->weights_clusters(inputs.at(4)); - // TODO Find and move to appropriate place for setting optional input - if (auto bias = dynamic_cast<luci::CircleOutputExclude *>(node->bias())) - { - // bias is not used for type inference, but node itself should have a type - bias->dtype(loco::DataType::FLOAT32); - - // bias is not used for shape inference - } - const auto *options = op.builtin_options.AsBCQFullyConnectedOptions(); node->weights_hidden_size(options->weights_hidden_size); node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); diff --git a/compiler/luci/import/src/Nodes/CircleBCQGather.cpp b/compiler/luci/import/src/Nodes/CircleBCQGather.cpp index 464f1ac18..ee1358197 100644 --- a/compiler/luci/import/src/Nodes/CircleBCQGather.cpp +++ b/compiler/luci/import/src/Nodes/CircleBCQGather.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleBCQGatherGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 4) - return false; - - return true; + return GraphBuilder::validate(args, 4); } CircleNode *CircleBCQGatherGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp b/compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp index 330775691..390719061 100644 --- a/compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp +++ b/compiler/luci/import/src/Nodes/CircleBatchMatMul.cpp @@ -23,10 +23,7 @@ namespace luci bool CircleBatchMatMulGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleBatchMatMulGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleBidirectionalSequenceLSTM.cpp b/compiler/luci/import/src/Nodes/CircleBidirectionalSequenceLSTM.cpp new file mode 100644 index 000000000..f8bdcff72 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleBidirectionalSequenceLSTM.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 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/Import/Nodes/CircleBidirectionalSequenceLSTM.h" + +#include <luci/IR/Nodes/CircleBidirectionalSequenceLSTM.h> +#include <luci/IR/Nodes/CircleBidirectionalSequenceLSTMOut.h> + +#include <loco.h> + +namespace luci +{ + +bool CircleBidirectionalSequenceLSTMGraphBuilder::validate(const ValidateArgs &args) const +{ + if (args.op.inputs.size() != 48) + return false; + if (args.op.outputs.size() != 2) + return false; + + return true; +} + +CircleNode *CircleBidirectionalSequenceLSTMGraphBuilder::build_node(const BuildNodeArgs &bna) const +{ + auto *node = bna.context->graph()->nodes()->create<CircleBidirectionalSequenceLSTM>(); + auto &inputs = bna.input_nodes; + node->input(inputs.at(0)); + node->fw_input_to_input_weights(inputs.at(1)); // Optional + node->fw_input_to_cell_weights(inputs.at(2)); + node->fw_input_to_forget_weights(inputs.at(3)); + node->fw_input_to_output_weights(inputs.at(4)); + node->fw_recurrent_to_input_weights(inputs.at(5)); // Optional + node->fw_recurrent_to_cell_weights(inputs.at(6)); + node->fw_recurrent_to_forget_weights(inputs.at(7)); + node->fw_recurrent_to_output_weights(inputs.at(8)); + node->fw_cell_to_input_weights(inputs.at(9)); // Optional + node->fw_cell_to_forget_weights(inputs.at(10)); // Optional + node->fw_cell_to_output_weights(inputs.at(11)); // Optional + node->fw_input_gate_bias(inputs.at(12)); // Optional + node->fw_forget_gate_bias(inputs.at(13)); + node->fw_cell_gate_bias(inputs.at(14)); + node->fw_output_gate_bias(inputs.at(15)); + node->fw_projection_weights(inputs.at(16)); // Optional + node->fw_projection_bias(inputs.at(17)); // Optional + node->bw_input_to_input_weights(inputs.at(18)); // Optional + node->bw_input_to_cell_weights(inputs.at(19)); + node->bw_input_to_forget_weights(inputs.at(20)); + node->bw_input_to_output_weights(inputs.at(21)); + node->bw_recurrent_to_input_weights(inputs.at(22)); // Optional + node->bw_recurrent_to_cell_weights(inputs.at(23)); + node->bw_recurrent_to_forget_weights(inputs.at(24)); + node->bw_recurrent_to_output_weights(inputs.at(25)); + node->bw_cell_to_input_weights(inputs.at(26)); // Optional + node->bw_cell_to_forget_weights(inputs.at(27)); // Optional + node->bw_cell_to_output_weights(inputs.at(28)); // Optional + node->bw_input_gate_bias(inputs.at(29)); // Optional + node->bw_forget_gate_bias(inputs.at(30)); + node->bw_cell_gate_bias(inputs.at(31)); + node->bw_output_gate_bias(inputs.at(32)); + node->bw_projection_weights(inputs.at(33)); // Optional + node->bw_projection_bias(inputs.at(34)); // Optional + node->fw_activation_state(inputs.at(35)); + node->fw_cell_state(inputs.at(36)); + node->bw_activation_state(inputs.at(37)); + node->bw_cell_state(inputs.at(38)); + + node->auxillary_input(inputs.at(39)); // Optional + node->fw_auxillary_input_to_input_weights(inputs.at(40)); // Optional + node->fw_auxillary_input_to_forget_weights(inputs.at(41)); // Optional + node->fw_auxillary_input_to_cell_weights(inputs.at(42)); // Optional + node->fw_auxillary_input_to_output_weights(inputs.at(43)); // Optional + node->bw_auxillary_input_to_input_weights(inputs.at(44)); // Optional + node->bw_auxillary_input_to_forget_weights(inputs.at(45)); // Optional + node->bw_auxillary_input_to_cell_weights(inputs.at(46)); // Optional + node->bw_auxillary_input_to_output_weights(inputs.at(47)); // Optional + + const auto *options = bna.op.builtin_options.AsBidirectionalSequenceLSTMOptions(); + node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); + node->cell_clip(options->cell_clip); + node->proj_clip(options->proj_clip); + node->merge_outputs(options->merge_outputs); + node->time_major(options->time_major); + node->asymmetric_quantize_inputs(options->asymmetric_quantize_inputs); + + return node; +} + +CircleNode *CircleBidirectionalSequenceLSTMGraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleBidirectionalSequenceLSTMOut>(); + + nodeout->input(boa.node); + nodeout->index(boa.index); + + return nodeout; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleCast.cpp b/compiler/luci/import/src/Nodes/CircleCast.cpp index 7bdb63044..3e8c08bfa 100644 --- a/compiler/luci/import/src/Nodes/CircleCast.cpp +++ b/compiler/luci/import/src/Nodes/CircleCast.cpp @@ -30,14 +30,13 @@ bool CircleCastGraphBuilder::validate(const ValidateArgs &args) const { LOGGER(l); + if (!GraphBuilder::validate(args, 1)) + return false; + auto settings = luci::UserSettings::settings(); const auto &inputs = args.op.inputs; const auto &outputs = args.op.outputs; - if (inputs.size() != 1) - return false; - if (outputs.size() != 1) - return false; // NOTE real models do have type mismatch const auto *options = args.op.builtin_options.AsCastOptions(); diff --git a/compiler/luci/import/src/Nodes/CircleCeil.cpp b/compiler/luci/import/src/Nodes/CircleCeil.cpp index 2e1aaa295..d439f41cd 100644 --- a/compiler/luci/import/src/Nodes/CircleCeil.cpp +++ b/compiler/luci/import/src/Nodes/CircleCeil.cpp @@ -25,16 +25,8 @@ namespace luci bool CircleCeilGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 1) - return false; - if (outputs.size() != 1) - return false; - // TODO dtype check - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleCeilGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleConv2D.cpp b/compiler/luci/import/src/Nodes/CircleConv2D.cpp index 9516ef16a..8cbecdc00 100644 --- a/compiler/luci/import/src/Nodes/CircleConv2D.cpp +++ b/compiler/luci/import/src/Nodes/CircleConv2D.cpp @@ -28,10 +28,7 @@ namespace luci bool CircleConv2DGraphBuilder::validate(const ValidateArgs &args) const { // Circle Conv2D may not have a bias but we won't support this - if (args.op.inputs.size() != 3) - return false; - - return true; + return GraphBuilder::validate(args, 3); } CircleNode *CircleConv2DGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleCos.cpp b/compiler/luci/import/src/Nodes/CircleCos.cpp index 27d60c62c..9705202ee 100644 --- a/compiler/luci/import/src/Nodes/CircleCos.cpp +++ b/compiler/luci/import/src/Nodes/CircleCos.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleCosGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleCosGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleCustom.cpp b/compiler/luci/import/src/Nodes/CircleCustom.cpp index d541ee87b..01ac3e2a0 100644 --- a/compiler/luci/import/src/Nodes/CircleCustom.cpp +++ b/compiler/luci/import/src/Nodes/CircleCustom.cpp @@ -27,62 +27,39 @@ bool CircleCustomGraphBuilder::validate(const ValidateArgs &) const return true; } -void CircleCustomGraphBuilder::build(const circle::OperatorT &op, - GraphBuilderContext *context) const +CircleNode *CircleCustomGraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); + uint32_t input_count = bna.op.inputs.size(); + uint32_t output_count = bna.op.outputs.size(); - auto graph = context->graph(); + auto *node = bna.context->graph()->nodes()->create<CircleCustom>(input_count, output_count); - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); + for (uint32_t idx = 0; idx < input_count; ++idx) + { + node->inputs(idx, bna.input_nodes[idx]); + } - // Create CircleCustom - const auto &opcodes = context->reader()->opcodes(); - const uint32_t opcode_index = op.opcode_index; + const auto &opcodes = bna.context->reader()->opcodes(); + const uint32_t opcode_index = bna.op.opcode_index; const circle::OperatorCodeT &opcode = *opcodes[opcode_index]; - auto *node = graph->nodes()->create<CircleCustom>(inputs.size()); - uint32_t input_idx = 0; - for (const int32_t input_tensor_index : inputs) - { - node->inputs(input_idx++, context->nodefinder()->node(input_tensor_index)); - } - node->custom_options(std::vector<uint8_t>{op.custom_options.begin(), op.custom_options.end()}); + node->custom_options( + std::vector<uint8_t>{bna.op.custom_options.begin(), bna.op.custom_options.end()}); node->custom_code(opcode.custom_code); - // Operator version of custom is always 1, so do nothing - uint32_t output_count = outputs.size(); + // NOTE Operator version of custom is always 1 - assert(output_count > 0); - { - // Let's use attributes from output 0 for this node - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->dtype(luci_datatype(output_tensor.type)); - } - - // Create virtual outputs of Custom - for (uint32_t n = 0; n < output_count; ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; + return node; +} - auto *nodeout = graph->nodes()->create<CircleCustomOut>(); - copy_tensor_attributes(output_tensor, nodeout); - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); +CircleNode *CircleCustomGraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleCustomOut>(); - nodeout->input(node); - nodeout->index(n); + nodeout->input(boa.node); + nodeout->index(boa.index); - context->nodefinder()->enroll(outputs[n], nodeout); - } + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp b/compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp index 49d31bb99..49eb30a83 100644 --- a/compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp +++ b/compiler/luci/import/src/Nodes/CircleDepthToSpace.cpp @@ -27,17 +27,13 @@ namespace luci bool CircleDepthToSpaceGraphBuilder::validate(const ValidateArgs &args) const { + if (!GraphBuilder::validate(args, 1)) + return false; + const auto &inputs = args.op.inputs; const auto &outputs = args.op.outputs; const auto *options = args.op.builtin_options.AsDepthToSpaceOptions(); - - if (inputs.size() != 1) - return false; - - if (outputs.size() != 1) - return false; - const auto &tensors = args.reader.tensors(); if (tensors[outputs[0]]->type != tensors[inputs.at(0)]->type) diff --git a/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp index 53f85f2f5..727487c6a 100644 --- a/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp +++ b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp @@ -32,6 +32,32 @@ bool CircleDepthwiseConv2DGraphBuilder::validate(const ValidateArgs &args) const if (args.op.outputs.size() != 1) return false; + const auto &tensors = args.reader.tensors(); + + // input shape + const auto &input = tensors.at(args.op.inputs.at(0)); + const auto &input_shape = input->shape; + + // input shape must be rank 4 + if (input_shape.size() != 4) + return false; + + // filter shape + const auto &filter = tensors.at(args.op.inputs.at(1)); + const auto &filter_shape = filter->shape; + + // filter shape must be rank 4 + if (filter_shape.size() != 4) + return false; + + // multiplier + const auto *options = args.op.builtin_options.AsDepthwiseConv2DOptions(); + const auto &multiplier = options->depth_multiplier; + + // filter represents as [1, H, W, C*M] where M is multiplier. + if (filter_shape.at(3) != input_shape.at(3) * multiplier) + return false; + return true; } diff --git a/compiler/luci/import/src/Nodes/CircleDequantize.cpp b/compiler/luci/import/src/Nodes/CircleDequantize.cpp index 1936da97c..3db546bd0 100644 --- a/compiler/luci/import/src/Nodes/CircleDequantize.cpp +++ b/compiler/luci/import/src/Nodes/CircleDequantize.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleDequantizeGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleDequantizeGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleDiv.cpp b/compiler/luci/import/src/Nodes/CircleDiv.cpp index 615c224d7..7ea1afd95 100644 --- a/compiler/luci/import/src/Nodes/CircleDiv.cpp +++ b/compiler/luci/import/src/Nodes/CircleDiv.cpp @@ -23,13 +23,7 @@ namespace luci bool CircleDivGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleDivGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleElu.cpp b/compiler/luci/import/src/Nodes/CircleElu.cpp index 919e95ee4..461da9517 100644 --- a/compiler/luci/import/src/Nodes/CircleElu.cpp +++ b/compiler/luci/import/src/Nodes/CircleElu.cpp @@ -25,14 +25,11 @@ namespace luci bool CircleEluGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; - if (outputs.size() != 1) - return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor = tensors.at(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleEqual.cpp b/compiler/luci/import/src/Nodes/CircleEqual.cpp index 1db33b8ac..4909692b4 100644 --- a/compiler/luci/import/src/Nodes/CircleEqual.cpp +++ b/compiler/luci/import/src/Nodes/CircleEqual.cpp @@ -25,13 +25,10 @@ namespace luci bool CircleEqualGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - - if (inputs.size() != 2) - { + if (!GraphBuilder::validate(args, 2)) return false; - } + const auto &inputs = args.op.inputs; const auto &tensors = args.reader.tensors(); return tensors[inputs.at(0)]->type == tensors[inputs.at(1)]->type; diff --git a/compiler/luci/import/src/Nodes/CircleExp.cpp b/compiler/luci/import/src/Nodes/CircleExp.cpp index 2c031d6b3..64f18fbd4 100644 --- a/compiler/luci/import/src/Nodes/CircleExp.cpp +++ b/compiler/luci/import/src/Nodes/CircleExp.cpp @@ -25,10 +25,10 @@ namespace luci bool CircleExpGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; // input type check const auto &tensors = args.reader.tensors(); const auto &tensor = tensors.at(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleExpandDims.cpp b/compiler/luci/import/src/Nodes/CircleExpandDims.cpp index ab537c710..ee0fbdc7e 100644 --- a/compiler/luci/import/src/Nodes/CircleExpandDims.cpp +++ b/compiler/luci/import/src/Nodes/CircleExpandDims.cpp @@ -25,13 +25,10 @@ namespace luci bool CircleExpandDimsGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - - if (inputs.size() != 2) - { + if (!GraphBuilder::validate(args, 2)) return false; - } + const auto &inputs = args.op.inputs; const auto &tensors = args.reader.tensors(); return tensors[inputs.at(1)]->type == circle::TensorType_INT32; diff --git a/compiler/luci/import/src/Nodes/CircleFakeQuant.cpp b/compiler/luci/import/src/Nodes/CircleFakeQuant.cpp new file mode 100644 index 000000000..7cf40b225 --- /dev/null +++ b/compiler/luci/import/src/Nodes/CircleFakeQuant.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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/Import/Nodes/CircleFakeQuant.h" + +#include <luci/IR/Nodes/CircleFullyConnected.h> +#include <luci/IR/Nodes/CircleOutput.h> + +#include <loco.h> +#include <oops/UserExn.h> + +namespace luci +{ + +bool CircleFakeQuantGraphBuilder::validate(const ValidateArgs &args) const +{ + return GraphBuilder::validate(args, 1); +} + +CircleNode *CircleFakeQuantGraphBuilder::build_node(const circle::OperatorT &op, + const std::vector<CircleNode *> &inputs, + loco::Graph *graph) const +{ + auto *node = graph->nodes()->create<CircleFakeQuant>(); + node->inputs(inputs.at(0)); + + const auto *options = op.builtin_options.AsFakeQuantOptions(); + node->min(options->min); + node->max(options->max); + node->num_bits(options->num_bits); + node->narrow_range(options->narrow_range); + + return node; +} + +} // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleFill.cpp b/compiler/luci/import/src/Nodes/CircleFill.cpp index 95d5b876b..9aacddcbe 100644 --- a/compiler/luci/import/src/Nodes/CircleFill.cpp +++ b/compiler/luci/import/src/Nodes/CircleFill.cpp @@ -23,13 +23,7 @@ namespace luci bool CircleFillGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleFillGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleFloor.cpp b/compiler/luci/import/src/Nodes/CircleFloor.cpp index ce756b3b1..9651259c7 100644 --- a/compiler/luci/import/src/Nodes/CircleFloor.cpp +++ b/compiler/luci/import/src/Nodes/CircleFloor.cpp @@ -25,16 +25,8 @@ namespace luci bool CircleFloorGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 1) - return false; - if (outputs.size() != 1) - return false; - // TODO dtype check - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleFloorGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleFloorDiv.cpp b/compiler/luci/import/src/Nodes/CircleFloorDiv.cpp index 55f385d60..ce329326a 100644 --- a/compiler/luci/import/src/Nodes/CircleFloorDiv.cpp +++ b/compiler/luci/import/src/Nodes/CircleFloorDiv.cpp @@ -25,19 +25,11 @@ namespace luci bool CircleFloorDivGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - { + if (!GraphBuilder::validate(args, 2)) return false; - } - - if (outputs.size() != 1) - { - return false; - } + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor_in_0 = tensors.at(inputs.at(0)); const auto &tensor_in_1 = tensors.at(inputs.at(1)); diff --git a/compiler/luci/import/src/Nodes/CircleFloorMod.cpp b/compiler/luci/import/src/Nodes/CircleFloorMod.cpp index 2101e417e..d8420a43c 100644 --- a/compiler/luci/import/src/Nodes/CircleFloorMod.cpp +++ b/compiler/luci/import/src/Nodes/CircleFloorMod.cpp @@ -25,13 +25,10 @@ namespace luci bool CircleFloorModGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 2) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; const auto &tensors = args.reader.tensors(); const auto &tensor_in_0 = tensors.at(inputs.at(0)); const auto &tensor_in_1 = tensors.at(inputs.at(1)); diff --git a/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp index 17293ad7a..58750d79a 100644 --- a/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp +++ b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp @@ -27,10 +27,7 @@ namespace luci bool CircleFullyConnectedGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 3) - return false; - - return true; + return GraphBuilder::validate(args, 3); } CircleNode *CircleFullyConnectedGraphBuilder::build_node(const circle::OperatorT &op, @@ -42,15 +39,6 @@ CircleNode *CircleFullyConnectedGraphBuilder::build_node(const circle::OperatorT node->weights(inputs.at(1)); node->bias(inputs.at(2)); // bias is optional - // TODO Find and move to appropriate place for setting optional input - if (auto bias = dynamic_cast<luci::CircleOutputExclude *>(node->bias())) - { - // bias is not used for type inference, but node itself should have a type - bias->dtype(loco::DataType::FLOAT32); - - // bias is not used for shape inference - } - const auto *options = op.builtin_options.AsFullyConnectedOptions(); node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); node->weights_format(luci_weights_format(options->weights_format)); diff --git a/compiler/luci/import/src/Nodes/CircleGather.cpp b/compiler/luci/import/src/Nodes/CircleGather.cpp index 75447a38a..8317a3340 100644 --- a/compiler/luci/import/src/Nodes/CircleGather.cpp +++ b/compiler/luci/import/src/Nodes/CircleGather.cpp @@ -26,18 +26,14 @@ namespace luci bool CircleGatherGraphBuilder::validate(const ValidateArgs &args) const { + if (!GraphBuilder::validate(args, 2)) + return false; + const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; const auto *options = args.op.builtin_options.AsGatherOptions(); int32_t axis = options->axis; - if (inputs.size() != 2) - return false; - - if (outputs.size() != 1) - return false; - if (axis < 0) axis += inputs.size(); diff --git a/compiler/luci/import/src/Nodes/CircleGatherNd.cpp b/compiler/luci/import/src/Nodes/CircleGatherNd.cpp index 981adbf63..a4bb26a10 100644 --- a/compiler/luci/import/src/Nodes/CircleGatherNd.cpp +++ b/compiler/luci/import/src/Nodes/CircleGatherNd.cpp @@ -27,15 +27,10 @@ namespace luci bool CircleGatherNdGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - return false; - - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; auto &indices_tensor = args.reader.tensors()[inputs.at(1)]; if (!(indices_tensor->type == circle::TensorType::TensorType_INT32 || diff --git a/compiler/luci/import/src/Nodes/CircleGreater.cpp b/compiler/luci/import/src/Nodes/CircleGreater.cpp index 1ad0467e4..f9c00346c 100644 --- a/compiler/luci/import/src/Nodes/CircleGreater.cpp +++ b/compiler/luci/import/src/Nodes/CircleGreater.cpp @@ -30,17 +30,13 @@ bool CircleGreaterGraphBuilder::validate(const ValidateArgs &args) const { LOGGER(l); + if (!GraphBuilder::validate(args, 2)) + return false; + auto settings = luci::UserSettings::settings(); const auto &inputs = args.op.inputs; const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - return false; - - if (outputs.size() != 1) - return false; - const auto &tensors = args.reader.tensors(); if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type) diff --git a/compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp b/compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp index 0ac63b017..e20038fd9 100644 --- a/compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp +++ b/compiler/luci/import/src/Nodes/CircleGreaterEqual.cpp @@ -25,19 +25,11 @@ namespace luci bool CircleGreaterEqualGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - { + if (!GraphBuilder::validate(args, 2)) return false; - } - - if (outputs.size() != 1) - { - return false; - } + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type) diff --git a/compiler/luci/import/src/Nodes/CircleIf.cpp b/compiler/luci/import/src/Nodes/CircleIf.cpp index db9ffe1cd..ffdbf0b79 100644 --- a/compiler/luci/import/src/Nodes/CircleIf.cpp +++ b/compiler/luci/import/src/Nodes/CircleIf.cpp @@ -70,69 +70,34 @@ bool CircleIfGraphBuilder::validate(const ValidateArgs &args) const * \- CircleIfOut --- Node --- */ -void CircleIfGraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const +CircleNode *CircleIfGraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); + uint32_t input_count = bna.op.inputs.size() - 1; + uint32_t output_count = bna.op.outputs.size(); - auto graph = context->graph(); + auto *node = bna.context->graph()->nodes()->create<CircleIf>(input_count, output_count); - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - const auto &opcodes = context->reader()->opcodes(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); - - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } - - uint32_t input_count = inputs.size() - 1; - uint32_t output_count = outputs.size(); - - // Create CircleIf - CircleIf *node = graph->nodes()->create<CircleIf>(input_count, output_count); - - node->cond(input_nodes[0]); + node->cond(bna.input_nodes[0]); for (uint32_t idx = 0; idx < input_count; ++idx) { - node->input(idx, input_nodes[idx + 1]); + node->input(idx, bna.input_nodes[idx + 1]); } - const auto *options = op.builtin_options.AsIfOptions(); + const auto *options = bna.op.builtin_options.AsIfOptions(); node->then_branch(options->then_subgraph_index); node->else_branch(options->else_subgraph_index); - assert(outputs.size() > 0); - { - // Lets use name of output 0 as If name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->op_version(opcodes[op.opcode_index].get()->version); - - // NOTE We don't set quantization for If itself but to virtual outputs - } - - // Create virtual outputs of If - for (uint32_t n = 0; n < output_count; ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; + return node; +} - auto *nodeout = graph->nodes()->create<CircleIfOut>(); - copy_tensor_attributes(output_tensor, nodeout); - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); +CircleNode *CircleIfGraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleIfOut>(); - nodeout->input(node); - nodeout->index(n); + nodeout->input(boa.node); + nodeout->index(boa.index); - context->nodefinder()->enroll(outputs[n], nodeout); - } + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp b/compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp index 6349fd3b7..977b53406 100644 --- a/compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp +++ b/compiler/luci/import/src/Nodes/CircleInstanceNorm.cpp @@ -25,12 +25,8 @@ namespace luci bool CircleInstanceNormGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 3) - return false; - // TODO check dtypes - - return true; + return GraphBuilder::validate(args, 3); } CircleNode *CircleInstanceNormGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleL2Normalize.cpp b/compiler/luci/import/src/Nodes/CircleL2Normalize.cpp index e4fdc200c..7e1faedfb 100644 --- a/compiler/luci/import/src/Nodes/CircleL2Normalize.cpp +++ b/compiler/luci/import/src/Nodes/CircleL2Normalize.cpp @@ -25,20 +25,7 @@ namespace luci bool CircleL2NormalizeGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 1) - { - return false; - } - - if (outputs.size() != 1) - { - return false; - } - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleL2NormalizeGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp b/compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp index 202d9d6fb..849c7c5ed 100644 --- a/compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp +++ b/compiler/luci/import/src/Nodes/CircleL2Pool2D.cpp @@ -25,12 +25,8 @@ namespace luci bool CircleL2Pool2DGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - // TODO check dtypes - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleL2Pool2DGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp b/compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp index ad4979f39..880fa6428 100644 --- a/compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp +++ b/compiler/luci/import/src/Nodes/CircleLeakyRelu.cpp @@ -25,13 +25,7 @@ namespace luci bool CircleLeakyReluGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleLeakyReluGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleLess.cpp b/compiler/luci/import/src/Nodes/CircleLess.cpp index 506036908..f9b99bebe 100644 --- a/compiler/luci/import/src/Nodes/CircleLess.cpp +++ b/compiler/luci/import/src/Nodes/CircleLess.cpp @@ -25,19 +25,11 @@ namespace luci bool CircleLessGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - { + if (!GraphBuilder::validate(args, 2)) return false; - } - - if (outputs.size() != 1) - { - return false; - } + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor = tensors.at(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleLessEqual.cpp b/compiler/luci/import/src/Nodes/CircleLessEqual.cpp index 9b4f934a5..bb1712137 100644 --- a/compiler/luci/import/src/Nodes/CircleLessEqual.cpp +++ b/compiler/luci/import/src/Nodes/CircleLessEqual.cpp @@ -25,19 +25,11 @@ namespace luci bool CircleLessEqualGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - { + if (!GraphBuilder::validate(args, 2)) return false; - } - - if (outputs.size() != 1) - { - return false; - } + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type) diff --git a/compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp b/compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp index 0e32f62de..d03c47d12 100644 --- a/compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp +++ b/compiler/luci/import/src/Nodes/CircleLocalResponseNormalization.cpp @@ -25,16 +25,12 @@ namespace luci bool CircleLocalResponseNormalizationGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - // TODO do attribute checks - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleLocalResponseNormalizationGraphBuilder::build_node( - const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const + const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const { auto *node = graph->nodes()->create<CircleLocalResponseNormalization>(); node->input(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleLog.cpp b/compiler/luci/import/src/Nodes/CircleLog.cpp index 346fc43bb..26b575070 100644 --- a/compiler/luci/import/src/Nodes/CircleLog.cpp +++ b/compiler/luci/import/src/Nodes/CircleLog.cpp @@ -25,12 +25,10 @@ namespace luci bool CircleLogGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) - return false; - if (args.op.outputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; // input type check // Must be one of bfloat16, half, float32, float64, complex64, complex128. // Currently circle supports half(float16), float32, float64, complex64. diff --git a/compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp b/compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp index ef69e868a..4361db691 100644 --- a/compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp +++ b/compiler/luci/import/src/Nodes/CircleLogSoftmax.cpp @@ -25,12 +25,8 @@ namespace luci bool CircleLogSoftmaxGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - // TODO do attribute checks - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleLogSoftmaxGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp b/compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp index 7844da0f6..b13fc2735 100644 --- a/compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp +++ b/compiler/luci/import/src/Nodes/CircleLogicalAnd.cpp @@ -25,11 +25,11 @@ namespace luci bool CircleLogicalAndGraphBuilder::validate(const ValidateArgs &args) const { - // Only BOOL type is allowed for inputs - const auto &inputs = args.op.inputs; - if (inputs.size() != 2) + if (!GraphBuilder::validate(args, 2)) return false; + // Only BOOL type is allowed for inputs + const auto &inputs = args.op.inputs; const auto &tensors = args.reader.tensors(); for (auto input : inputs) { diff --git a/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp index 3758642e4..f68218349 100644 --- a/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp +++ b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp @@ -25,7 +25,7 @@ namespace luci bool CircleLogicalNotGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; // Only BOOL type is allowed for the input diff --git a/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp index 1b87e6f9c..8c9023dd3 100644 --- a/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp +++ b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp @@ -25,7 +25,7 @@ namespace luci bool CircleLogicalOrGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) + if (!GraphBuilder::validate(args, 2)) return false; // Only BOOL type is allowed for inputs diff --git a/compiler/luci/import/src/Nodes/CircleLogistic.cpp b/compiler/luci/import/src/Nodes/CircleLogistic.cpp index 9606e19cd..0f92a9bb4 100644 --- a/compiler/luci/import/src/Nodes/CircleLogistic.cpp +++ b/compiler/luci/import/src/Nodes/CircleLogistic.cpp @@ -25,13 +25,11 @@ namespace luci bool CircleLogisticGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) - return false; - const auto &outputs = args.op.outputs; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); if (tensors.at(inputs.at(0))->type != tensors.at(outputs[0])->type) return false; diff --git a/compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp b/compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp index a4a21a8b7..590a07f2d 100644 --- a/compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp +++ b/compiler/luci/import/src/Nodes/CircleMatrixDiag.cpp @@ -25,15 +25,11 @@ namespace luci bool CircleMatrixDiagGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 1) - return false; - - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor = tensors.at(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp b/compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp index cf0313149..edd7d2ae2 100644 --- a/compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp +++ b/compiler/luci/import/src/Nodes/CircleMatrixSetDiag.cpp @@ -25,15 +25,11 @@ namespace luci bool CircleMatrixSetDiagGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - return false; - - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor = tensors.at(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp index 4bca0f40b..5c03fff18 100644 --- a/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp +++ b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleMaxPool2DGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleMaxPool2DGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleMean.cpp b/compiler/luci/import/src/Nodes/CircleMean.cpp index d8fa9a53d..7882f17fc 100644 --- a/compiler/luci/import/src/Nodes/CircleMean.cpp +++ b/compiler/luci/import/src/Nodes/CircleMean.cpp @@ -23,10 +23,7 @@ namespace luci bool CircleMeanGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleMeanGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleMirrorPad.cpp b/compiler/luci/import/src/Nodes/CircleMirrorPad.cpp index e0ddd4c11..e40ce2249 100644 --- a/compiler/luci/import/src/Nodes/CircleMirrorPad.cpp +++ b/compiler/luci/import/src/Nodes/CircleMirrorPad.cpp @@ -25,12 +25,8 @@ namespace luci bool CircleMirrorPadGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - // TODO check others - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleMirrorPadGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleMul.cpp b/compiler/luci/import/src/Nodes/CircleMul.cpp index e3c4a7ee5..28421f8c4 100644 --- a/compiler/luci/import/src/Nodes/CircleMul.cpp +++ b/compiler/luci/import/src/Nodes/CircleMul.cpp @@ -23,13 +23,7 @@ namespace luci bool CircleMulGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleMulGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleNeg.cpp b/compiler/luci/import/src/Nodes/CircleNeg.cpp index a64a69560..9dd1458f4 100644 --- a/compiler/luci/import/src/Nodes/CircleNeg.cpp +++ b/compiler/luci/import/src/Nodes/CircleNeg.cpp @@ -24,11 +24,8 @@ namespace luci { bool CircleNegGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - // TODO Support type check - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleNegGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp index a4ad4a53d..d3d69506b 100644 --- a/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp +++ b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV4.cpp @@ -61,63 +61,27 @@ bool CircleNonMaxSuppressionV4GraphBuilder::validate(const ValidateArgs &args) c * We will create multiple NonMasSuppressionV4Oout nodes to emulate this */ -void CircleNonMaxSuppressionV4GraphBuilder::build(const circle::OperatorT &op, - GraphBuilderContext *context) const +CircleNode *CircleNonMaxSuppressionV4GraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); - - auto graph = context->graph(); - - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - const auto &opcodes = context->reader()->opcodes(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); - - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } - - // Create CircleNonMaxSuppressionV4 - auto node = graph->nodes()->create<CircleNonMaxSuppressionV4>(); - node->boxes(input_nodes[0]); - node->scores(input_nodes[1]); - node->max_output_size(input_nodes[2]); - node->iou_threshold(input_nodes[3]); - node->score_threshold(input_nodes[4]); - - assert(outputs.size() == 2); - { - // Let's use name of output 0 as NonMaxSuppressionV4 name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->op_version(opcodes[op.opcode_index].get()->version); - - // NOTE We don't set quantization for NonMaxSuppressionV4 itself but to virtual outputs - } - - // Create virtual outputs of NonMaxSuppressionV4 - for (size_t n = 0; n < outputs.size(); ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; - - auto *nodeout = graph->nodes()->create<CircleNonMaxSuppressionV4Out>(); - copy_tensor_attributes(output_tensor, nodeout); - - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); - - nodeout->input(node); - nodeout->index(n); - - context->nodefinder()->enroll(outputs[n], nodeout); - } + auto node = bna.context->graph()->nodes()->create<CircleNonMaxSuppressionV4>(); + + node->boxes(bna.input_nodes[0]); + node->scores(bna.input_nodes[1]); + node->max_output_size(bna.input_nodes[2]); + node->iou_threshold(bna.input_nodes[3]); + node->score_threshold(bna.input_nodes[4]); + + return node; +} + +CircleNode *CircleNonMaxSuppressionV4GraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleNonMaxSuppressionV4Out>(); + + nodeout->input(boa.node); + nodeout->index(boa.index); + + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp index 241dbf5ff..d797d4cb7 100644 --- a/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp +++ b/compiler/luci/import/src/Nodes/CircleNonMaxSuppressionV5.cpp @@ -63,64 +63,28 @@ bool CircleNonMaxSuppressionV5GraphBuilder::validate(const ValidateArgs &args) c * We will create multiple NonMasSuppressionV5Oout nodes to emulate this */ -void CircleNonMaxSuppressionV5GraphBuilder::build(const circle::OperatorT &op, - GraphBuilderContext *context) const +CircleNode *CircleNonMaxSuppressionV5GraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); - - auto graph = context->graph(); - - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - const auto &opcodes = context->reader()->opcodes(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); - - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } - - // Create CircleNonMaxSuppressionV5 - auto node = graph->nodes()->create<CircleNonMaxSuppressionV5>(); - node->boxes(input_nodes[0]); - node->scores(input_nodes[1]); - node->max_output_size(input_nodes[2]); - node->iou_threshold(input_nodes[3]); - node->score_threshold(input_nodes[4]); - node->soft_nms_sigma(input_nodes[5]); - - assert(outputs.size() == 3); - { - // Let's use name of output 0 as NonMaxSuppressionV5 name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->op_version(opcodes[op.opcode_index].get()->version); - - // NOTE We don't set quantization for NonMaxSuppressionV5 itself but to virtual outputs - } - - // Create virtual outputs of NonMaxSuppressionV5 - for (size_t n = 0; n < outputs.size(); ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; - - auto *nodeout = graph->nodes()->create<CircleNonMaxSuppressionV5Out>(); - copy_tensor_attributes(output_tensor, nodeout); - - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); - - nodeout->input(node); - nodeout->index(n); - - context->nodefinder()->enroll(outputs[n], nodeout); - } + auto node = bna.context->graph()->nodes()->create<CircleNonMaxSuppressionV5>(); + + node->boxes(bna.input_nodes[0]); + node->scores(bna.input_nodes[1]); + node->max_output_size(bna.input_nodes[2]); + node->iou_threshold(bna.input_nodes[3]); + node->score_threshold(bna.input_nodes[4]); + node->soft_nms_sigma(bna.input_nodes[5]); + + return node; +} + +CircleNode *CircleNonMaxSuppressionV5GraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleNonMaxSuppressionV5Out>(); + + nodeout->input(boa.node); + nodeout->index(boa.index); + + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleNotEqual.cpp b/compiler/luci/import/src/Nodes/CircleNotEqual.cpp index 77e986de1..a0b8f9e4f 100644 --- a/compiler/luci/import/src/Nodes/CircleNotEqual.cpp +++ b/compiler/luci/import/src/Nodes/CircleNotEqual.cpp @@ -25,19 +25,11 @@ namespace luci bool CircleNotEqualGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - { + if (!GraphBuilder::validate(args, 2)) return false; - } - - if (outputs.size() != 1) - { - return false; - } + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); if (tensors[inputs.at(0)]->type != tensors[inputs.at(1)]->type) diff --git a/compiler/luci/import/src/Nodes/CircleOneHot.cpp b/compiler/luci/import/src/Nodes/CircleOneHot.cpp index 69294e1ed..3952cc21a 100644 --- a/compiler/luci/import/src/Nodes/CircleOneHot.cpp +++ b/compiler/luci/import/src/Nodes/CircleOneHot.cpp @@ -26,17 +26,12 @@ namespace luci bool CircleOneHotGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - const auto *options = args.op.builtin_options.AsOneHotOptions(); - // Only 4 Input come refered from - if (inputs.size() != 4) - return false; - - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 4)) return false; + const auto &inputs = args.op.inputs; + const auto *options = args.op.builtin_options.AsOneHotOptions(); const auto &tensors = args.reader.tensors(); const auto &indices = tensors.at(inputs.at(0)); const auto &depth = tensors.at(inputs.at(1)); diff --git a/compiler/luci/import/src/Nodes/CirclePRelu.cpp b/compiler/luci/import/src/Nodes/CirclePRelu.cpp index c07920f7c..7c81f04bb 100644 --- a/compiler/luci/import/src/Nodes/CirclePRelu.cpp +++ b/compiler/luci/import/src/Nodes/CirclePRelu.cpp @@ -25,13 +25,7 @@ namespace luci bool CirclePReluGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CirclePReluGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CirclePad.cpp b/compiler/luci/import/src/Nodes/CirclePad.cpp index 999173b90..67dce6dee 100644 --- a/compiler/luci/import/src/Nodes/CirclePad.cpp +++ b/compiler/luci/import/src/Nodes/CirclePad.cpp @@ -25,12 +25,8 @@ namespace luci bool CirclePadGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - // TODO do attribute checks - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CirclePadGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CirclePadV2.cpp b/compiler/luci/import/src/Nodes/CirclePadV2.cpp index 493876e68..84a45722a 100644 --- a/compiler/luci/import/src/Nodes/CirclePadV2.cpp +++ b/compiler/luci/import/src/Nodes/CirclePadV2.cpp @@ -25,13 +25,7 @@ namespace luci bool CirclePadV2GraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 3) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 3); } CircleNode *CirclePadV2GraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CirclePow.cpp b/compiler/luci/import/src/Nodes/CirclePow.cpp index def012614..1d2d41607 100644 --- a/compiler/luci/import/src/Nodes/CirclePow.cpp +++ b/compiler/luci/import/src/Nodes/CirclePow.cpp @@ -25,13 +25,7 @@ namespace luci bool CirclePowGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CirclePowGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleRange.cpp b/compiler/luci/import/src/Nodes/CircleRange.cpp index 38dc44ed6..d3b5afc95 100644 --- a/compiler/luci/import/src/Nodes/CircleRange.cpp +++ b/compiler/luci/import/src/Nodes/CircleRange.cpp @@ -24,11 +24,8 @@ namespace luci { bool CircleRangeGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 3) - return false; - // TODO Support type check - return true; + return GraphBuilder::validate(args, 3); } CircleNode *CircleRangeGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleRank.cpp b/compiler/luci/import/src/Nodes/CircleRank.cpp index 12658b192..afebb9509 100644 --- a/compiler/luci/import/src/Nodes/CircleRank.cpp +++ b/compiler/luci/import/src/Nodes/CircleRank.cpp @@ -24,13 +24,7 @@ namespace luci { bool CircleRankGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleRankGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleReduceAny.cpp b/compiler/luci/import/src/Nodes/CircleReduceAny.cpp index 21a821951..13205dd7a 100644 --- a/compiler/luci/import/src/Nodes/CircleReduceAny.cpp +++ b/compiler/luci/import/src/Nodes/CircleReduceAny.cpp @@ -23,13 +23,11 @@ namespace luci bool CircleReduceAnyGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 2) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor_0 = tensors.at(inputs.at(0)); const auto &tensor_1 = tensors.at(inputs.at(1)); diff --git a/compiler/luci/import/src/Nodes/CircleReduceProd.cpp b/compiler/luci/import/src/Nodes/CircleReduceProd.cpp index 5f054586e..3549c1a18 100644 --- a/compiler/luci/import/src/Nodes/CircleReduceProd.cpp +++ b/compiler/luci/import/src/Nodes/CircleReduceProd.cpp @@ -23,12 +23,10 @@ namespace luci bool CircleReduceProdGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 2) - return false; - if (args.op.outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; const auto &tensors = args.reader.tensors(); const auto &tensor_1 = tensors.at(inputs.at(1)); diff --git a/compiler/luci/import/src/Nodes/CircleRelu.cpp b/compiler/luci/import/src/Nodes/CircleRelu.cpp index 8e1c32a3a..73b8ffee8 100644 --- a/compiler/luci/import/src/Nodes/CircleRelu.cpp +++ b/compiler/luci/import/src/Nodes/CircleRelu.cpp @@ -25,13 +25,7 @@ namespace luci bool CircleReluGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleReluGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleRelu6.cpp b/compiler/luci/import/src/Nodes/CircleRelu6.cpp index 0283d7350..ab957eda8 100644 --- a/compiler/luci/import/src/Nodes/CircleRelu6.cpp +++ b/compiler/luci/import/src/Nodes/CircleRelu6.cpp @@ -25,13 +25,7 @@ namespace luci bool CircleRelu6GraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleRelu6GraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleReluN1To1.cpp b/compiler/luci/import/src/Nodes/CircleReluN1To1.cpp index 7f517bc0d..4987f3be2 100644 --- a/compiler/luci/import/src/Nodes/CircleReluN1To1.cpp +++ b/compiler/luci/import/src/Nodes/CircleReluN1To1.cpp @@ -25,15 +25,8 @@ namespace luci bool CircleReluN1To1GraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - if (args.op.outputs.size() != 1) - return false; - // TODO check dtypes - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleReluN1To1GraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleReshape.cpp b/compiler/luci/import/src/Nodes/CircleReshape.cpp index 996ae9d20..401dff0fc 100644 --- a/compiler/luci/import/src/Nodes/CircleReshape.cpp +++ b/compiler/luci/import/src/Nodes/CircleReshape.cpp @@ -30,6 +30,19 @@ bool CircleReshapeGraphBuilder::validate(const ValidateArgs &args) const if (args.op.outputs.size() != 1) return false; + // for two inputs, check if type is S32 + if (args.op.inputs.size() == 2) + { + const auto &inputs = args.op.inputs; + const auto &tensors = args.reader.tensors(); + const auto &tensor_in = tensors.at(inputs.at(1)); + + // NOTE fix this if there is any other case + // TensorFlow lite and circle only supports S32 + if (tensor_in->type != circle::TensorType::TensorType_INT32) + return false; + } + return true; } @@ -53,6 +66,7 @@ static CircleNode *create_shape_node(const std::vector<int32_t> &shape, loco::Gr { shape_node->at<loco::DataType::S32>(i) = shape[i]; } + shape_node->name("Reshape/shape"); return shape_node; } @@ -73,6 +87,7 @@ CircleNode *CircleReshapeGraphBuilder::build_node(const circle::OperatorT &op, shape_node = graph->nodes()->create<CircleOutputDummy>(); shape_node->dtype(loco::DataType::S32); shape_node->rank(0); + shape_node->name("Reshape/dummy"); } } diff --git a/compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp b/compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp index 0fccb7b44..c751b245c 100644 --- a/compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp +++ b/compiler/luci/import/src/Nodes/CircleResizeBilinear.cpp @@ -16,7 +16,6 @@ #include "luci/Import/Nodes/CircleResizeBilinear.h" -#include <luci/IR/Nodes/CircleConst.h> #include <luci/IR/Nodes/CircleResizeBilinear.h> namespace luci @@ -24,13 +23,7 @@ namespace luci bool CircleResizeBilinearGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleResizeBilinearGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp b/compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp index 324323f59..df7517fe9 100644 --- a/compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp +++ b/compiler/luci/import/src/Nodes/CircleResizeNearestNeighbor.cpp @@ -16,7 +16,6 @@ #include "luci/Import/Nodes/CircleResizeNearestNeighbor.h" -#include <luci/IR/Nodes/CircleConst.h> #include <luci/IR/Nodes/CircleResizeNearestNeighbor.h> namespace luci @@ -24,17 +23,11 @@ namespace luci bool CircleResizeNearestNeighborGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleResizeNearestNeighborGraphBuilder::build_node( - const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const + const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const { auto *node = graph->nodes()->create<CircleResizeNearestNeighbor>(); node->input(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleReverseSequence.cpp b/compiler/luci/import/src/Nodes/CircleReverseSequence.cpp index ad11d4c63..2fbb7a87c 100644 --- a/compiler/luci/import/src/Nodes/CircleReverseSequence.cpp +++ b/compiler/luci/import/src/Nodes/CircleReverseSequence.cpp @@ -25,14 +25,11 @@ namespace luci bool CircleReverseSequenceGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor_in = tensors.at(inputs.at(0)); const auto &tensor_lengths = tensors.at(inputs.at(1)); diff --git a/compiler/luci/import/src/Nodes/CircleReverseV2.cpp b/compiler/luci/import/src/Nodes/CircleReverseV2.cpp index e2e53bb4b..ca7653201 100644 --- a/compiler/luci/import/src/Nodes/CircleReverseV2.cpp +++ b/compiler/luci/import/src/Nodes/CircleReverseV2.cpp @@ -25,14 +25,11 @@ namespace luci bool CircleReverseV2GraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor_in = tensors.at(inputs.at(0)); const auto &tensor_axis = tensors.at(inputs.at(1)); diff --git a/compiler/luci/import/src/Nodes/CircleRound.cpp b/compiler/luci/import/src/Nodes/CircleRound.cpp index ad77f9f03..d13e0fafe 100644 --- a/compiler/luci/import/src/Nodes/CircleRound.cpp +++ b/compiler/luci/import/src/Nodes/CircleRound.cpp @@ -25,14 +25,11 @@ namespace luci bool CircleRoundGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 1) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; // Must be one of the following types // bfloat16, half (float16), float32, float64, complex64, complex128 // Currently, circle supports float16, float32, complex64 diff --git a/compiler/luci/import/src/Nodes/CircleRsqrt.cpp b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp index ae05fbbf9..a9ca90832 100644 --- a/compiler/luci/import/src/Nodes/CircleRsqrt.cpp +++ b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp @@ -25,10 +25,10 @@ namespace luci bool CircleRsqrtGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; // Must be one of the following types // bfloat16, half (float16), float32, float64, complex64, complex128 // Currently, circle supports float16, float32, complex64 @@ -36,6 +36,8 @@ bool CircleRsqrtGraphBuilder::validate(const ValidateArgs &args) const const auto &tensor = tensors.at(inputs.at(0)); switch (tensor->type) { + case circle::TensorType_UINT8: + case circle::TensorType_INT16: case circle::TensorType_FLOAT16: case circle::TensorType_FLOAT32: case circle::TensorType_COMPLEX64: diff --git a/compiler/luci/import/src/Nodes/CircleScatterNd.cpp b/compiler/luci/import/src/Nodes/CircleScatterNd.cpp index 7f86aeb74..f8c175110 100644 --- a/compiler/luci/import/src/Nodes/CircleScatterNd.cpp +++ b/compiler/luci/import/src/Nodes/CircleScatterNd.cpp @@ -25,10 +25,10 @@ namespace luci bool CircleScatterNdGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 3) + if (!GraphBuilder::validate(args, 3)) return false; + const auto &inputs = args.op.inputs; // indices must have the same type as shape const auto &tensors = args.reader.tensors(); diff --git a/compiler/luci/import/src/Nodes/CircleSegmentSum.cpp b/compiler/luci/import/src/Nodes/CircleSegmentSum.cpp index fb84e5d52..bfa333e8d 100644 --- a/compiler/luci/import/src/Nodes/CircleSegmentSum.cpp +++ b/compiler/luci/import/src/Nodes/CircleSegmentSum.cpp @@ -25,13 +25,11 @@ namespace luci bool CircleSegmentSumGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 2) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor_in = tensors.at(inputs.at(0)); const auto &tensor_out = tensors.at(outputs[0]); diff --git a/compiler/luci/import/src/Nodes/CircleSelect.cpp b/compiler/luci/import/src/Nodes/CircleSelect.cpp index 1e649f1e0..36a5fa8a8 100644 --- a/compiler/luci/import/src/Nodes/CircleSelect.cpp +++ b/compiler/luci/import/src/Nodes/CircleSelect.cpp @@ -25,13 +25,10 @@ namespace luci bool CircleSelectGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 3) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 3)) return false; + const auto &inputs = args.op.inputs; const auto &tensors = args.reader.tensors(); const auto &tensor = tensors.at(inputs.at(0)); if (tensor->type != circle::TensorType_BOOL) diff --git a/compiler/luci/import/src/Nodes/CircleSelectV2.cpp b/compiler/luci/import/src/Nodes/CircleSelectV2.cpp index e6dd04de0..556c8fa33 100644 --- a/compiler/luci/import/src/Nodes/CircleSelectV2.cpp +++ b/compiler/luci/import/src/Nodes/CircleSelectV2.cpp @@ -25,13 +25,10 @@ namespace luci bool CircleSelectV2GraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 3) - return false; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 3)) return false; + const auto &inputs = args.op.inputs; const auto &tensors = args.reader.tensors(); const auto &condition = tensors.at(inputs.at(0)); if (condition->type != circle::TensorType_BOOL) diff --git a/compiler/luci/import/src/Nodes/CircleShape.cpp b/compiler/luci/import/src/Nodes/CircleShape.cpp index bd7dfc9d9..86c0bf59b 100644 --- a/compiler/luci/import/src/Nodes/CircleShape.cpp +++ b/compiler/luci/import/src/Nodes/CircleShape.cpp @@ -25,16 +25,8 @@ namespace luci bool CircleShapeGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - if (inputs.size() != 1) - return false; - if (outputs.size() != 1) - return false; - // TODO check shape, dtype - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleShapeGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleSin.cpp b/compiler/luci/import/src/Nodes/CircleSin.cpp index 4b245ef6b..22f461123 100644 --- a/compiler/luci/import/src/Nodes/CircleSin.cpp +++ b/compiler/luci/import/src/Nodes/CircleSin.cpp @@ -25,12 +25,10 @@ namespace luci bool CircleSinGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) - return false; - if (args.op.outputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; // input type check const auto &tensors = args.reader.tensors(); const auto &tensor = tensors.at(inputs.at(0)); diff --git a/compiler/luci/import/src/Nodes/CircleSlice.cpp b/compiler/luci/import/src/Nodes/CircleSlice.cpp index 8601fbf21..4166040b3 100644 --- a/compiler/luci/import/src/Nodes/CircleSlice.cpp +++ b/compiler/luci/import/src/Nodes/CircleSlice.cpp @@ -27,14 +27,8 @@ namespace luci bool CircleSliceGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 3) - return false; - if (args.op.outputs.size() != 1) - return false; - // TODO check shapes and types - - return true; + return GraphBuilder::validate(args, 3); } CircleNode *CircleSliceGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleSoftmax.cpp b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp index 0ef0b5418..e79914455 100644 --- a/compiler/luci/import/src/Nodes/CircleSoftmax.cpp +++ b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp @@ -25,12 +25,8 @@ namespace luci bool CircleSoftmaxGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - // TODO do attribute checks - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleSoftmaxGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp b/compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp index 8ccd55dc6..2152b65c9 100644 --- a/compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp +++ b/compiler/luci/import/src/Nodes/CircleSpaceToDepth.cpp @@ -27,13 +27,8 @@ namespace luci bool CircleSpaceToDepthGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) - return false; - // TODO do attribute checks - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleSpaceToDepthGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleSparseToDense.cpp b/compiler/luci/import/src/Nodes/CircleSparseToDense.cpp index ac756b1f3..ce0688bb9 100644 --- a/compiler/luci/import/src/Nodes/CircleSparseToDense.cpp +++ b/compiler/luci/import/src/Nodes/CircleSparseToDense.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleSparseToDenseGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 4) - return false; - - return true; + return GraphBuilder::validate(args, 4); } CircleNode *CircleSparseToDenseGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleSplit.cpp b/compiler/luci/import/src/Nodes/CircleSplit.cpp index 07b6cc939..d0a24aae3 100644 --- a/compiler/luci/import/src/Nodes/CircleSplit.cpp +++ b/compiler/luci/import/src/Nodes/CircleSplit.cpp @@ -58,62 +58,27 @@ bool CircleSplitGraphBuilder::validate(const ValidateArgs &args) const * \- CircleSplitOut --- FullyConnected --- */ -void CircleSplitGraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const +CircleNode *CircleSplitGraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); + auto node = bna.context->graph()->nodes()->create<CircleSplit>(); - auto graph = context->graph(); + node->split_dim(bna.input_nodes[0]); + node->input(bna.input_nodes[1]); - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - const auto &opcodes = context->reader()->opcodes(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); + const auto *options = bna.op.builtin_options.AsSplitOptions(); + node->num_split(options->num_splits); - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } + return node; +} - // Create CircleSplit - auto node = graph->nodes()->create<CircleSplit>(); - node->split_dim(input_nodes[0]); - node->input(input_nodes[1]); +CircleNode *CircleSplitGraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleSplitOut>(); - const auto *options = op.builtin_options.AsSplitOptions(); - node->num_split(options->num_splits); + nodeout->input(boa.node); + nodeout->index(boa.index); - assert(outputs.size() > 0); - assert(int32_t(outputs.size()) == options->num_splits); - { - // Let's use name of output 0 as Split name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->op_version(opcodes[op.opcode_index].get()->version); - - // NOTE We don't set quantization for Split itself but to virtual outputs - } - - // Create virtual outputs of Split - for (int32_t n = 0; n < options->num_splits; ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; - - auto *nodeout = graph->nodes()->create<CircleSplitOut>(); - copy_tensor_attributes(output_tensor, nodeout); - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); - - nodeout->input(node); - nodeout->index(n); - - context->nodefinder()->enroll(outputs[n], nodeout); - } + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleSplitV.cpp b/compiler/luci/import/src/Nodes/CircleSplitV.cpp index 7c6e83e17..76cbf7046 100644 --- a/compiler/luci/import/src/Nodes/CircleSplitV.cpp +++ b/compiler/luci/import/src/Nodes/CircleSplitV.cpp @@ -58,64 +58,30 @@ bool CircleSplitVGraphBuilder::validate(const ValidateArgs &args) const * \- CircleSplitVOut --- FullyConnected --- */ -void CircleSplitVGraphBuilder::build(const circle::OperatorT &op, - GraphBuilderContext *context) const +CircleNode *CircleSplitVGraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); - - auto graph = context->graph(); - - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - const auto &opcodes = context->reader()->opcodes(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); - - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } - - // Create CircleSplitV - auto node = graph->nodes()->create<CircleSplitV>(); - node->input(input_nodes[0]); - node->size_splits(input_nodes[1]); - node->split_dim(input_nodes[2]); - - const auto *options = op.builtin_options.AsSplitVOptions(); + auto node = bna.context->graph()->nodes()->create<CircleSplitV>(); + + node->input(bna.input_nodes[0]); + node->size_splits(bna.input_nodes[1]); + node->split_dim(bna.input_nodes[2]); + + const auto *options = bna.op.builtin_options.AsSplitVOptions(); node->num_split(options->num_splits); - assert(outputs.size() > 0); - assert(int32_t(outputs.size()) == options->num_splits); - { - // Let's use name of output 0 as Split name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->op_version(opcodes[op.opcode_index].get()->version); - - // NOTE We don't set quantization for Split itself but to virtual outputs - } - - // Create virtual outputs of Split - for (int32_t n = 0; n < options->num_splits; ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; - - auto *nodeout = graph->nodes()->create<CircleSplitVOut>(); - copy_tensor_attributes(output_tensor, nodeout); - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); - - nodeout->input(node); - nodeout->index(n); - - context->nodefinder()->enroll(outputs[n], nodeout); - } + assert(int32_t(bna.op.outputs.size()) == options->num_splits); + + return node; +} + +CircleNode *CircleSplitVGraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleSplitVOut>(); + + nodeout->input(boa.node); + nodeout->index(boa.index); + + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleSqrt.cpp b/compiler/luci/import/src/Nodes/CircleSqrt.cpp index c8beaee0d..b1fdf7996 100644 --- a/compiler/luci/import/src/Nodes/CircleSqrt.cpp +++ b/compiler/luci/import/src/Nodes/CircleSqrt.cpp @@ -25,10 +25,7 @@ namespace luci bool CircleSqrtGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleSqrtGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/Nodes/CircleSquare.cpp b/compiler/luci/import/src/Nodes/CircleSquare.cpp index b5ba048d7..7ff2b84e6 100644 --- a/compiler/luci/import/src/Nodes/CircleSquare.cpp +++ b/compiler/luci/import/src/Nodes/CircleSquare.cpp @@ -25,10 +25,10 @@ namespace luci bool CircleSquareGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; // Must be one of the following types // bfloat16, half (float16), float32, float64, complex64, complex128 // Currently, circle supports float16, float32, complex64 diff --git a/compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp b/compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp index 6deae94c5..f4e193713 100644 --- a/compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp +++ b/compiler/luci/import/src/Nodes/CircleSquaredDifference.cpp @@ -25,15 +25,11 @@ namespace luci bool CircleSquaredDifferenceGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 2) - return false; - - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; // Inputs must be one of the following types // bfloat16, half(float16), float32, float64, int32, int64, complex64, complex128 const auto &tensors = args.reader.tensors(); diff --git a/compiler/luci/import/src/Nodes/CircleSqueeze.cpp b/compiler/luci/import/src/Nodes/CircleSqueeze.cpp index 32792c266..d24d8166c 100644 --- a/compiler/luci/import/src/Nodes/CircleSqueeze.cpp +++ b/compiler/luci/import/src/Nodes/CircleSqueeze.cpp @@ -16,7 +16,6 @@ #include "luci/Import/Nodes/CircleSqueeze.h" -#include <luci/IR/Nodes/CircleConst.h> #include <luci/IR/Nodes/CircleSqueeze.h> namespace luci @@ -24,13 +23,7 @@ namespace luci bool CircleSqueezeGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleSqueezeGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleStridedSlice.cpp b/compiler/luci/import/src/Nodes/CircleStridedSlice.cpp index 8f943a682..ca8259cac 100644 --- a/compiler/luci/import/src/Nodes/CircleStridedSlice.cpp +++ b/compiler/luci/import/src/Nodes/CircleStridedSlice.cpp @@ -27,14 +27,8 @@ namespace luci bool CircleStridedSliceGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 4) - return false; - if (args.op.outputs.size() != 1) - return false; - // TODO check shapes and types - - return true; + return GraphBuilder::validate(args, 4); } CircleNode *CircleStridedSliceGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleSub.cpp b/compiler/luci/import/src/Nodes/CircleSub.cpp index 9acf83d40..c3978f218 100644 --- a/compiler/luci/import/src/Nodes/CircleSub.cpp +++ b/compiler/luci/import/src/Nodes/CircleSub.cpp @@ -25,13 +25,7 @@ namespace luci bool CircleSubGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleSubGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleSum.cpp b/compiler/luci/import/src/Nodes/CircleSum.cpp index bd3cb6239..e348a62d9 100644 --- a/compiler/luci/import/src/Nodes/CircleSum.cpp +++ b/compiler/luci/import/src/Nodes/CircleSum.cpp @@ -23,10 +23,7 @@ namespace luci bool CircleSumGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleSumGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleTanh.cpp b/compiler/luci/import/src/Nodes/CircleTanh.cpp index 018f5701b..95625a0e4 100644 --- a/compiler/luci/import/src/Nodes/CircleTanh.cpp +++ b/compiler/luci/import/src/Nodes/CircleTanh.cpp @@ -25,13 +25,11 @@ namespace luci bool CircleTanhGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - if (inputs.size() != 1) - return false; - const auto &outputs = args.op.outputs; - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); if (tensors.at(inputs.at(0))->type != tensors.at(outputs[0])->type) return false; diff --git a/compiler/luci/import/src/Nodes/CircleTile.cpp b/compiler/luci/import/src/Nodes/CircleTile.cpp index bc6f320ba..6da44130c 100644 --- a/compiler/luci/import/src/Nodes/CircleTile.cpp +++ b/compiler/luci/import/src/Nodes/CircleTile.cpp @@ -25,15 +25,11 @@ namespace luci bool CircleTileGraphBuilder::validate(const ValidateArgs &args) const { - auto inputs = args.op.inputs; - auto outputs = args.op.outputs; - - if (inputs.size() != 2) - return false; - - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 2)) return false; + auto inputs = args.op.inputs; + auto outputs = args.op.outputs; // Multiples (inputs.at(1)) must be one of the following types // int32, int64 const auto &tensors = args.reader.tensors(); diff --git a/compiler/luci/import/src/Nodes/CircleTopKV2.cpp b/compiler/luci/import/src/Nodes/CircleTopKV2.cpp index f0677de86..49f858798 100644 --- a/compiler/luci/import/src/Nodes/CircleTopKV2.cpp +++ b/compiler/luci/import/src/Nodes/CircleTopKV2.cpp @@ -59,59 +59,24 @@ bool CircleTopKV2GraphBuilder::validate(const ValidateArgs &args) const * \- CircleTopKV2Out --- FullyConnected --- */ -void CircleTopKV2GraphBuilder::build(const circle::OperatorT &op, - GraphBuilderContext *context) const +CircleNode *CircleTopKV2GraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); - - auto graph = context->graph(); - - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - const auto &opcodes = context->reader()->opcodes(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); - - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } - - // Create CircleTopKV2 - auto node = graph->nodes()->create<CircleTopKV2>(); - node->input(input_nodes[0]); - node->k(input_nodes[1]); - - assert(outputs.size() == 2); - { - // Let's use name of output 0 as TopKV2 name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->op_version(opcodes[op.opcode_index].get()->version); - - // NOTE We don't set quantization for TopKV2 itself but to virtual outputs - } - - // Create virtual outputs of TopKV2 - for (size_t n = 0; n < outputs.size(); ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; - - auto *nodeout = graph->nodes()->create<CircleTopKV2Out>(); - copy_tensor_attributes(output_tensor, nodeout); - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); - - nodeout->input(node); - nodeout->index(n); - - context->nodefinder()->enroll(outputs[n], nodeout); - } + auto node = bna.context->graph()->nodes()->create<CircleTopKV2>(); + + node->input(bna.input_nodes[0]); + node->k(bna.input_nodes[1]); + + return node; +} + +CircleNode *CircleTopKV2GraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleTopKV2Out>(); + + nodeout->input(boa.node); + nodeout->index(boa.index); + + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleTranspose.cpp b/compiler/luci/import/src/Nodes/CircleTranspose.cpp index cc3153085..01095239e 100644 --- a/compiler/luci/import/src/Nodes/CircleTranspose.cpp +++ b/compiler/luci/import/src/Nodes/CircleTranspose.cpp @@ -25,13 +25,7 @@ namespace luci bool CircleTransposeGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 2) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 2); } CircleNode *CircleTransposeGraphBuilder::build_node(const circle::OperatorT &op, diff --git a/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp b/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp index c280faaf5..5a60e2f54 100644 --- a/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp +++ b/compiler/luci/import/src/Nodes/CircleTransposeConv.cpp @@ -61,16 +61,15 @@ CircleNode *CircleTransposeConvGraphBuilder::build_node(const circle::OperatorT node->filter(inputs.at(1)); node->outBackprop(inputs.at(2)); if (inputs.size() == 3) - node->bias(graph->nodes()->create<CircleOutputExclude>()); - else - node->bias(inputs.at(3)); - - if (auto bias = dynamic_cast<luci::CircleOutputExclude *>(node->bias())) { - // CircleOutputExclude doesn't need a type, but since all nodes must have a type, a dummy type - // is inserted. + auto *bias = graph->nodes()->create<CircleOutputExclude>(); + // CircleOutputExclude doesn't need a type, but since all nodes must have a type, + // a dummy type is inserted. bias->dtype(loco::DataType::FLOAT32); + node->bias(bias); } + else + node->bias(inputs.at(3)); const auto *options = op.builtin_options.AsTransposeConvOptions(); node->padding(luci_padding(options->padding)); diff --git a/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp b/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp index c41cf4def..d9cc3f8d0 100644 --- a/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp +++ b/compiler/luci/import/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp @@ -25,14 +25,11 @@ namespace luci bool CircleUnidirectionalSequenceLSTMGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 24) - return false; - - return true; + return GraphBuilder::validate(args, 24); } CircleNode *CircleUnidirectionalSequenceLSTMGraphBuilder::build_node( - const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const + const circle::OperatorT &op, const std::vector<CircleNode *> &inputs, loco::Graph *graph) const { auto *node = graph->nodes()->create<CircleUnidirectionalSequenceLSTM>(); node->input(inputs.at(0)); @@ -59,16 +56,6 @@ CircleNode *CircleUnidirectionalSequenceLSTMGraphBuilder::build_node( node->forget_layer_norm_coefficients(inputs.at(21)); // Optional node->cell_layer_norm_coefficients(inputs.at(22)); // Optional node->output_layer_norm_coefficients(inputs.at(23)); // Optional - const std::vector<int32_t> optionals = {1, 5, 9, 10, 11, 12, 16, 17, 20, 21, 22, 23}; - for (auto optional : optionals) - { - if (auto inp = dynamic_cast<luci::CircleOutputExclude *>(node->arg(optional))) - { - // CircleOutputExclude doesn't need a type, but since all nodes must have a type, a dummy type - // is inserted. - inp->dtype(loco::DataType::FLOAT32); - } - } const auto *options = op.builtin_options.AsUnidirectionalSequenceLSTMOptions(); node->fusedActivationFunction(luci_actfunc(options->fused_activation_function)); diff --git a/compiler/luci/import/src/Nodes/CircleUnique.cpp b/compiler/luci/import/src/Nodes/CircleUnique.cpp index 5e79a2920..f6914c24a 100644 --- a/compiler/luci/import/src/Nodes/CircleUnique.cpp +++ b/compiler/luci/import/src/Nodes/CircleUnique.cpp @@ -35,55 +35,26 @@ bool CircleUniqueGraphBuilder::validate(const ValidateArgs &args) const return true; } -void CircleUniqueGraphBuilder::build(const circle::OperatorT &op, - GraphBuilderContext *context) const +CircleNode *CircleUniqueGraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); + auto node = bna.context->graph()->nodes()->create<CircleUnique>(); - auto graph = context->graph(); + node->input(bna.input_nodes[0]); - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); + const auto *options = bna.op.builtin_options.AsUniqueOptions(); + node->idx_out_type(luci_datatype(options->idx_out_type)); - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } - - // Create CircleUnique - auto node = graph->nodes()->create<CircleUnique>(); - node->input(input_nodes[0]); - - const auto *options = op.builtin_options.AsUniqueOptions(); - node->output_type(luci_datatype(options->idx_out_type)); - - assert(int32_t(outputs.size()) == 2); - // Let's use name of output 0 as Unique name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - - // Create virtual outputs of Unique - for (int32_t n = 0; n < 2; ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; + return node; +} - auto *nodeout = graph->nodes()->create<CircleUniqueOut>(); - copy_tensor_attributes(output_tensor, nodeout); - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); +CircleNode *CircleUniqueGraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleUniqueOut>(); - nodeout->input(node); - nodeout->index(n); + nodeout->input(boa.node); + nodeout->index(boa.index); - context->nodefinder()->enroll(outputs[n], nodeout); - } + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleUnpack.cpp b/compiler/luci/import/src/Nodes/CircleUnpack.cpp index 9e7f3d3e1..9bfc76b57 100644 --- a/compiler/luci/import/src/Nodes/CircleUnpack.cpp +++ b/compiler/luci/import/src/Nodes/CircleUnpack.cpp @@ -88,64 +88,27 @@ bool CircleUnpackGraphBuilder::validate(const ValidateArgs &args) const * \- CircleUnpackOut --- FullyConnected --- */ -void CircleUnpackGraphBuilder::build(const circle::OperatorT &op, - GraphBuilderContext *context) const +CircleNode *CircleUnpackGraphBuilder::build_node(const BuildNodeArgs &bna) const { - assert(context != nullptr); + auto node = bna.context->graph()->nodes()->create<CircleUnpack>(); - auto graph = context->graph(); + node->value(bna.input_nodes[0]); - const std::vector<int32_t> &inputs = op.inputs; - const std::vector<int32_t> &outputs = op.outputs; - const auto &tensors = context->reader()->tensors(); - const auto &opcodes = context->reader()->opcodes(); - auto tensors_ptr = context->reader()->tensors_ptr(); - assert(tensors_ptr != nullptr); - - // NOTE Unpack has only one input so running a loop is not necessary - // This is provided as a reference for other Ops as a reference - std::vector<CircleNode *> input_nodes; - for (const int32_t input_tensor_index : inputs) - { - input_nodes.push_back(context->nodefinder()->node(input_tensor_index)); - } - - // Create CircleUnpack - CircleUnpack *node = graph->nodes()->create<CircleUnpack>(); - node->value(input_nodes[0]); - - const auto *options = op.builtin_options.AsUnpackOptions(); + const auto *options = bna.op.builtin_options.AsUnpackOptions(); node->num(options->num); node->axis(options->axis); - assert(outputs.size() > 0); - { - // Let's use name of output 0 as Unpack name - const circle::TensorT &output_tensor = *tensors[outputs[0]]; - node->name(tensor_name(output_tensor)); - node->op_version(opcodes[op.opcode_index].get()->version); - - // NOTE We don't set quantization for Unpack itself but to virtual outputs - } - - // Create virtual outputs of Unpack - for (int32_t n = 0; n < options->num; ++n) - { - const circle::TensorT &output_tensor = *tensors[outputs[n]]; + return node; +} - auto *nodeout = graph->nodes()->create<CircleUnpackOut>(); - copy_tensor_attributes(output_tensor, nodeout); - // mark shape_status - if (tensors_ptr->Get(outputs[n])->shape() == nullptr) - nodeout->shape_status(ShapeStatus::NOSHAPE); - else - nodeout->shape_status(ShapeStatus::VALID); +CircleNode *CircleUnpackGraphBuilder::build_out(const BuildOutArgs &boa) const +{ + auto *nodeout = boa.node->graph()->nodes()->create<CircleUnpackOut>(); - nodeout->input(node); - nodeout->index(n); + nodeout->input(boa.node); + nodeout->index(boa.index); - context->nodefinder()->enroll(outputs[n], nodeout); - } + return nodeout; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleWhere.cpp b/compiler/luci/import/src/Nodes/CircleWhere.cpp index f4c5f0c66..8e4f1a0c4 100644 --- a/compiler/luci/import/src/Nodes/CircleWhere.cpp +++ b/compiler/luci/import/src/Nodes/CircleWhere.cpp @@ -25,15 +25,11 @@ namespace luci bool CircleWhereGraphBuilder::validate(const ValidateArgs &args) const { - const auto &inputs = args.op.inputs; - const auto &outputs = args.op.outputs; - - if (inputs.size() != 1) - return false; - - if (outputs.size() != 1) + if (!GraphBuilder::validate(args, 1)) return false; + const auto &inputs = args.op.inputs; + const auto &outputs = args.op.outputs; const auto &tensors = args.reader.tensors(); const auto &tensor_condition = tensors.at(inputs.at(0)); const auto &tensor_out = tensors.at(outputs[0]); diff --git a/compiler/luci/import/src/Nodes/CircleWhile.cpp b/compiler/luci/import/src/Nodes/CircleWhile.cpp index aead25071..26147562f 100644 --- a/compiler/luci/import/src/Nodes/CircleWhile.cpp +++ b/compiler/luci/import/src/Nodes/CircleWhile.cpp @@ -58,7 +58,8 @@ bool CircleWhileGraphBuilder::validate(const ValidateArgs &args) const * \- CircleWhileOut --- Node --- */ -void CircleWhileGraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const +CircleNode *CircleWhileGraphBuilder::build(const circle::OperatorT &op, + GraphBuilderContext *context) const { assert(context != nullptr); @@ -118,6 +119,8 @@ void CircleWhileGraphBuilder::build(const circle::OperatorT &op, GraphBuilderCon context->nodefinder()->enroll(outputs[n], nodeout); } + + return node; } } // namespace luci diff --git a/compiler/luci/import/src/Nodes/CircleZerosLike.cpp b/compiler/luci/import/src/Nodes/CircleZerosLike.cpp index e60424def..ddb05e8a4 100644 --- a/compiler/luci/import/src/Nodes/CircleZerosLike.cpp +++ b/compiler/luci/import/src/Nodes/CircleZerosLike.cpp @@ -25,13 +25,7 @@ namespace luci bool CircleZerosLikeGraphBuilder::validate(const ValidateArgs &args) const { - if (args.op.inputs.size() != 1) - return false; - - if (args.op.outputs.size() != 1) - return false; - - return true; + return GraphBuilder::validate(args, 1); } CircleNode *CircleZerosLikeGraphBuilder::build_node(const circle::OperatorT &, diff --git a/compiler/luci/import/src/PostImport.cpp b/compiler/luci/import/src/PostImport.cpp index f436b48e8..63b16bb95 100644 --- a/compiler/luci/import/src/PostImport.cpp +++ b/compiler/luci/import/src/PostImport.cpp @@ -130,7 +130,10 @@ private: namespace { /** - * @brief ValidateNodeProp will validate inter graph connections for each Nodes + * @brief ValidateNodeProp will validate inter graph connections for each Nodes. + * @note In here, only loco::GraphInput and loco::GraphOutput are validated, + * since this class is for checking inter graph connections. + * CircleNodes such as CircleInput and CircleOutput will be validated at later steps. */ class ValidateNodeProp final : public luci::CircleNodeMutableVisitor<void> { @@ -172,9 +175,19 @@ public: auto then_graph_output = then_graph_outputs->at(then_out->index()); auto else_graph_output = else_graph_outputs->at(else_out->index()); - if (!(*then_graph_output->shape() == *else_graph_output->shape())) + if (then_graph_output->shape()->rank() != else_graph_output->shape()->rank()) { - INTERNAL_EXN_V("CircleIf THEN and ELSE Graph Output shape mismatch ", idx); + INTERNAL_EXN_V("CircleIf THEN and ELSE Graph Output rank mismatch ", idx); + } + for (uint32_t i = 0; i < then_graph_output->shape()->rank(); ++i) + { + if (then_graph_output->shape()->dim(i).known() && + else_graph_output->shape()->dim(i).known() && + then_graph_output->shape()->dim(i).value() != + else_graph_output->shape()->dim(i).value()) + { + INTERNAL_EXN_V("CircleIf THEN and ELSE Graph Output dimension mismatch ", idx); + } } if (then_graph_output->dtype() != else_graph_output->dtype()) { @@ -231,18 +244,20 @@ public: auto cond_graph_input = cond_graph_inputs->at(cond_in->index()); auto body_graph_input = body_graph_inputs->at(body_in->index()); - if ((cond_in->rank() != body_in->rank())) + if (cond_graph_input->shape()->rank() != body_graph_input->shape()->rank()) { - INTERNAL_EXN_V("CircleWhile COND input and BODY input shape mismatch ", idx); + INTERNAL_EXN_V("CircleWhile COND input and BODY input rank mismatch ", idx); } - if (cond_in->rank() > 0 && body_in->rank() > 0) + for (uint32_t i = 0; i < cond_graph_input->shape()->rank(); ++i) { - if (!(*cond_graph_input->shape() == *body_graph_input->shape())) + if (cond_graph_input->shape()->dim(i).known() && + body_graph_input->shape()->dim(i).known() && + cond_graph_input->shape()->dim(i).value() != body_graph_input->shape()->dim(i).value()) { - INTERNAL_EXN_V("CircleWhile COND input and BODY input shape mismatch ", idx); + INTERNAL_EXN_V("CircleWhile COND input and BODY input dimension mismatch ", idx); } } - if (cond_in->dtype() != body_in->dtype()) + if (cond_graph_input->dtype() != body_graph_input->dtype()) { INTERNAL_EXN_V("CircleWhile COND input and BODY input type mismatch ", idx); } @@ -257,18 +272,20 @@ public: auto cond_graph_input = cond_graph_inputs->at(cond_in->index()); auto body_graph_output = body_graph_outputs->at(body_out->index()); - if ((cond_in->rank() != body_out->rank())) + if (cond_graph_input->shape()->rank() != body_graph_output->shape()->rank()) { - INTERNAL_EXN_V("CircleWhile COND input and BODY output shape mismatch ", idx); + INTERNAL_EXN_V("CircleWhile COND input and BODY output rank mismatch ", idx); } - if (cond_in->rank() > 0 && body_out->rank() > 0) + for (uint32_t i = 0; i < cond_graph_input->shape()->rank(); ++i) { - if (!(*cond_graph_input->shape() == *body_graph_output->shape())) + if (cond_graph_input->shape()->dim(i).known() && + body_graph_output->shape()->dim(i).known() && + cond_graph_input->shape()->dim(i).value() != body_graph_output->shape()->dim(i).value()) { - INTERNAL_EXN_V("CircleWhile COND input and BODY output shape mismatch ", idx); + INTERNAL_EXN_V("CircleWhile COND input and BODY output dimension mismatch ", idx); } } - if (cond_in->dtype() != body_out->dtype()) + if (cond_graph_input->dtype() != body_graph_output->dtype()) { INTERNAL_EXN_V("CircleWhile COND input and BODY output type mismatch ", idx); } diff --git a/compiler/luci/lang/CMakeLists.txt b/compiler/luci/lang/CMakeLists.txt index 32d0a890d..c618fdd6f 100644 --- a/compiler/luci/lang/CMakeLists.txt +++ b/compiler/luci/lang/CMakeLists.txt @@ -7,6 +7,7 @@ target_include_directories(luci_lang PRIVATE src) target_include_directories(luci_lang PUBLIC include) target_link_libraries(luci_lang PUBLIC loco) target_link_libraries(luci_lang PUBLIC oops) +target_link_libraries(luci_lang PUBLIC nncc_coverage) target_link_libraries(luci_lang PRIVATE logo) target_link_libraries(luci_lang PRIVATE nncc_common) diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h index e6410d154..edec9d18b 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h +++ b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h @@ -20,7 +20,6 @@ #include <loco/IR/Dialect.h> #include <loco/IR/Node.h> #include <loco/IR/NodeMixins.h> -#include <luci/IR/CircleShapeSignature.h> #include <luci/IR/PropertyShapeStatus.h> #include "CircleOpcode.h" @@ -62,9 +61,6 @@ struct CircleNode : public loco::Node, _sparsityparam = std::move(sparsityparam); } - const ShapeSignature &shape_signature(void) const { return _shape_signature; } - void shape_signature(const ShapeSignature &ss) { _shape_signature = ss; } - ShapeStatus shape_status(void) const { return _shape_status; } void shape_status(ShapeStatus ss) { _shape_status = ss; } @@ -75,7 +71,6 @@ private: NodeName _name; std::unique_ptr<CircleQuantParam> _quantparam; std::unique_ptr<SparsityParam> _sparsityparam; - ShapeSignature _shape_signature; ShapeStatus _shape_status{ShapeStatus::UNDEFINED}; int32_t _op_version = 1; }; diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h index a6b9488db..4b3178b9b 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h +++ b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h @@ -34,8 +34,10 @@ template <typename T> T CircleNode::accept(CircleNodeVisitorBase<T> *v) const \ case CircleOpcode::OPCODE: \ return v->visit(dynamic_cast<const CLASS *>(this)); +#define CIRCLE_VNODE CIRCLE_NODE #include "CircleNodes.lst" +#undef CIRCLE_VNODE #undef CIRCLE_NODE default: @@ -53,8 +55,10 @@ template <typename T> T CircleNode::accept(CircleNodeMutableVisitorBase<T> *v) \ case CircleOpcode::OPCODE: \ return v->visit(dynamic_cast<CLASS *>(this)); +#define CIRCLE_VNODE CIRCLE_NODE #include "CircleNodes.lst" +#undef CIRCLE_VNODE #undef CIRCLE_NODE default: diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeMixins.h b/compiler/luci/lang/include/luci/IR/CircleNodeMixins.h new file mode 100644 index 000000000..3f8ab7d61 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/CircleNodeMixins.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 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 __LUCI_IR_CIRCLE_NODE_MIXINS_H__ +#define __LUCI_IR_CIRCLE_NODE_MIXINS_H__ + +#include "luci/IR/AttrFusedActFunc.h" + +#include <loco/IR/Node.h> +#include <loco/IR/NodeMixins.h> + +#include <vector> + +namespace luci +{ + +/// @brief enumeration of mixin class +enum class CircleNodeTrait +{ + FusedActFunc, + Bias +}; + +template <CircleNodeTrait T> class CircleNodeMixin; + +template <> class CircleNodeMixin<CircleNodeTrait::FusedActFunc> +{ +public: + CircleNodeMixin() = default; + +public: + FusedActFunc fusedActivationFunction() const { return _fused_act_fun; } + void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; } + +private: + FusedActFunc _fused_act_fun = FusedActFunc::UNDEFINED; +}; + +/** + * @brief Mixin class for nodes that has a bias input + */ +template <> class CircleNodeMixin<CircleNodeTrait::Bias> +{ +public: + CircleNodeMixin() = default; + +public: + virtual loco::Node *bias(void) const = 0; /// @brief get the input for bias. + virtual void bias(loco::Node *node) = 0; /// @brief set the input for bias. +}; + +/** + * @brief Nodes with the fixed number of inputs + * + * TODO Deprecated this class, and use loco::FixedArity instead + */ +template <unsigned N, typename Base> class FixedArityNode : public Base +{ +public: + FixedArityNode() + { + _args.resize(N); + for (uint32_t n = 0; n < N; ++n) + { + _args[n] = std::make_unique<loco::Use>(this); + } + } + + virtual ~FixedArityNode() = default; + +public: + unsigned arity(void) const final { return N; } + + loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); } + + void drop(void) final + { + for (uint32_t n = 0; n < N; ++n) + { + _args.at(n)->node(nullptr); + } + } + +protected: + // This API allows inherited classes to access "_args" field. + loco::Use *at(unsigned n) const { return _args.at(n).get(); } + +private: + std::vector<std::unique_ptr<loco::Use>> _args{}; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_NODE_MIXINS_H__ diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h index 43339fe84..599e4bcd9 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h +++ b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h @@ -33,8 +33,10 @@ template <typename T> struct CircleNodeVisitorBase virtual ~CircleNodeVisitorBase() = default; #define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(const CIRCLE_CLASS *) = 0; +#define CIRCLE_VNODE CIRCLE_NODE #include "CircleNodes.lst" +#undef CIRCLE_VNODE #undef CIRCLE_NODE }; @@ -44,9 +46,11 @@ template <typename T> struct CircleNodeVisitor : public CircleNodeVisitorBase<T> #define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ virtual T visit(const CIRCLE_CLASS *node) { return visit(static_cast<const CircleNode *>(node)); } +#define CIRCLE_VNODE CIRCLE_NODE #include "CircleNodes.lst" +#undef CIRCLE_VNODE #undef CIRCLE_NODE /// @brief Default fallback @@ -61,9 +65,11 @@ template <typename T> struct CircleNodeMutableVisitorBase virtual ~CircleNodeMutableVisitorBase() = default; #define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(CIRCLE_CLASS *) = 0; +#define CIRCLE_VNODE CIRCLE_NODE #include "CircleNodes.lst" +#undef CIRCLE_VNODE #undef CIRCLE_NODE }; @@ -73,9 +79,11 @@ template <typename T> struct CircleNodeMutableVisitor : public CircleNodeMutable #define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ virtual T visit(CIRCLE_CLASS *node) { return visit(static_cast<CircleNode *>(node)); } +#define CIRCLE_VNODE CIRCLE_NODE #include "CircleNodes.lst" +#undef CIRCLE_VNODE #undef CIRCLE_NODE /// @brief Default fallback diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h index fde0b612b..69a82a7b9 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.h +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h @@ -25,6 +25,7 @@ #include "Nodes/CircleAveragePool2D.h" #include "Nodes/CircleBatchMatMul.h" #include "Nodes/CircleBatchToSpaceND.h" +#include "Nodes/CircleBidirectionalSequenceLSTM.h" #include "Nodes/CircleCast.h" #include "Nodes/CircleCeil.h" #include "Nodes/CircleConcatenation.h" @@ -40,6 +41,7 @@ #include "Nodes/CircleEqual.h" #include "Nodes/CircleExp.h" #include "Nodes/CircleExpandDims.h" +#include "Nodes/CircleFakeQuant.h" #include "Nodes/CircleFill.h" #include "Nodes/CircleFloor.h" #include "Nodes/CircleFloorDiv.h" @@ -134,6 +136,7 @@ // Virtual nodes #include "Nodes/CircleInput.h" #include "Nodes/CircleOutput.h" +#include "Nodes/CircleBidirectionalSequenceLSTMOut.h" #include "Nodes/CircleCustomOut.h" #include "Nodes/CircleIfOut.h" #include "Nodes/CircleNonMaxSuppressionV4Out.h" @@ -150,15 +153,6 @@ namespace luci { -/** - * @brief Set both CircleReshape's 2nd input as CircleConst, and newShape attribute - * with same value - * @note Shape inference for TFLReshape forces them to be same - * - * TODO find better place for this helper - */ -void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size); - /// @brief Link GraphOutput with CircleOutput node void link(loco::GraphOutput *, CircleOutput *); diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst index b9d545893..b93fdc89d 100644 --- a/compiler/luci/lang/include/luci/IR/CircleNodes.lst +++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst @@ -2,6 +2,10 @@ #error "Define CIRCLE_NODE" #endif // CIRCLE_NODE +#ifndef CIRCLE_VNODE +#error "Define CIRCLE_VNODE" +#endif // CIRCLE_VNODE + // // PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER // @@ -18,7 +22,8 @@ CIRCLE_NODE(ARG_MAX, luci::CircleArgMax) CIRCLE_NODE(ARG_MIN, luci::CircleArgMin) CIRCLE_NODE(AVERAGE_POOL_2D, luci::CircleAveragePool2D) CIRCLE_NODE(BATCH_TO_SPACE_ND, luci::CircleBatchToSpaceND) -CIRCLE_NODE(BATCHMATMUL, luci::CircleBatchMatMul) +CIRCLE_NODE(BATCH_MATMUL, luci::CircleBatchMatMul) +CIRCLE_NODE(BIDIRECTIONAL_SEQUENCE_LSTM, luci::CircleBidirectionalSequenceLSTM) CIRCLE_NODE(CAST, luci::CircleCast) CIRCLE_NODE(CEIL, luci::CircleCeil) CIRCLE_NODE(CONCATENATION, luci::CircleConcatenation) @@ -33,6 +38,7 @@ CIRCLE_NODE(ELU, luci::CircleElu) CIRCLE_NODE(EQUAL, luci::CircleEqual) CIRCLE_NODE(EXP, luci::CircleExp) CIRCLE_NODE(EXPAND_DIMS, luci::CircleExpandDims) +CIRCLE_NODE(FAKE_QUANT, luci::CircleFakeQuant) CIRCLE_NODE(FILL, luci::CircleFill) CIRCLE_NODE(FLOOR, luci::CircleFloor) CIRCLE_NODE(FLOOR_DIV, luci::CircleFloorDiv) @@ -125,18 +131,19 @@ CIRCLE_NODE(BCQ_FULLY_CONNECTED, luci::CircleBCQFullyConnected) CIRCLE_NODE(BCQ_GATHER, luci::CircleBCQGather) CIRCLE_NODE(INSTANCE_NORM, luci::CircleInstanceNorm) // Virtual node(s) -CIRCLE_NODE(CIRCLECONST, luci::CircleConst) -CIRCLE_NODE(CIRCLEINPUT, luci::CircleInput) -CIRCLE_NODE(CIRCLEOUTPUT, luci::CircleOutput) -CIRCLE_NODE(CIRCLEOUTPUTDUMMY, luci::CircleOutputDummy) -CIRCLE_NODE(CIRCLEOUTPUTEXCLUDE, luci::CircleOutputExclude) -CIRCLE_NODE(CIRCLECUSTOMOUT, luci::CircleCustomOut) -CIRCLE_NODE(CIRCLEIFOUT, luci::CircleIfOut) -CIRCLE_NODE(CIRCLENONMAXSUPPRESSIONV4OUT, luci::CircleNonMaxSuppressionV4Out) -CIRCLE_NODE(CIRCLENONMAXSUPPRESSIONV5OUT, luci::CircleNonMaxSuppressionV5Out) -CIRCLE_NODE(CIRCLESPLITOUT, luci::CircleSplitOut) -CIRCLE_NODE(CIRCLESPLITVOUT, luci::CircleSplitVOut) -CIRCLE_NODE(CIRCLETOPKV2OUT, luci::CircleTopKV2Out) -CIRCLE_NODE(CIRCLEUNIQUEOUT, luci::CircleUniqueOut) -CIRCLE_NODE(CIRCLEUNPACKOUT, luci::CircleUnpackOut) -CIRCLE_NODE(CIRCLEWHILEOUT, luci::CircleWhileOut) +CIRCLE_VNODE(CIRCLEBIDIRECTIONAL_SEQUENCE_LSTM_OUT, luci::CircleBidirectionalSequenceLSTMOut) +CIRCLE_VNODE(CIRCLECONST, luci::CircleConst) +CIRCLE_VNODE(CIRCLEINPUT, luci::CircleInput) +CIRCLE_VNODE(CIRCLEOUTPUT, luci::CircleOutput) +CIRCLE_VNODE(CIRCLEOUTPUTDUMMY, luci::CircleOutputDummy) +CIRCLE_VNODE(CIRCLEOUTPUTEXCLUDE, luci::CircleOutputExclude) +CIRCLE_VNODE(CIRCLECUSTOMOUT, luci::CircleCustomOut) +CIRCLE_VNODE(CIRCLEIFOUT, luci::CircleIfOut) +CIRCLE_VNODE(CIRCLENONMAXSUPPRESSIONV4OUT, luci::CircleNonMaxSuppressionV4Out) +CIRCLE_VNODE(CIRCLENONMAXSUPPRESSIONV5OUT, luci::CircleNonMaxSuppressionV5Out) +CIRCLE_VNODE(CIRCLESPLITOUT, luci::CircleSplitOut) +CIRCLE_VNODE(CIRCLESPLITVOUT, luci::CircleSplitVOut) +CIRCLE_VNODE(CIRCLETOPKV2OUT, luci::CircleTopKV2Out) +CIRCLE_VNODE(CIRCLEUNIQUEOUT, luci::CircleUniqueOut) +CIRCLE_VNODE(CIRCLEUNPACKOUT, luci::CircleUnpackOut) +CIRCLE_VNODE(CIRCLEWHILEOUT, luci::CircleWhileOut) diff --git a/compiler/luci/lang/include/luci/IR/CircleOpcode.h b/compiler/luci/lang/include/luci/IR/CircleOpcode.h index 703b70da2..be3069f94 100644 --- a/compiler/luci/lang/include/luci/IR/CircleOpcode.h +++ b/compiler/luci/lang/include/luci/IR/CircleOpcode.h @@ -23,7 +23,9 @@ namespace luci enum class CircleOpcode { #define CIRCLE_NODE(OPCODE, CLASS) OPCODE, +#define CIRCLE_VNODE CIRCLE_NODE #include "CircleNodes.lst" +#undef CIRCLE_VNODE #undef CIRCLE_NODE }; diff --git a/compiler/luci/lang/include/luci/IR/CircleShapeSignature.h b/compiler/luci/lang/include/luci/IR/CircleShapeSignature.h deleted file mode 100644 index 18a260486..000000000 --- a/compiler/luci/lang/include/luci/IR/CircleShapeSignature.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020 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 __LUCI_IR_SHAPE_SIGNATURE_H__ -#define __LUCI_IR_SHAPE_SIGNATURE_H__ - -#include <stdint.h> -#include <vector> - -namespace luci -{ - -class ShapeSignature -{ -public: - ShapeSignature() = default; - - ShapeSignature(const std::vector<int32_t> &shape_signature) - { - _shape_signature = shape_signature; - } - -public: - const std::vector<int32_t> &as_vector() const { return _shape_signature; } - - int32_t dim(uint32_t d) const { return _shape_signature.at(d); } - int32_t &dim(uint32_t d) { return _shape_signature.at(d); } - - uint32_t rank(void) const { return _shape_signature.size(); } - void rank(uint32_t rank) { _shape_signature.resize(rank); } - -private: - std::vector<int32_t> _shape_signature{}; -}; - -bool operator==(const ShapeSignature &lhs, const ShapeSignature &rhs); - -} // namespace luci - -#endif // __LUCI_IR_SHAPE_SIGNATURE_H__ diff --git a/compiler/luci/lang/src/DeadNodeQueryService.h b/compiler/luci/lang/include/luci/IR/DeadNodeQueryService.h index d10696667..d10696667 100644 --- a/compiler/luci/lang/src/DeadNodeQueryService.h +++ b/compiler/luci/lang/include/luci/IR/DeadNodeQueryService.h diff --git a/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h index c1bb0db11..2078495c6 100644 --- a/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h +++ b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h @@ -17,90 +17,16 @@ #ifndef __LUCI_IR_LUCINODEMIXINS_H__ #define __LUCI_IR_LUCINODEMIXINS_H__ -#include "luci/IR/AttrFusedActFunc.h" +// TODO remove this file after LuciNodeTrait and LuciNodeMixin are not used in backend -#include <loco/IR/Node.h> -#include <loco/IR/NodeMixins.h> - -#include <vector> +#include "luci/IR/CircleNodeMixins.h" namespace luci { -/// @brief enumeration of mixin class -enum class LuciNodeTrait -{ - FusedActFunc, - Bias -}; - -template <LuciNodeTrait T> class LuciNodeMixin; - -template <> class LuciNodeMixin<LuciNodeTrait::FusedActFunc> -{ -public: - LuciNodeMixin() = default; - -public: - FusedActFunc fusedActivationFunction() const { return _fused_act_fun; } - void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; } - -private: - FusedActFunc _fused_act_fun = FusedActFunc::UNDEFINED; -}; - -/** - * @brief Mixin class for nodes that has a bias input - */ -template <> class LuciNodeMixin<LuciNodeTrait::Bias> -{ -public: - LuciNodeMixin() = default; - -public: - virtual loco::Node *bias(void) const = 0; /// @brief get the input for bias. - virtual void bias(loco::Node *node) = 0; /// @brief set the input for bias. -}; - -/** - * @brief Nodes with the fixed number of inputs - * - * TODO Deprecated this class, and use loco::FixedArity instead - */ -template <unsigned N, typename Base> class FixedArityNode : public Base -{ -public: - FixedArityNode() - { - _args.resize(N); - for (uint32_t n = 0; n < N; ++n) - { - _args[n] = std::make_unique<loco::Use>(this); - } - } - - virtual ~FixedArityNode() = default; - -public: - unsigned arity(void) const final { return N; } - - loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); } - - void drop(void) final - { - for (uint32_t n = 0; n < N; ++n) - { - _args.at(n)->node(nullptr); - } - } - -protected: - // This API allows inherited classes to access "_args" field. - loco::Use *at(unsigned n) const { return _args.at(n).get(); } +using LuciNodeTrait = CircleNodeTrait; -private: - std::vector<std::unique_ptr<loco::Use>> _args{}; -}; +template <LuciNodeTrait T> using LuciNodeMixin = CircleNodeMixin<T>; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h index 45dba15bf..7a73f37cd 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h index f26eccd1a..92563de4c 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,7 +30,7 @@ namespace luci * @brief ADD in Circle */ class CircleAdd final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ADD>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: loco::Node *x(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h index dbc4b2b3a..c1e4631e4 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h index 8cb561983..b4d026201 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMin.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h index 0b43b40c8..4aa45c2d8 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h @@ -24,7 +24,7 @@ #include "luci/IR/AttrPadding.h" #include "luci/IR/AttrStride.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -33,16 +33,14 @@ namespace luci * @brief AVERAGE_POOL_2D in Circle */ class CircleAveragePool2D final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::AVERAGE_POOL_2D>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::AVERAGE_POOL_2D>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: - CircleAveragePool2D() : _padding(Padding::UNDEFINED) { /* empty */} - -public: loco::Node *value(void) const { return at(0)->node(); } void value(loco::Node *node) { at(0)->node(node); } +public: Padding padding() const { return _padding; } void padding(Padding padding) { _padding = padding; } @@ -53,7 +51,7 @@ public: Stride *stride(void) { return &_stride; } private: - Padding _padding; + Padding _padding{Padding::UNDEFINED}; Stride _stride; Filter _filter; }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h index 7d12d593a..4c164ebca 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQFullyConnected.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,9 +30,9 @@ namespace luci * @brief BCQ_FULLY_CONNECTED in Circle */ class CircleBCQFullyConnected final - : public FixedArityNode<5, CircleNodeImpl<CircleOpcode::BCQ_FULLY_CONNECTED>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc>, - public LuciNodeMixin<LuciNodeTrait::Bias> + : public FixedArityNode<5, CircleNodeImpl<CircleOpcode::BCQ_FULLY_CONNECTED>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc>, + public CircleNodeMixin<CircleNodeTrait::Bias> { public: loco::Node *input(void) const { return at(0)->node(); } @@ -58,7 +58,7 @@ public: } private: - int32_t _weights_hidden_size = 0; + int32_t _weights_hidden_size{0}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h index f7638261d..1a0bf4f19 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBCQGather.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -51,8 +51,8 @@ public: void input_hidden_size(int32_t input_hidden_size) { _input_hidden_size = input_hidden_size; } private: - int32_t _axis = 0; - int32_t _input_hidden_size = 0; + int32_t _axis{0}; + int32_t _input_hidden_size{0}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h index 19999924e..864b033ed 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchMatMul.h @@ -20,15 +20,15 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { /** - * @brief BATCHMATMUL in Circle + * @brief BATCH_MATMUL in Circle */ -class CircleBatchMatMul final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::BATCHMATMUL>> +class CircleBatchMatMul final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::BATCH_MATMUL>> { public: loco::Node *x(void) const { return at(0)->node(); } @@ -45,8 +45,8 @@ public: void adj_y(bool arg) { _adj_y = arg; } private: - bool _adj_x = false; - bool _adj_y = false; + bool _adj_x{false}; + bool _adj_y{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h index 67c0a2102..80fa53b8e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief BATCH_TO_SPACE_ND in Circle */ class CircleBatchToSpaceND final - : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::BATCH_TO_SPACE_ND>> + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::BATCH_TO_SPACE_ND>> { public: loco::Node *input(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBidirectionalSequenceLSTM.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBidirectionalSequenceLSTM.h new file mode 100644 index 000000000..d16281b69 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBidirectionalSequenceLSTM.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 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 __LUCI_IR_CIRCLEBIDIRECTIONALSEQUENCE_LSTM_H__ +#define __LUCI_IR_CIRCLEBIDIRECTIONALSEQUENCE_LSTM_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/AttrFusedActFunc.h" +#include "luci/IR/CircleNodeMixins.h" + +namespace luci +{ + +/** + * @brief BIDIRECTIONAL_SEQUENCE_LSTM in Circle + */ +class CircleBidirectionalSequenceLSTM final + : public FixedArityNode<48, CircleNodeImpl<CircleOpcode::BIDIRECTIONAL_SEQUENCE_LSTM>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + + loco::Node *fw_input_to_input_weights(void) const { return at(1)->node(); } + void fw_input_to_input_weights(loco::Node *node) { at(1)->node(node); } + loco::Node *fw_input_to_forget_weights(void) const { return at(2)->node(); } + void fw_input_to_forget_weights(loco::Node *node) { at(2)->node(node); } + loco::Node *fw_input_to_cell_weights(void) const { return at(3)->node(); } + void fw_input_to_cell_weights(loco::Node *node) { at(3)->node(node); } + loco::Node *fw_input_to_output_weights(void) const { return at(4)->node(); } + void fw_input_to_output_weights(loco::Node *node) { at(4)->node(node); } + + loco::Node *fw_recurrent_to_input_weights(void) const { return at(5)->node(); } + void fw_recurrent_to_input_weights(loco::Node *node) { at(5)->node(node); } + loco::Node *fw_recurrent_to_forget_weights(void) const { return at(6)->node(); } + void fw_recurrent_to_forget_weights(loco::Node *node) { at(6)->node(node); } + loco::Node *fw_recurrent_to_cell_weights(void) const { return at(7)->node(); } + void fw_recurrent_to_cell_weights(loco::Node *node) { at(7)->node(node); } + loco::Node *fw_recurrent_to_output_weights(void) const { return at(8)->node(); } + void fw_recurrent_to_output_weights(loco::Node *node) { at(8)->node(node); } + + loco::Node *fw_cell_to_input_weights(void) const { return at(9)->node(); } + void fw_cell_to_input_weights(loco::Node *node) { at(9)->node(node); } + loco::Node *fw_cell_to_forget_weights(void) const { return at(10)->node(); } + void fw_cell_to_forget_weights(loco::Node *node) { at(10)->node(node); } + loco::Node *fw_cell_to_output_weights(void) const { return at(11)->node(); } + void fw_cell_to_output_weights(loco::Node *node) { at(11)->node(node); } + + loco::Node *fw_input_gate_bias(void) const { return at(12)->node(); } + void fw_input_gate_bias(loco::Node *node) { at(12)->node(node); } + loco::Node *fw_forget_gate_bias(void) const { return at(13)->node(); } + void fw_forget_gate_bias(loco::Node *node) { at(13)->node(node); } + loco::Node *fw_cell_gate_bias(void) const { return at(14)->node(); } + void fw_cell_gate_bias(loco::Node *node) { at(14)->node(node); } + loco::Node *fw_output_gate_bias(void) const { return at(15)->node(); } + void fw_output_gate_bias(loco::Node *node) { at(15)->node(node); } + + loco::Node *fw_projection_weights(void) const { return at(16)->node(); } + void fw_projection_weights(loco::Node *node) { at(16)->node(node); } + loco::Node *fw_projection_bias(void) const { return at(17)->node(); } + void fw_projection_bias(loco::Node *node) { at(17)->node(node); } + + loco::Node *bw_input_to_input_weights(void) const { return at(18)->node(); } + void bw_input_to_input_weights(loco::Node *node) { at(18)->node(node); } + loco::Node *bw_input_to_forget_weights(void) const { return at(19)->node(); } + void bw_input_to_forget_weights(loco::Node *node) { at(19)->node(node); } + loco::Node *bw_input_to_cell_weights(void) const { return at(20)->node(); } + void bw_input_to_cell_weights(loco::Node *node) { at(20)->node(node); } + loco::Node *bw_input_to_output_weights(void) const { return at(21)->node(); } + void bw_input_to_output_weights(loco::Node *node) { at(21)->node(node); } + + loco::Node *bw_recurrent_to_input_weights(void) const { return at(22)->node(); } + void bw_recurrent_to_input_weights(loco::Node *node) { at(22)->node(node); } + loco::Node *bw_recurrent_to_forget_weights(void) const { return at(23)->node(); } + void bw_recurrent_to_forget_weights(loco::Node *node) { at(23)->node(node); } + loco::Node *bw_recurrent_to_cell_weights(void) const { return at(24)->node(); } + void bw_recurrent_to_cell_weights(loco::Node *node) { at(24)->node(node); } + loco::Node *bw_recurrent_to_output_weights(void) const { return at(25)->node(); } + void bw_recurrent_to_output_weights(loco::Node *node) { at(25)->node(node); } + + loco::Node *bw_cell_to_input_weights(void) const { return at(26)->node(); } + void bw_cell_to_input_weights(loco::Node *node) { at(26)->node(node); } + loco::Node *bw_cell_to_forget_weights(void) const { return at(27)->node(); } + void bw_cell_to_forget_weights(loco::Node *node) { at(27)->node(node); } + loco::Node *bw_cell_to_output_weights(void) const { return at(28)->node(); } + void bw_cell_to_output_weights(loco::Node *node) { at(28)->node(node); } + + loco::Node *bw_input_gate_bias(void) const { return at(29)->node(); } + void bw_input_gate_bias(loco::Node *node) { at(29)->node(node); } + loco::Node *bw_forget_gate_bias(void) const { return at(30)->node(); } + void bw_forget_gate_bias(loco::Node *node) { at(30)->node(node); } + loco::Node *bw_cell_gate_bias(void) const { return at(31)->node(); } + void bw_cell_gate_bias(loco::Node *node) { at(31)->node(node); } + loco::Node *bw_output_gate_bias(void) const { return at(32)->node(); } + void bw_output_gate_bias(loco::Node *node) { at(32)->node(node); } + + loco::Node *bw_projection_weights(void) const { return at(33)->node(); } + void bw_projection_weights(loco::Node *node) { at(33)->node(node); } + loco::Node *bw_projection_bias(void) const { return at(34)->node(); } + void bw_projection_bias(loco::Node *node) { at(34)->node(node); } + + loco::Node *fw_activation_state(void) const { return at(35)->node(); } + void fw_activation_state(loco::Node *node) { at(35)->node(node); } + loco::Node *fw_cell_state(void) const { return at(36)->node(); } + void fw_cell_state(loco::Node *node) { at(36)->node(node); } + + loco::Node *bw_activation_state(void) const { return at(37)->node(); } + void bw_activation_state(loco::Node *node) { at(37)->node(node); } + loco::Node *bw_cell_state(void) const { return at(38)->node(); } + void bw_cell_state(loco::Node *node) { at(38)->node(node); } + + loco::Node *auxillary_input(void) const { return at(39)->node(); } + void auxillary_input(loco::Node *node) { at(39)->node(node); } + loco::Node *fw_auxillary_input_to_input_weights(void) const { return at(40)->node(); } + void fw_auxillary_input_to_input_weights(loco::Node *node) { at(40)->node(node); } + loco::Node *fw_auxillary_input_to_forget_weights(void) const { return at(41)->node(); } + void fw_auxillary_input_to_forget_weights(loco::Node *node) { at(41)->node(node); } + loco::Node *fw_auxillary_input_to_cell_weights(void) const { return at(42)->node(); } + void fw_auxillary_input_to_cell_weights(loco::Node *node) { at(42)->node(node); } + loco::Node *fw_auxillary_input_to_output_weights(void) const { return at(43)->node(); } + void fw_auxillary_input_to_output_weights(loco::Node *node) { at(43)->node(node); } + loco::Node *bw_auxillary_input_to_input_weights(void) const { return at(44)->node(); } + void bw_auxillary_input_to_input_weights(loco::Node *node) { at(44)->node(node); } + loco::Node *bw_auxillary_input_to_forget_weights(void) const { return at(45)->node(); } + void bw_auxillary_input_to_forget_weights(loco::Node *node) { at(45)->node(node); } + loco::Node *bw_auxillary_input_to_cell_weights(void) const { return at(46)->node(); } + void bw_auxillary_input_to_cell_weights(loco::Node *node) { at(46)->node(node); } + loco::Node *bw_auxillary_input_to_output_weights(void) const { return at(47)->node(); } + void bw_auxillary_input_to_output_weights(loco::Node *node) { at(47)->node(node); } + +public: + float cell_clip(void) const { return _cell_clip; } + void cell_clip(float cell_clip) { _cell_clip = cell_clip; } + float proj_clip(void) const { return _proj_clip; } + void proj_clip(float proj_clip) { _proj_clip = proj_clip; } + bool merge_outputs(void) const { return _merge_outputs; } + void merge_outputs(bool merge_outputs) { _merge_outputs = merge_outputs; } + bool time_major(void) const { return _time_major; } + void time_major(bool time_major) { _time_major = time_major; } + bool asymmetric_quantize_inputs(void) const { return _asymmetric_quantize_inputs; } + void asymmetric_quantize_inputs(bool asymmetric_quantize_inputs) + { + _asymmetric_quantize_inputs = asymmetric_quantize_inputs; + } + +private: + float _cell_clip{0.0f}; + float _proj_clip{0.0f}; + bool _merge_outputs{false}; + bool _time_major{false}; + bool _asymmetric_quantize_inputs{false}; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEBIDIRECTIONALSEQUENCE_LSTM_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBidirectionalSequenceLSTMOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBidirectionalSequenceLSTMOut.h new file mode 100644 index 000000000..fb2eb0831 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBidirectionalSequenceLSTMOut.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 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 __LUCI_IR_CIRCLE_BIDIRECTIONAL_SEQUENCE_LSTM_OUT_H__ +#define __LUCI_IR_CIRCLE_BIDIRECTIONAL_SEQUENCE_LSTM_OUT_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/CircleNodeMixins.h" + +namespace luci +{ + +/** + * @brief Virtual CIRCLEBIDIRECTIONAL_SEQUENCE_LSTM_OUT in Circle + */ +class CircleBidirectionalSequenceLSTMOut final + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEBIDIRECTIONAL_SEQUENCE_LSTM_OUT>> +{ +public: + loco::Node *input(void) const { return at(0)->node(); } + void input(loco::Node *node) { at(0)->node(node); } + +public: + int32_t index(void) const { return _index; } + void index(int32_t index) { _index = index; } + +private: + int32_t _index{-1}; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLE_BIDIRECTIONAL_SEQUENCE_LSTM_OUT_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h index 9a89d0b2b..0b793607f 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCast.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h index 8a8715dcf..3d7a7ebc7 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCeil.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h index dea1a4613..2746a0a2e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" #include "luci/IR/VariadicArityNode.h" #include <cassert> @@ -33,12 +33,12 @@ namespace luci * @brief CONCATENATION in Circle */ class CircleConcatenation final - : public VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + : public VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: CircleConcatenation(uint32_t arity) - : VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>(arity) + : VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>(arity) { // TODO Support when arity is 0 assert(arity >= 1); diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h index 250282049..e44363d14 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" #include <loco/IR/DataTypeTraits.h> @@ -34,9 +34,6 @@ namespace luci class CircleConst final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLECONST>> { public: - CircleConst() = default; - -public: template <loco::DataType DT> uint32_t size(void) const; template <loco::DataType DT> void size(uint32_t size); template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h index 13657cee4..7c390940e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h @@ -24,7 +24,7 @@ #include "luci/IR/AttrStride.h" #include "luci/IR/AttrDilation.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -33,8 +33,8 @@ namespace luci * @brief CONV_2D in Circle */ class CircleConv2D final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::CONV_2D>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc>, - public LuciNodeMixin<LuciNodeTrait::Bias> + public CircleNodeMixin<CircleNodeTrait::FusedActFunc>, + public CircleNodeMixin<CircleNodeTrait::Bias> { public: loco::Node *input(void) const { return at(0)->node(); } @@ -57,7 +57,7 @@ public: Dilation *dilation(void) { return &_dilation; } private: - Padding _padding = Padding::UNDEFINED; + Padding _padding{Padding::UNDEFINED}; Stride _stride; Dilation _dilation; }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h index 07ced620a..cff04906d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h index 6c722b766..b21cc679f 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustom.h @@ -29,19 +29,23 @@ namespace luci class CircleCustom final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::CUSTOM>> { public: - CircleCustom(uint32_t arity) : VariadicArityNode<CircleNodeImpl<CircleOpcode::CUSTOM>>(arity) + CircleCustom(uint32_t arity, uint32_t out) + : VariadicArityNode<CircleNodeImpl<CircleOpcode::CUSTOM>>(arity), _output_count(out) { // TODO Support when arity is 0 assert(arity >= 1); + assert(out > 0); } public: uint32_t numInputs(void) const { return arity(); } + uint32_t numOutputs(void) const { return _output_count; } public: Node *inputs(uint32_t index) const { return at(index)->node(); } void inputs(uint32_t index, Node *node) { at(index)->node(node); } +public: const std::vector<uint8_t> &custom_options(void) const { return _custom_options; } void custom_options(const std::vector<uint8_t> &custom_options) { @@ -54,6 +58,7 @@ public: private: std::vector<uint8_t> _custom_options; std::string _custom_code; + uint32_t _output_count{0}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h index 36b8e4aed..91a89c151 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCustomOut.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief Virtual CIRCLECUSTOMOUT in Circle */ class CircleCustomOut final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLECUSTOMOUT>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLECUSTOMOUT>> { public: - CircleCustomOut() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h index e19282b97..85b567fb7 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthToSpace.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,18 +29,18 @@ namespace luci * @brief DEPTH_TO_SPACE in Circle */ class CircleDepthToSpace final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::DEPTH_TO_SPACE>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::DEPTH_TO_SPACE>> { public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } public: - int block_size(void) const { return _block_size; } - void block_size(int block_size) { _block_size = block_size; } + int32_t block_size(void) const { return _block_size; } + void block_size(int32_t block_size) { _block_size = block_size; } private: - int _block_size{0}; + int32_t _block_size{0}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h index eb058cec1..046aa5908 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h @@ -25,7 +25,7 @@ #include "luci/IR/AttrPadding.h" #include "luci/IR/AttrStride.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -34,9 +34,9 @@ namespace luci * @brief DEPTHWISE_CONV_2D in Circle */ class CircleDepthwiseConv2D final - : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::DEPTHWISE_CONV_2D>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc>, - public LuciNodeMixin<LuciNodeTrait::Bias> + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::DEPTHWISE_CONV_2D>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc>, + public CircleNodeMixin<CircleNodeTrait::Bias> { public: loco::Node *input(void) const { return at(0)->node(); } @@ -62,9 +62,9 @@ public: Dilation *dilation(void) { return &_dilation; } private: - Padding _padding = Padding::UNDEFINED; + Padding _padding{Padding::UNDEFINED}; Stride _stride; - int32_t _depth_multiplier = 0; + int32_t _depth_multiplier{0}; Dilation _dilation; }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h index 847c5dfc5..c3ee44253 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDequantize.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h index 1d4d3a239..fcc3f427c 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h @@ -24,7 +24,7 @@ #include "luci/IR/AttrPadding.h" #include "luci/IR/AttrStride.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -33,12 +33,9 @@ namespace luci * @brief DIV in Circle */ class CircleDiv final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::DIV>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: - CircleDiv() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h index fbb2f3533..721edd9ae 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleElu.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleElu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ELU>> { public: - CircleElu() = default; - -public: loco::Node *features(void) const { return at(0)->node(); } void features(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h index 2087d097a..69697ac7e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h index 97aecb30a..b8a5d4561 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h index f70219614..15bfe6a29 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleExpandDims.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleExpandDims final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::EXPAND_DIMS>> { public: - CircleExpandDims() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFakeQuant.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFakeQuant.h new file mode 100644 index 000000000..9e3159685 --- /dev/null +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFakeQuant.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 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 __LUCI_IR_CIRCLE_FAKE_QUANT_H__ +#define __LUCI_IR_CIRCLE_FAKE_QUANT_H__ + +#include "luci/IR/CircleNodeDecl.h" +#include "luci/IR/CircleOpcode.h" + +#include "luci/IR/CircleNodeMixins.h" + +namespace luci +{ + +/** + * @brief FAKE_QUANT in Circle + * @note 'inputs' came from TF.quantize.fake_quant_from_min_max_vars + */ +class CircleFakeQuant final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::FAKE_QUANT>> +{ +public: + loco::Node *inputs(void) const { return at(0)->node(); } + void inputs(loco::Node *node) { at(0)->node(node); } + +public: + float min(void) const { return _min; } + void min(float min) { _min = min; } + + float max(void) const { return _max; } + void max(float max) { _max = max; } + + int32_t num_bits(void) const { return _num_bits; } + void num_bits(int32_t num_bits) { _num_bits = num_bits; } + + bool narrow_range(void) const { return _narrow_range; } + void narrow_range(bool narrow_range) { _narrow_range = narrow_range; } + +private: + float _min{0.0f}; + float _max{0.0f}; + int32_t _num_bits{0}; + bool _narrow_range{false}; +}; + +} // namespace luci + +#endif // __LUCI_IR_CIRCLEGATHER_H__ diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h index bfc65274a..183794d41 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFill.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h index 7e10547b6..ce6807e98 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloor.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h index ba9db010c..bf76e37b6 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorDiv.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h index 4d13717a0..1af0af758 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFloorMod.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h index 952befc87..2862cadb2 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,9 +30,9 @@ namespace luci * @brief FULLY_CONNECTED in Circle */ class CircleFullyConnected final - : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::FULLY_CONNECTED>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc>, - public LuciNodeMixin<LuciNodeTrait::Bias> + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::FULLY_CONNECTED>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc>, + public CircleNodeMixin<CircleNodeTrait::Bias> { public: enum class WeightsFormat diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h index 1e8c4982a..78fa2fc28 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -42,7 +42,7 @@ public: void axis(int32_t axis) { _axis = axis; } private: - int32_t _axis = 0; + int32_t _axis{0}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h index 3423a8216..d6f34f1ea 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGatherNd.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h index 040a4e338..a03b6c749 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreater.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h index 82bdab212..e435320b2 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGreaterEqual.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief GREATER EQUAL in Circle */ class CircleGreaterEqual final - : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GREATER_EQUAL>> + : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GREATER_EQUAL>> { public: loco::Node *x(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h index 2f9eac211..1c037a406 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleIf.h @@ -34,7 +34,7 @@ class CircleIf final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::IF> { public: CircleIf(uint32_t arity, uint32_t out) - : VariadicArityNode<CircleNodeImpl<CircleOpcode::IF>>(arity + 1), _output_count(out) + : VariadicArityNode<CircleNodeImpl<CircleOpcode::IF>>(arity + 1), _output_count(out) { assert(arity > 0); assert(out > 0); diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h index 3654e943b..5adaaa447 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleIfOut.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleIfOut final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEIFOUT>> { public: - CircleIfOut() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h index 4a7d36a4e..e0be9aa6e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" #include <loco/IR/DataTypeTraits.h> #include <loco/IR/GraphInputIndex.h> @@ -35,16 +35,13 @@ namespace luci class CircleInput final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEINPUT>> { public: - CircleInput() = default; - -public: void index(const loco::GraphInputIndex &index); loco::GraphInputIndex index(void) const; bool indexed(void) const { return _index != -1; } private: - int64_t _index = -1; // Uninitialized + int64_t _index{-1}; // Uninitialized }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h index db0faa05e..65c34194d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,8 +30,8 @@ namespace luci * @brief INSTANCE_NORM in Circle */ class CircleInstanceNorm final - : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: /// @note Currently only support FLOAT32 as input node @@ -44,11 +44,12 @@ public: loco::Node *beta(void) const { return at(2)->node(); } void beta(loco::Node *node) { at(2)->node(node); } +public: float epsilon() const { return _epsilon; } void epsilon(float epsilon) { _epsilon = epsilon; } private: - float _epsilon = 1e-05; + float _epsilon{1e-05}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h index efa932d95..eb2b372ce 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Normalize.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,8 +30,8 @@ namespace luci * @brief L2_NORMALIZATION in Circle */ class CircleL2Normalize final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::L2_NORMALIZATION>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::L2_NORMALIZATION>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: loco::Node *x(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h index 7c76ee5d0..624d29e9e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleL2Pool2D.h @@ -24,7 +24,7 @@ #include "luci/IR/AttrPadding.h" #include "luci/IR/AttrStride.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -33,15 +33,13 @@ namespace luci * @brief L2_POOL_2D in Circle */ class CircleL2Pool2D final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::L2_POOL_2D>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: - CircleL2Pool2D() : _padding(Padding::UNDEFINED) { /* empty */} - -public: loco::Node *value(void) const { return at(0)->node(); } void value(loco::Node *node) { at(0)->node(node); } +public: Padding padding() const { return _padding; } void padding(Padding padding) { _padding = padding; } @@ -52,7 +50,7 @@ public: Stride *stride(void) { return &_stride; } private: - Padding _padding; + Padding _padding{Padding::UNDEFINED}; Stride _stride; Filter _filter; }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h index d6ac97fc0..c8e93af91 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLeakyRelu.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,17 +31,15 @@ namespace luci class CircleLeakyRelu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LEAKY_RELU>> { public: - CircleLeakyRelu() = default; - -public: loco::Node *features(void) const { return at(0)->node(); } void features(loco::Node *node) { at(0)->node(node); } +public: float alpha() const { return _alpha; } void alpha(float alpha) { _alpha = alpha; } private: - float _alpha = 0.2f; + float _alpha{0.2f}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h index cd6cf1872..7adf67842 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLess.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h index 4c7c6a49b..eb8962494 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLessEqual.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h index 8ad2b40fd..4d324700e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLocalResponseNormalization.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief LOCAL_RESPONSE_NORMALIZATION in Circle */ class CircleLocalResponseNormalization final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOCAL_RESPONSE_NORMALIZATION>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOCAL_RESPONSE_NORMALIZATION>> { public: loco::Node *input(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h index aeb13fed9..2cc57ce2d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLog.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h index 5dfd2c1f9..b73ff7c2a 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogSoftmax.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h index 975f6dbc7..9943c71cd 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalAnd.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h index 749dbe518..369a3e7bf 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h index 570be57af..c54ec3ebf 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h index 8328cb328..1f95e0f77 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogistic.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleLogistic final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOGISTIC>> { public: - CircleLogistic() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h index dca6538c3..f8bf259f9 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixDiag.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h index c1f5f3023..76aeaff40 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMatrixSetDiag.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief MATRIX_SET_DIAG in Circle */ class CircleMatrixSetDiag final - : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MATRIX_SET_DIAG>> + : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MATRIX_SET_DIAG>> { public: loco::Node *input(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h index 1eb6532ff..557240d54 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h @@ -24,7 +24,7 @@ #include "luci/IR/AttrPadding.h" #include "luci/IR/AttrStride.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -33,15 +33,13 @@ namespace luci * @brief MAX_POOL_2D in Circle */ class CircleMaxPool2D final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::MAX_POOL_2D>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: - CircleMaxPool2D() : _padding(Padding::UNDEFINED) { /* empty */} - -public: loco::Node *value(void) const { return at(0)->node(); } void value(loco::Node *node) { at(0)->node(node); } +public: Padding padding() const { return _padding; } void padding(Padding padding) { _padding = padding; } @@ -52,7 +50,7 @@ public: Stride *stride(void) { return &_stride; } private: - Padding _padding; + Padding _padding{Padding::UNDEFINED}; Stride _stride; Filter _filter; }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h index 6f789bc14..317cea308 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h index 7f8aeb5aa..f56e4f4c0 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -42,7 +42,7 @@ public: void keep_dims(bool keep_dims) { _keep_dims = keep_dims; } private: - bool _keep_dims = false; + bool _keep_dims{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h index 79d5a6f17..959d9c93b 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMinimum.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h index 68db8f6f3..c69e8f7c1 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMirrorPad.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" #include "luci/IR/AttrMirrorPadMode.h" namespace luci @@ -32,9 +32,6 @@ namespace luci class CircleMirrorPad final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MIRROR_PAD>> { public: - CircleMirrorPad() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h index 67e897170..85ed694b3 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,7 +30,7 @@ namespace luci * @brief MUL in Circle */ class CircleMul final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MUL>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: loco::Node *x(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h index 4149ac4a7..adea3fb83 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNeg.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h index 69f3368c0..b47404bb0 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief NON_MAX_SUPPRESSION_V4 in Circle */ class CircleNonMaxSuppressionV4 final - : public FixedArityNode<5, CircleNodeImpl<CircleOpcode::NON_MAX_SUPPRESSION_V4>> + : public FixedArityNode<5, CircleNodeImpl<CircleOpcode::NON_MAX_SUPPRESSION_V4>> { public: loco::Node *boxes(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h index a24dc3e9c..7e6923b5e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV4Out.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief Virtual NONMAXSUPPRESSIONV4OUT in Circle */ class CircleNonMaxSuppressionV4Out final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLENONMAXSUPPRESSIONV4OUT>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLENONMAXSUPPRESSIONV4OUT>> { public: - CircleNonMaxSuppressionV4Out() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h index 52d682147..77086ede7 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief NON_MAX_SUPPRESSION_V5 in Circle */ class CircleNonMaxSuppressionV5 final - : public FixedArityNode<6, CircleNodeImpl<CircleOpcode::NON_MAX_SUPPRESSION_V5>> + : public FixedArityNode<6, CircleNodeImpl<CircleOpcode::NON_MAX_SUPPRESSION_V5>> { public: loco::Node *boxes(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h index 0c6989cc7..63d061f11 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNonMaxSuppressionV5Out.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief Virtual NONMAXSUPPRESSIONV5OUT in Circle */ class CircleNonMaxSuppressionV5Out final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLENONMAXSUPPRESSIONV5OUT>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLENONMAXSUPPRESSIONV5OUT>> { public: - CircleNonMaxSuppressionV5Out() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h index cca7a5e22..add6a0747 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleNotEqual.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h index 665e01d48..b3eb0f436 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleOneHot.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -48,7 +48,7 @@ public: void axis(int32_t axis) { _axis = axis; } private: - int32_t _axis = -1; + int32_t _axis{-1}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h index 67e55f1a1..eb02f824e 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" #include <loco/IR/GraphOutputIndex.h> @@ -34,8 +34,6 @@ namespace luci class CircleOutput final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUT>> { public: - CircleOutput() = default; - void index(const loco::GraphOutputIndex &index); loco::GraphOutputIndex index(void) const; @@ -46,7 +44,7 @@ public: void from(loco::Node *node) { at(0)->node(node); } private: - int64_t _index = -1; // Uninitialized + int64_t _index{-1}; // Uninitialized }; /** @@ -54,7 +52,7 @@ private: */ // TODO remove CircleOutputDummy class CircleOutputDummy final - : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUTDUMMY>> + : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUTDUMMY>> { public: CircleOutputDummy() = default; @@ -64,7 +62,7 @@ public: * @brief CircleOutputExclude is used to specifying not exported nodes */ class CircleOutputExclude final - : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUTEXCLUDE>> + : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUTEXCLUDE>> { public: CircleOutputExclude() = default; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h index 693777512..3c5559db2 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePRelu.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CirclePRelu final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::PRELU>> { public: - CirclePRelu() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h index 31599bda0..ede217789 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CirclePad final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::PAD>> { public: - CirclePad() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h index 563cfd9a4..644e2bb27 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePadV2.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CirclePadV2 final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::PADV2>> { public: - CirclePadV2() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h index 006e3dd86..40c5a829d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePow.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CirclePow final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::POW>> { public: - CirclePow() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h index 977a37a52..56f8a2eba 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRange.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h index ba6d67f69..034f251bc 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRank.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h index 0456be863..c64dbbdf8 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceAny.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -42,7 +42,7 @@ public: void keep_dims(bool keep_dims) { _keep_dims = keep_dims; } private: - bool _keep_dims = false; + bool _keep_dims{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h index 925c977e5..97cbecd08 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMax.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -42,7 +42,7 @@ public: void keep_dims(bool keep_dims) { _keep_dims = keep_dims; } private: - bool _keep_dims = false; + bool _keep_dims{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h index fd789ae5e..33708928f 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceMin.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -42,7 +42,7 @@ public: void keep_dims(bool keep_dims) { _keep_dims = keep_dims; } private: - bool _keep_dims = false; + bool _keep_dims{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h index b7d226255..3689ee532 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReduceProd.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -42,7 +42,7 @@ public: void keep_dims(bool keep_dims) { _keep_dims = keep_dims; } private: - bool _keep_dims = false; + bool _keep_dims{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h index 91272d2bf..6148caa03 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleRelu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU>> { public: - CircleRelu() = default; - -public: loco::Node *features(void) const { return at(0)->node(); } void features(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h index b4274ded9..0fa25e873 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleRelu6 final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU6>> { public: - CircleRelu6() = default; - -public: loco::Node *features(void) const { return at(0)->node(); } void features(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h index a5c5710c2..13c0d166f 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReluN1To1.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleReluN1To1 final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU_N1_TO_1>> { public: - CircleReluN1To1() = default; - -public: loco::Node *features(void) const { return at(0)->node(); } void features(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h index b13144f7e..090df4044 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,14 +31,11 @@ namespace luci class CircleReshape final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESHAPE>> { public: - CircleReshape() = default; - -public: loco::Node *tensor(void) const { return at(0)->node(); } void tensor(loco::Node *node) { at(0)->node(node); } // NOTE shape is optional and can be CircleConst or any other type - // and also can be CircleOutputDummy when reshape option does not exist + // and also should be CircleOutputDummy when reshape option does not exist loco::Node *shape(void) const { return at(1)->node(); } void shape(loco::Node *node) { at(1)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h index 3c8223338..091916a2b 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeBilinear.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,18 +29,16 @@ namespace luci * @brief RESIZE_BILINEAR in Circle */ class CircleResizeBilinear final - : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESIZE_BILINEAR>> + : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESIZE_BILINEAR>> { public: - CircleResizeBilinear() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } loco::Node *size(void) const { return at(1)->node(); } void size(loco::Node *node) { at(1)->node(node); } +public: bool align_corners() const { return _align_corners; } void align_corners(bool value) { _align_corners = value; } @@ -48,8 +46,8 @@ public: void half_pixel_centers(bool value) { _half_pixel_centers = value; } private: - bool _align_corners = false; - bool _half_pixel_centers = false; + bool _align_corners{false}; + bool _half_pixel_centers{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h index dc32ebee7..ab880d767 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleResizeNearestNeighbor.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,23 +29,21 @@ namespace luci * @brief RESIZE_NEAREST_NEIGHBOR in Circle */ class CircleResizeNearestNeighbor final - : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESIZE_NEAREST_NEIGHBOR>> + : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESIZE_NEAREST_NEIGHBOR>> { public: - CircleResizeNearestNeighbor() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } loco::Node *size(void) const { return at(1)->node(); } void size(loco::Node *node) { at(1)->node(node); } +public: bool align_corners() const { return _align_corners; } void align_corners(bool value) { _align_corners = value; } private: - bool _align_corners = false; + bool _align_corners{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h index b0766dd3e..5f089a768 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseSequence.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief REVERSE_SEQUENCE in Circle */ class CircleReverseSequence final - : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REVERSE_SEQUENCE>> + : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::REVERSE_SEQUENCE>> { public: - CircleReverseSequence() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } @@ -42,15 +39,15 @@ public: void seq_lengths(loco::Node *node) { at(1)->node(node); } public: - int seq_axis(void) const { return _seq_axis; } - void seq_axis(int seq_axis) { _seq_axis = seq_axis; } + int32_t seq_axis(void) const { return _seq_axis; } + void seq_axis(int32_t seq_axis) { _seq_axis = seq_axis; } - int batch_axis(void) const { return _batch_axis; } - void batch_axis(int batch_axis) { _batch_axis = batch_axis; } + int32_t batch_axis(void) const { return _batch_axis; } + void batch_axis(int32_t batch_axis) { _batch_axis = batch_axis; } private: - int _seq_axis{0}; - int _batch_axis{0}; + int32_t _seq_axis{0}; + int32_t _batch_axis{0}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h index 71d9f65aa..96b6a793d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReverseV2.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h index 30296ce9e..e340266ed 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRound.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleRound final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ROUND>> { public: - CircleRound() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h index 873397bce..7907f326b 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleRsqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RSQRT>> { public: - CircleRsqrt() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h index 9f93a0a80..fda3abafc 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleScatterNd.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h index 416d617b2..e7227e9ee 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSegmentSum.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleSegmentSum final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SEGMENT_SUM>> { public: - CircleSegmentSum() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h index 727647168..6f778d72d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelect.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleSelect final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SELECT>> { public: - CircleSelect() = default; - -public: loco::Node *condition(void) const { return at(0)->node(); } void condition(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h index 7ac3c0524..7969cc2aa 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSelectV2.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleSelectV2 final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SELECT_V2>> { public: - CircleSelectV2() = default; - -public: loco::Node *condition(void) const { return at(0)->node(); } void condition(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h index ff20ce684..903894dbd 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleShape.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleShape final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SHAPE>> { public: - CircleShape() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h index 5624db253..25dc18b0d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSin.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h index a2113643d..98556d7a6 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSlice.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h index 7166a329b..d10cb1682 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h index 042ebffcd..ef715c6d0 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToBatchND.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief SPACE_TO_BATCH_ND in Circle */ class CircleSpaceToBatchND final - : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SPACE_TO_BATCH_ND>> + : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::SPACE_TO_BATCH_ND>> { public: loco::Node *input(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h index 420a4cb96..387e0d80f 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSpaceToDepth.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,18 +29,18 @@ namespace luci * @brief SPACE_TO_DEPTH in Circle */ class CircleSpaceToDepth final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SPACE_TO_DEPTH>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SPACE_TO_DEPTH>> { public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } public: - int block_size(void) const { return _block_size; } - void block_size(int block_size) { _block_size = block_size; } + int32_t block_size(void) const { return _block_size; } + void block_size(int32_t block_size) { _block_size = block_size; } private: - int _block_size{0}; + int32_t _block_size{0}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h index 7e80304b0..94a20c064 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSparseToDense.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief SPARSE_TO_DENSE in Circle */ class CircleSparseToDense final - : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::SPARSE_TO_DENSE>> + : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::SPARSE_TO_DENSE>> { public: loco::Node *indices(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h index 0eda19501..0cb953131 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplit.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h index 6bf4a9fef..a507740e4 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitOut.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleSplitOut final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLESPLITOUT>> { public: - CircleSplitOut() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h index 1b7d55534..cb02cbbcf 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitV.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h index d3b2f1e5a..adf79f30c 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSplitVOut.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief Virtual CIRCLESPLITVOUT in Circle */ class CircleSplitVOut final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLESPLITVOUT>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLESPLITVOUT>> { public: - CircleSplitVOut() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h index c96ca8498..b76bd1ad5 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleSqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQRT>> { public: - CircleSqrt() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h index a29edfe82..3f9228b3b 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquare.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleSquare final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQUARE>> { public: - CircleSquare() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h index b5b39f920..355c9f3d3 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief SQUARED_DIFFERENCE in Circle */ class CircleSquaredDifference final - : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SQUARED_DIFFERENCE>> + : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SQUARED_DIFFERENCE>> { public: - CircleSquaredDifference() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h index f175f1411..ba71ff217 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqueeze.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleSqueeze final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQUEEZE>> { public: - CircleSqueeze() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h index 98799fec1..6a4155ef1 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleStridedSlice.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,7 +29,7 @@ namespace luci * @brief STRIDED_SLICE in Circle */ class CircleStridedSlice final - : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::STRIDED_SLICE>> + : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::STRIDED_SLICE>> { public: loco::Node *input(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h index 08208f942..d9aaa44e5 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,12 +30,9 @@ namespace luci * @brief SUB in Circle */ class CircleSub final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SUB>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: - CircleSub() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h index 21faa76fe..a72e18f54 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSum.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h index f7444921f..2036a7301 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTanh.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleTanh final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::TANH>> { public: - CircleTanh() = default; - -public: loco::Node *x(void) const { return at(0)->node(); } void x(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h index 96e1f69c6..1ec2f5e82 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTile.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleTile final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TILE>> { public: - CircleTile() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h index 3b2b5abb7..0bf78c3ee 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleTopKV2 final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TOPK_V2>> { public: - CircleTopKV2() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h index 5a6dd0c02..f1a6b4a41 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTopKV2Out.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief Virtual CIRCLETOPKV2OUT in Circle */ class CircleTopKV2Out final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLETOPKV2OUT>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLETOPKV2OUT>> { public: - CircleTopKV2Out() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h index 095cd6746..72ce0738c 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,13 +31,7 @@ namespace luci class CircleTranspose final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TRANSPOSE>> { public: - CircleTranspose() = default; - -public: - /// @brief Get the input node to transpose loco::Node *a(void) const { return at(0)->node(); } - - /// @brief Set the input node to transpose void a(loco::Node *node) { at(0)->node(node); } loco::Node *perm(void) const { return at(1)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h index e355102d6..5ae41c0c4 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h @@ -22,7 +22,7 @@ #include "luci/IR/AttrPadding.h" #include "luci/IR/AttrStride.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -34,8 +34,8 @@ namespace luci * 'out' acutally means 'out' and 'in' of the this node. */ class CircleTransposeConv final - : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::TRANSPOSE_CONV>>, - public LuciNodeMixin<LuciNodeTrait::Bias> + : public FixedArityNode<4, CircleNodeImpl<CircleOpcode::TRANSPOSE_CONV>>, + public CircleNodeMixin<CircleNodeTrait::Bias> { public: loco::Node *inputSizes(void) const { return at(0)->node(); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h index 4352b045b..faf0ec94d 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnidirectionalSequenceLSTM.h @@ -21,7 +21,7 @@ #include "luci/IR/CircleOpcode.h" #include "luci/IR/AttrFusedActFunc.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -30,8 +30,8 @@ namespace luci * @brief UNIDIRECTIONAL_SEQUENCE_LSTM in Circle */ class CircleUnidirectionalSequenceLSTM final - : public FixedArityNode<24, CircleNodeImpl<CircleOpcode::UNIDIRECTIONAL_SEQUENCE_LSTM>>, - public LuciNodeMixin<LuciNodeTrait::FusedActFunc> + : public FixedArityNode<24, CircleNodeImpl<CircleOpcode::UNIDIRECTIONAL_SEQUENCE_LSTM>>, + public CircleNodeMixin<CircleNodeTrait::FusedActFunc> { public: loco::Node *input(void) const { return at(0)->node(); } @@ -104,10 +104,10 @@ public: } private: - float _cell_clip = 0.0f; - float _proj_clip = 0.0f; - bool _time_major = false; - bool _asymmetric_quantize_inputs = false; + float _cell_clip{0.0f}; + float _proj_clip{0.0f}; + bool _time_major{false}; + bool _asymmetric_quantize_inputs{false}; }; } // namespace luci diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h index 719a72362..2dd48b2f9 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnique.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -36,7 +36,7 @@ public: public: loco::DataType idx_out_type(void) const { return _idx_out_type; } - void output_type(loco::DataType ot) { _idx_out_type = ot; } + void idx_out_type(loco::DataType ot) { _idx_out_type = ot; } private: loco::DataType _idx_out_type{loco::DataType::S32}; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h index f846403e0..233351860 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUniqueOut.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief Virtual CIRCLEUNIQUEOUT in Circle */ class CircleUniqueOut final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEUNIQUEOUT>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEUNIQUEOUT>> { public: - CircleUniqueOut() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h index cb91d7e6a..fd0c66ce0 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpack.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleUnpack final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::UNPACK>> { public: - CircleUnpack() = default; - -public: loco::Node *value(void) const { return at(0)->node(); } void value(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h index 6f24578a1..640d2f1bb 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleUnpackOut.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -29,12 +29,9 @@ namespace luci * @brief Virtual CIRCLEUNPACKOUT in Circle */ class CircleUnpackOut final - : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEUNPACKOUT>> + : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEUNPACKOUT>> { public: - CircleUnpackOut() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h index 51eda3d6e..8895bcbbd 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhere.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" #include <cassert> @@ -33,9 +33,6 @@ namespace luci class CircleWhere final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::WHERE>> { public: - CircleWhere() = default; - -public: loco::Node *condition() const { return at(0)->node(); } void condition(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h index 40ec96414..f4154d3ab 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhile.h @@ -34,7 +34,7 @@ class CircleWhile final : public VariadicArityNode<CircleNodeImpl<CircleOpcode:: { public: CircleWhile(uint32_t arity, uint32_t out) - : VariadicArityNode<CircleNodeImpl<CircleOpcode::WHILE>>(arity), _output_count(out) + : VariadicArityNode<CircleNodeImpl<CircleOpcode::WHILE>>(arity), _output_count(out) { assert(arity > 0); assert(out > 0); diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h index cdf617848..98efc21e5 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleWhileOut.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,9 +31,6 @@ namespace luci class CircleWhileOut final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEWHILEOUT>> { public: - CircleWhileOut() = default; - -public: loco::Node *input(void) const { return at(0)->node(); } void input(loco::Node *node) { at(0)->node(node); } diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h index d3b6d272a..9302facd0 100644 --- a/compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h +++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleZerosLike.h @@ -20,7 +20,7 @@ #include "luci/IR/CircleNodeDecl.h" #include "luci/IR/CircleOpcode.h" -#include "luci/IR/LuciNodeMixins.h" +#include "luci/IR/CircleNodeMixins.h" namespace luci { @@ -31,13 +31,7 @@ namespace luci class CircleZerosLike final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ZEROS_LIKE>> { public: - CircleZerosLike() = default; - -public: - /// @brief Get the input node loco::Node *input(void) const { return at(0)->node(); } - - /// @brief Set the input node void input(loco::Node *node) { at(0)->node(node); } }; diff --git a/compiler/luci/lang/include/luci/IR/SparsityParam.h b/compiler/luci/lang/include/luci/IR/SparsityParam.h index f471e5ef9..6cfff67e1 100644 --- a/compiler/luci/lang/include/luci/IR/SparsityParam.h +++ b/compiler/luci/lang/include/luci/IR/SparsityParam.h @@ -44,7 +44,7 @@ class SparseIndexVector public: SparseIndexVector() = default; SparseIndexVector(const SparseIndexVectorType &type, const std::vector<int32_t> &sparse_index_vec) - : _type{type} + : _type{type} { switch (type) { @@ -53,7 +53,7 @@ public: case SparseIndexVectorType::I32: { _vec_ptr = static_cast<void *>( - new std::vector<int32_t>(sparse_index_vec.begin(), sparse_index_vec.end())); + new std::vector<int32_t>(sparse_index_vec.begin(), sparse_index_vec.end())); break; } case SparseIndexVectorType::U16: @@ -90,21 +90,21 @@ public: case SparseIndexVectorType::I32: { const std::vector<int32_t> *vec = - static_cast<const std::vector<int32_t> *>(sparse_index_vec); + static_cast<const std::vector<int32_t> *>(sparse_index_vec); _vec_ptr = static_cast<void *>(new std::vector<int32_t>(vec->begin(), vec->end())); break; } case SparseIndexVectorType::U16: { const std::vector<uint16_t> *vec = - static_cast<const std::vector<uint16_t> *>(sparse_index_vec); + static_cast<const std::vector<uint16_t> *>(sparse_index_vec); _vec_ptr = static_cast<void *>(new std::vector<uint16_t>(vec->begin(), vec->end())); break; } case SparseIndexVectorType::U8: { const std::vector<uint8_t> *vec = - static_cast<const std::vector<uint8_t> *>(sparse_index_vec); + static_cast<const std::vector<uint8_t> *>(sparse_index_vec); _vec_ptr = static_cast<void *>(new std::vector<uint8_t>(vec->begin(), vec->end())); break; } @@ -114,12 +114,12 @@ public: } SparseIndexVector(const SparseIndexVector &sparse_index_vec) - : SparseIndexVector(sparse_index_vec._type, sparse_index_vec._vec_ptr) + : SparseIndexVector(sparse_index_vec._type, sparse_index_vec._vec_ptr) { } SparseIndexVector(SparseIndexVector &&sparse_index_vec) - : _type{sparse_index_vec._type}, _vec_ptr{std::exchange(sparse_index_vec._vec_ptr, nullptr)} + : _type{sparse_index_vec._type}, _vec_ptr{std::exchange(sparse_index_vec._vec_ptr, nullptr)} { } @@ -178,8 +178,8 @@ public: const std::vector<uint16_t> *as_uint16_vector(void) const { return _type == SparseIndexVectorType::U16 - ? static_cast<const std::vector<uint16_t> *>(_vec_ptr) - : nullptr; + ? static_cast<const std::vector<uint16_t> *>(_vec_ptr) + : nullptr; } const std::vector<uint8_t> *as_uint8_vector(void) const { @@ -202,8 +202,8 @@ public: } DimMetaData(DimensionType format, int32_t dense_size, const SparseIndexVector &array_segments, const SparseIndexVector &array_indices) - : _format{format}, _dense_size{dense_size}, _array_segments{array_segments}, - _array_indices{array_indices} + : _format{format}, _dense_size{dense_size}, _array_segments{array_segments}, _array_indices{ + array_indices} { // DO NOTHING } diff --git a/compiler/luci/lang/src/CircleDialect.cpp b/compiler/luci/lang/src/CircleDialect.cpp index 42ca3c917..0d315fc55 100644 --- a/compiler/luci/lang/src/CircleDialect.cpp +++ b/compiler/luci/lang/src/CircleDialect.cpp @@ -15,6 +15,7 @@ */ #include "luci/IR/CircleDialect.h" +#include "luci/IR/DeadNodeQueryService.h" #include "luci/IR/Nodes/CircleInput.h" #include "luci/IR/Nodes/CircleOutput.h" @@ -22,8 +23,6 @@ #include <loco/IR/GraphInputIndex.h> #include <loco/IR/GraphOutputIndex.h> -#include "DeadNodeQueryService.h" - #include <cassert> #include <memory> diff --git a/compiler/luci/lang/src/LuciNodeMixins.cpp b/compiler/luci/lang/src/CircleNodeMixins.cpp index 660cbe1a5..f72178df5 100644 --- a/compiler/luci/lang/src/LuciNodeMixins.cpp +++ b/compiler/luci/lang/src/CircleNodeMixins.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,5 +14,5 @@ * limitations under the License. */ -// This is to validate LuciNodeMixins.h -#include "luci/IR/LuciNodeMixins.h" +// This is to validate CircleNodeMixins.h +#include "luci/IR/CircleNodeMixins.h" diff --git a/compiler/luci/lang/src/CircleNodes.cpp b/compiler/luci/lang/src/CircleNodes.cpp index c77c06861..2c2688c9e 100644 --- a/compiler/luci/lang/src/CircleNodes.cpp +++ b/compiler/luci/lang/src/CircleNodes.cpp @@ -23,31 +23,6 @@ namespace luci { -void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size) -{ - // Check node does not have both of new shape infos - LUCI_ASSERT(node->shape() == nullptr, "node already has shape input"); - LUCI_ASSERT(node->newShape()->rank() == 0, "node already has newShape attribute"); - - const loco::DataType S32 = loco::DataType::S32; - - // Set 2nd input as CircleConst - auto const_shape_node = node->graph()->nodes()->create<CircleConst>(); - const_shape_node->rank(1); - const_shape_node->dim(0) = size; - const_shape_node->dtype(S32); - const_shape_node->size<S32>(size); - const_shape_node->shape_status(luci::ShapeStatus::VALID); - for (uint32_t axis = 0; axis < size; ++axis) - const_shape_node->at<S32>(axis) = base[axis]; - node->shape(const_shape_node); - - // Set newShape attribute - node->newShape()->rank(size); - for (uint32_t axis = 0; axis < size; ++axis) - node->newShape()->dim(axis) = base[axis]; -} - void link(loco::GraphOutput *output, CircleOutput *node) { node->index(output->index()); } CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index) diff --git a/compiler/luci/lang/src/DeadNodeQueryService.cpp b/compiler/luci/lang/src/DeadNodeQueryService.cpp index a22574c94..7dac08b5f 100644 --- a/compiler/luci/lang/src/DeadNodeQueryService.cpp +++ b/compiler/luci/lang/src/DeadNodeQueryService.cpp @@ -14,9 +14,8 @@ * limitations under the License. */ -#include "DeadNodeQueryService.h" - #include "luci/IR/CircleNodeVisitor.h" +#include "luci/IR/DeadNodeQueryService.h" #include <loco/IR/Graph.h> diff --git a/compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp b/compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp index d7712c8dd..3859d7fca 100644 --- a/compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp +++ b/compiler/luci/lang/src/Nodes/CircleBatchMatMul.test.cpp @@ -26,7 +26,7 @@ TEST(CircleBatchMatMulTest, constructor) luci::CircleBatchMatMul batchmatmul_node; ASSERT_EQ(luci::CircleDialect::get(), batchmatmul_node.dialect()); - ASSERT_EQ(luci::CircleOpcode::BATCHMATMUL, batchmatmul_node.opcode()); + ASSERT_EQ(luci::CircleOpcode::BATCH_MATMUL, batchmatmul_node.opcode()); ASSERT_EQ(nullptr, batchmatmul_node.x()); ASSERT_EQ(nullptr, batchmatmul_node.y()); diff --git a/compiler/luci/lang/src/Nodes/CircleBidrectionalSequenceLSTM.test.cpp b/compiler/luci/lang/src/Nodes/CircleBidrectionalSequenceLSTM.test.cpp new file mode 100644 index 000000000..3f13422e5 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleBidrectionalSequenceLSTM.test.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021 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/IR/Nodes/CircleBidirectionalSequenceLSTM.h" + +#include "luci/IR/CircleDialect.h" +#include "luci/IR/CircleNodeVisitor.h" + +#include <gtest/gtest.h> + +TEST(CircleBidirectionalSequenceLSTMTest, constructor_P) +{ + luci::CircleBidirectionalSequenceLSTM trc_node; + + ASSERT_EQ(luci::CircleDialect::get(), trc_node.dialect()); + ASSERT_EQ(luci::CircleOpcode::BIDIRECTIONAL_SEQUENCE_LSTM, trc_node.opcode()); + + ASSERT_EQ(nullptr, trc_node.input()); + + ASSERT_EQ(nullptr, trc_node.fw_input_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.fw_input_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.fw_input_to_cell_weights()); + ASSERT_EQ(nullptr, trc_node.fw_input_to_output_weights()); + + ASSERT_EQ(nullptr, trc_node.fw_recurrent_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.fw_recurrent_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.fw_recurrent_to_cell_weights()); + ASSERT_EQ(nullptr, trc_node.fw_recurrent_to_output_weights()); + + ASSERT_EQ(nullptr, trc_node.fw_cell_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.fw_cell_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.fw_cell_to_output_weights()); + + ASSERT_EQ(nullptr, trc_node.fw_input_gate_bias()); + ASSERT_EQ(nullptr, trc_node.fw_forget_gate_bias()); + ASSERT_EQ(nullptr, trc_node.fw_cell_gate_bias()); + ASSERT_EQ(nullptr, trc_node.fw_output_gate_bias()); + + ASSERT_EQ(nullptr, trc_node.fw_projection_weights()); + ASSERT_EQ(nullptr, trc_node.fw_projection_bias()); + + ASSERT_EQ(nullptr, trc_node.bw_input_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.bw_input_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.bw_input_to_cell_weights()); + ASSERT_EQ(nullptr, trc_node.bw_input_to_output_weights()); + + ASSERT_EQ(nullptr, trc_node.bw_recurrent_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.bw_recurrent_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.bw_recurrent_to_cell_weights()); + ASSERT_EQ(nullptr, trc_node.bw_recurrent_to_output_weights()); + + ASSERT_EQ(nullptr, trc_node.bw_cell_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.bw_cell_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.bw_cell_to_output_weights()); + + ASSERT_EQ(nullptr, trc_node.bw_input_gate_bias()); + ASSERT_EQ(nullptr, trc_node.bw_forget_gate_bias()); + ASSERT_EQ(nullptr, trc_node.bw_cell_gate_bias()); + ASSERT_EQ(nullptr, trc_node.bw_output_gate_bias()); + + ASSERT_EQ(nullptr, trc_node.bw_projection_weights()); + ASSERT_EQ(nullptr, trc_node.bw_projection_bias()); + + ASSERT_EQ(nullptr, trc_node.fw_activation_state()); + ASSERT_EQ(nullptr, trc_node.fw_cell_state()); + ASSERT_EQ(nullptr, trc_node.bw_activation_state()); + ASSERT_EQ(nullptr, trc_node.bw_cell_state()); + + ASSERT_EQ(nullptr, trc_node.auxillary_input()); + ASSERT_EQ(nullptr, trc_node.fw_auxillary_input_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.fw_auxillary_input_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.fw_auxillary_input_to_cell_weights()); + ASSERT_EQ(nullptr, trc_node.fw_auxillary_input_to_output_weights()); + ASSERT_EQ(nullptr, trc_node.bw_auxillary_input_to_input_weights()); + ASSERT_EQ(nullptr, trc_node.bw_auxillary_input_to_forget_weights()); + ASSERT_EQ(nullptr, trc_node.bw_auxillary_input_to_cell_weights()); + ASSERT_EQ(nullptr, trc_node.bw_auxillary_input_to_output_weights()); + + ASSERT_EQ(luci::FusedActFunc::UNDEFINED, trc_node.fusedActivationFunction()); + ASSERT_EQ(0.f, trc_node.cell_clip()); + ASSERT_EQ(0.f, trc_node.proj_clip()); + ASSERT_EQ(false, trc_node.merge_outputs()); + ASSERT_EQ(false, trc_node.time_major()); + ASSERT_EQ(false, trc_node.asymmetric_quantize_inputs()); +} + +TEST(CircleBidirectionalSequenceLSTMTest, arity_NEG) +{ + luci::CircleBidirectionalSequenceLSTM trc_node; + + ASSERT_NO_THROW(trc_node.arg(36)); + ASSERT_THROW(trc_node.arg(48), std::out_of_range); +} + +TEST(CircleBidirectionalSequenceLSTMTest, visit_mutable_NEG) +{ + struct TestVisitor final : public luci::CircleNodeMutableVisitor<void> + { + }; + + luci::CircleBidirectionalSequenceLSTM trc_node; + + TestVisitor tv; + ASSERT_THROW(trc_node.accept(&tv), std::exception); +} + +TEST(CircleBidirectionalSequenceLSTMTest, visit_NEG) +{ + struct TestVisitor final : public luci::CircleNodeVisitor<void> + { + }; + + luci::CircleBidirectionalSequenceLSTM trc_node; + + TestVisitor tv; + ASSERT_THROW(trc_node.accept(&tv), std::exception); +} diff --git a/compiler/luci/lang/src/Nodes/CircleConst.test.cpp b/compiler/luci/lang/src/Nodes/CircleConst.test.cpp new file mode 100644 index 000000000..a81f4b00d --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleConst.test.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 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/IR/Nodes/CircleConst.h" + +#include "luci/IR/CircleDialect.h" +#include "luci/IR/CircleNodeVisitor.h" + +#include <gtest/gtest.h> + +TEST(CircleConstTest, constructor) +{ + luci::CircleConst const_node; + + ASSERT_EQ(luci::CircleDialect::get(), const_node.dialect()); + ASSERT_EQ(luci::CircleOpcode::CIRCLECONST, const_node.opcode()); +} + +TEST(CircleConstTest, dype_size) +{ + luci::CircleConst const_node; + + const_node.dtype(loco::DataType::S32); + const_node.size<loco::DataType::S32>(1); + + ASSERT_EQ(loco::DataType::S32, const_node.dtype()); + ASSERT_EQ(1, const_node.size<loco::DataType::S32>()); +} + +TEST(CircleConstTest, scalar) +{ + luci::CircleConst const_node; + + const_node.dtype(loco::DataType::S32); + const_node.size<loco::DataType::S32>(1); + const_node.scalar<loco::DataType::S32>() = 1; + + auto const &cs = const_node.scalar<loco::DataType::S32>(); + ASSERT_EQ(1, cs); +} diff --git a/compiler/luci/lang/src/Nodes/CircleCustom.test.cpp b/compiler/luci/lang/src/Nodes/CircleCustom.test.cpp index c07268cbf..76b70f38b 100644 --- a/compiler/luci/lang/src/Nodes/CircleCustom.test.cpp +++ b/compiler/luci/lang/src/Nodes/CircleCustom.test.cpp @@ -22,7 +22,7 @@ TEST(CircleCustomTest, constructor) { - luci::CircleCustom custom_node(2); + luci::CircleCustom custom_node(2, 1); ASSERT_EQ(luci::CircleDialect::get(), custom_node.dialect()); ASSERT_EQ(luci::CircleOpcode::CUSTOM, custom_node.opcode()); @@ -33,18 +33,19 @@ TEST(CircleCustomTest, constructor) ASSERT_EQ(2, custom_node.numInputs()); ASSERT_EQ(0, custom_node.custom_code().size()); + ASSERT_EQ(1, custom_node.numOutputs()); } TEST(CircleCustomTest, constructor_NEG) { - ASSERT_DEBUG_DEATH(luci::CircleCustom{0}, ""); + ASSERT_DEBUG_DEATH(luci::CircleCustom(0, 0), ""); SUCCEED(); } TEST(CircleCustomTest, invalidIndex_NEG) { - luci::CircleCustom custom_node(2); + luci::CircleCustom custom_node(2, 1); EXPECT_ANY_THROW(custom_node.arg(5)); } diff --git a/compiler/luci/lang/src/Nodes/CircleFakeQuant.test.cpp b/compiler/luci/lang/src/Nodes/CircleFakeQuant.test.cpp new file mode 100644 index 000000000..912e40570 --- /dev/null +++ b/compiler/luci/lang/src/Nodes/CircleFakeQuant.test.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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/IR/Nodes/CircleFakeQuant.h" + +#include "luci/IR/CircleDialect.h" +#include "luci/IR/CircleNodeVisitor.h" + +#include <gtest/gtest.h> + +TEST(CircleFakeQuantTest, constructor_P) +{ + luci::CircleFakeQuant fakequant; + + ASSERT_EQ(fakequant.dialect(), luci::CircleDialect::get()); + ASSERT_EQ(fakequant.opcode(), luci::CircleOpcode::FAKE_QUANT); + + ASSERT_EQ(nullptr, fakequant.inputs()); + ASSERT_EQ(0.0f, fakequant.min()); + ASSERT_EQ(0.0f, fakequant.max()); + ASSERT_EQ(0, fakequant.num_bits()); + ASSERT_FALSE(fakequant.narrow_range()); +} diff --git a/compiler/luci/logex/src/FormattedGraph.cpp b/compiler/luci/logex/src/FormattedGraph.cpp index b2b9cb72b..f1337e3e6 100644 --- a/compiler/luci/logex/src/FormattedGraph.cpp +++ b/compiler/luci/logex/src/FormattedGraph.cpp @@ -146,7 +146,9 @@ std::string circle_opname(uint32_t opnum) #define CIRCLE_NODE(OPCODE, CLASS) \ case luci::CircleOpcode::OPCODE: \ return prefix + #OPCODE; +#define CIRCLE_VNODE CIRCLE_NODE #include <luci/IR/CircleNodes.lst> +#undef CIRCLE_VNODE #undef CIRCLE_NODE default: break; @@ -175,7 +177,9 @@ protected: s.state(locop::NodeSummary::State::PartiallyKnown); \ return true; \ } +#define CIRCLE_VNODE CIRCLE_NODE #include <luci/IR/CircleNodes.lst> +#undef CIRCLE_VNODE #undef CIRCLE_NODE protected: @@ -205,6 +209,7 @@ private: IMPLEMENT(luci::CircleAveragePool2D) IMPLEMENT(luci::CircleBatchMatMul) IMPLEMENT(luci::CircleBatchToSpaceND) + IMPLEMENT(luci::CircleBidirectionalSequenceLSTM) IMPLEMENT(luci::CircleCast) IMPLEMENT(luci::CircleCeil) IMPLEMENT(luci::CircleConcatenation) @@ -219,6 +224,7 @@ private: IMPLEMENT(luci::CircleElu) IMPLEMENT(luci::CircleExp) IMPLEMENT(luci::CircleExpandDims) + IMPLEMENT(luci::CircleFakeQuant) IMPLEMENT(luci::CircleFill) IMPLEMENT(luci::CircleFloor) IMPLEMENT(luci::CircleFloorDiv) @@ -433,6 +439,96 @@ bool summary_node(const locop::SymbolTable *tbl, const luci::CircleBatchToSpaceN return true; } +bool summary_node(const locop::SymbolTable *tbl, const luci::CircleBidirectionalSequenceLSTM *node, + locop::NodeSummary &s) +{ + s.args().append("input", tbl->lookup(node->input())); + + s.args().append("fw_input_to_input_weights", tbl->lookup(node->fw_input_to_input_weights())); + s.args().append("fw_input_to_forget_weights", tbl->lookup(node->fw_input_to_forget_weights())); + s.args().append("fw_input_to_cell_weights", tbl->lookup(node->fw_input_to_cell_weights())); + s.args().append("fw_input_to_output_weights", tbl->lookup(node->fw_input_to_output_weights())); + + s.args().append("fw_recurrent_to_input_weights", + tbl->lookup(node->fw_recurrent_to_input_weights())); + s.args().append("fw_recurrent_to_forget_weights", + tbl->lookup(node->fw_recurrent_to_forget_weights())); + s.args().append("fw_recurrent_to_cell_weights", + tbl->lookup(node->fw_recurrent_to_cell_weights())); + s.args().append("fw_recurrent_to_output_weights", + tbl->lookup(node->fw_recurrent_to_output_weights())); + + s.args().append("fw_cell_to_input_weights", tbl->lookup(node->fw_cell_to_input_weights())); + s.args().append("fw_cell_to_forget_weights", tbl->lookup(node->fw_cell_to_forget_weights())); + s.args().append("fw_cell_to_output_weights", tbl->lookup(node->fw_cell_to_output_weights())); + + s.args().append("fw_input_gate_bias", tbl->lookup(node->fw_input_gate_bias())); + s.args().append("fw_forget_gate_bias", tbl->lookup(node->fw_forget_gate_bias())); + s.args().append("fw_cell_gate_bias", tbl->lookup(node->fw_cell_gate_bias())); + s.args().append("fw_output_gate_bias", tbl->lookup(node->fw_output_gate_bias())); + + s.args().append("fw_projection_weights", tbl->lookup(node->fw_projection_weights())); + s.args().append("fw_projection_bias", tbl->lookup(node->fw_projection_bias())); + + s.args().append("bw_input_to_input_weights", tbl->lookup(node->bw_input_to_input_weights())); + s.args().append("bw_input_to_forget_weights", tbl->lookup(node->bw_input_to_forget_weights())); + s.args().append("bw_input_to_cell_weights", tbl->lookup(node->bw_input_to_cell_weights())); + s.args().append("bw_input_to_output_weights", tbl->lookup(node->bw_input_to_output_weights())); + + s.args().append("bw_recurrent_to_input_weights", + tbl->lookup(node->bw_recurrent_to_input_weights())); + s.args().append("bw_recurrent_to_forget_weights", + tbl->lookup(node->bw_recurrent_to_forget_weights())); + s.args().append("bw_recurrent_to_cell_weights", + tbl->lookup(node->bw_recurrent_to_cell_weights())); + s.args().append("bw_recurrent_to_output_weights", + tbl->lookup(node->bw_recurrent_to_output_weights())); + + s.args().append("bw_cell_to_input_weights", tbl->lookup(node->bw_cell_to_input_weights())); + s.args().append("bw_cell_to_forget_weights", tbl->lookup(node->bw_cell_to_forget_weights())); + s.args().append("bw_cell_to_output_weights", tbl->lookup(node->bw_cell_to_output_weights())); + + s.args().append("bw_input_gate_bias", tbl->lookup(node->bw_input_gate_bias())); + s.args().append("bw_forget_gate_bias", tbl->lookup(node->bw_forget_gate_bias())); + s.args().append("bw_cell_gate_bias", tbl->lookup(node->bw_cell_gate_bias())); + s.args().append("bw_output_gate_bias", tbl->lookup(node->bw_output_gate_bias())); + + s.args().append("bw_projection_weights", tbl->lookup(node->bw_projection_weights())); + s.args().append("bw_projection_bias", tbl->lookup(node->bw_projection_bias())); + + s.args().append("fw_activation_state", tbl->lookup(node->fw_activation_state())); + s.args().append("fw_cell_state", tbl->lookup(node->fw_cell_state())); + s.args().append("bw_activation_state", tbl->lookup(node->bw_activation_state())); + s.args().append("bw_cell_state", tbl->lookup(node->bw_cell_state())); + + s.args().append("auxillary_input", tbl->lookup(node->auxillary_input())); + s.args().append("fw_auxillary_input_to_input_weights", + tbl->lookup(node->fw_auxillary_input_to_input_weights())); + s.args().append("fw_auxillary_input_to_forget_weights", + tbl->lookup(node->fw_auxillary_input_to_forget_weights())); + s.args().append("fw_auxillary_input_to_cell_weights", + tbl->lookup(node->fw_auxillary_input_to_cell_weights())); + s.args().append("fw_auxillary_input_to_output_weights", + tbl->lookup(node->fw_auxillary_input_to_output_weights())); + s.args().append("bw_auxillary_input_to_input_weights", + tbl->lookup(node->bw_auxillary_input_to_input_weights())); + s.args().append("bw_auxillary_input_to_forget_weights", + tbl->lookup(node->bw_auxillary_input_to_forget_weights())); + s.args().append("bw_auxillary_input_to_cell_weights", + tbl->lookup(node->bw_auxillary_input_to_cell_weights())); + s.args().append("bw_auxillary_input_to_output_weights", + tbl->lookup(node->bw_auxillary_input_to_output_weights())); + + s.args().append("cell_clip", to_str(node->cell_clip())); + s.args().append("proj_clip", to_str(node->proj_clip())); + s.args().append("merge_outputs", to_str(node->merge_outputs())); + s.args().append("time_major", to_str(node->time_major())); + s.args().append("asymmetric_quantize_inputs", to_str(node->asymmetric_quantize_inputs())); + + s.state(locop::NodeSummary::State::Complete); + return true; +} + bool summary_node(const locop::SymbolTable *tbl, const luci::CircleCast *node, locop::NodeSummary &s) { @@ -521,6 +617,18 @@ bool summary_node(const locop::SymbolTable *tbl, const luci::CircleExpandDims *n return true; } +bool summary_node(const locop::SymbolTable *tbl, const luci::CircleFakeQuant *node, + locop::NodeSummary &s) +{ + s.args().append("inputs", tbl->lookup(node->inputs())); + s.args().append("min", pepper::str(node->min())); + s.args().append("max", pepper::str(node->max())); + s.args().append("num_bits", pepper::str(node->num_bits())); + s.args().append("narrow_range", node->narrow_range() ? "true" : "false"); + s.state(locop::NodeSummary::State::Complete); + return true; +} + bool summary_node(const locop::SymbolTable *tbl, const luci::CircleFill *node, locop::NodeSummary &s) { @@ -1189,7 +1297,9 @@ bool CircleNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSumm s.comments().append("Mem = " + ptr_to_str(node)); \ return summary(dynamic_cast<const CLASS *>(node), s); \ } +#define CIRCLE_VNODE CIRCLE_NODE #include <luci/IR/CircleNodes.lst> +#undef CIRCLE_VNODE #undef CIRCLE_NODE return false; @@ -1238,6 +1348,12 @@ bool CircleNodeSummaryBuilder::summary(const luci::CircleBatchToSpaceND *node, return summary_node(tbl(), node, s); } +bool CircleNodeSummaryBuilder::summary(const luci::CircleBidirectionalSequenceLSTM *node, + locop::NodeSummary &s) const +{ + return summary_node(tbl(), node, s); +} + bool CircleNodeSummaryBuilder::summary(const luci::CircleCast *node, locop::NodeSummary &s) const { return summary_node(tbl(), node, s); @@ -1314,6 +1430,17 @@ bool CircleNodeSummaryBuilder::summary(const luci::CircleExpandDims *node, return summary_node(tbl(), node, s); } +bool CircleNodeSummaryBuilder::summary(const luci::CircleFakeQuant *node, + locop::NodeSummary &s) const +{ + return summary_node(tbl(), node, s); +} + +bool CircleNodeSummaryBuilder::summary(const luci::CircleFill *node, locop::NodeSummary &s) const +{ + return summary_node(tbl(), node, s); +} + bool CircleNodeSummaryBuilder::summary(const luci::CircleFloor *node, locop::NodeSummary &s) const { return use_x(tbl(), node, s); @@ -1331,11 +1458,6 @@ bool CircleNodeSummaryBuilder::summary(const luci::CircleFloorMod *node, return use_xy(tbl(), node, s); } -bool CircleNodeSummaryBuilder::summary(const luci::CircleFill *node, locop::NodeSummary &s) const -{ - return summary_node(tbl(), node, s); -} - bool CircleNodeSummaryBuilder::summary(const luci::CircleFullyConnected *node, locop::NodeSummary &s) const { diff --git a/compiler/luci/partition/CMakeLists.txt b/compiler/luci/partition/CMakeLists.txt new file mode 100644 index 000000000..838642b6e --- /dev/null +++ b/compiler/luci/partition/CMakeLists.txt @@ -0,0 +1,29 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_partition SHARED ${SOURCES}) +target_include_directories(luci_partition PRIVATE src) +target_include_directories(luci_partition PUBLIC include) +target_link_libraries(luci_partition PUBLIC luci_lang) +target_link_libraries(luci_partition PRIVATE luci_service) +target_link_libraries(luci_partition PRIVATE luci_log) +target_link_libraries(luci_partition PRIVATE luci_logex) +target_link_libraries(luci_partition PRIVATE mio_circle) +target_link_libraries(luci_partition PRIVATE nncc_common) +target_link_libraries(luci_partition PRIVATE oops) + +install(TARGETS luci_partition DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(luci_partition_test ${TESTS}) +target_include_directories(luci_partition_test PRIVATE src) +target_link_libraries(luci_partition_test luci_lang) +target_link_libraries(luci_partition_test luci_partition) +target_link_libraries(luci_partition_test luci_testhelper) +target_link_libraries(luci_partition_test luci_service) diff --git a/compiler/luci/partition/README.md b/compiler/luci/partition/README.md new file mode 100644 index 000000000..40a46bc56 --- /dev/null +++ b/compiler/luci/partition/README.md @@ -0,0 +1,4 @@ +# luci-partition + +`luci-partition` provides partition of a model to two or more sub models and +its connection configuration having same computational results. diff --git a/compiler/luci/partition/include/luci/Partition.h b/compiler/luci/partition/include/luci/Partition.h new file mode 100644 index 000000000..cf90e448b --- /dev/null +++ b/compiler/luci/partition/include/luci/Partition.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITION_H__ +#define __LUCI_PARTITION_H__ + +#include <luci/IR/Module.h> + +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +namespace luci +{ + +/** + * @brief PartitionTable holds partition information + */ +struct PartitionTable +{ + std::vector<std::string> groups; + std::string default_group; + + // assign by opcode name: OPCODENAME=group + std::unordered_map<std::string /* OPCODENAME */, std::string /* group */> byopcodes; + + // TODO add assign by OP name +}; + +/** + * @brief PartedModule holds partitioned module and group name + */ +struct PartedModule +{ + std::unique_ptr<Module> module; + // group name used to partition this module + std::string group; + + // unique name(filename) of this module + std::string name; +}; + +struct PartedModules +{ + std::vector<PartedModule> pmodules; + + // TODO add connections ? +}; + +/** + * @brief Method to do paritioning from module and PartitionTable to produce PartedModules + */ +PartedModules apply(Module *module, const PartitionTable &partition); + +} // namespace luci + +#endif // __LUCI_PARTITION_H__ diff --git a/compiler/luci/partition/src/CircleOpCode.cpp b/compiler/luci/partition/src/CircleOpCode.cpp new file mode 100644 index 000000000..86694fa40 --- /dev/null +++ b/compiler/luci/partition/src/CircleOpCode.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 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 "CircleOpCode.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> + +#include <mio/circle/schema_generated.h> + +namespace +{ + +using namespace luci; +using namespace circle; + +class QueryOpCode final : public CircleNodeVisitor<BuiltinOperator> +{ +public: +// NOTE only circle operator may have BuiltinOperator_XXX +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \ + BuiltinOperator visit(const CIRCLE_CLASS *) final { return BuiltinOperator_##OPCODE; } +#define CIRCLE_VNODE(OPCODE, CIRCLE_CLASS) + +#include "luci/IR/CircleNodes.lst" +#undef CIRCLE_VNODE +#undef CIRCLE_NODE + + // NOTE only builtin operators should be called (NOT virtual nodes) +}; + +class QueryCircleName final : public luci::CircleNodeVisitor<const char *> +{ +public: +// NOTE provide names for circle virtual nodes +#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) +#define CIRCLE_VNODE(OPCODE, CIRCLE_CLASS) \ + const char *visit(const CIRCLE_CLASS *) final { return #OPCODE; } + +#include "luci/IR/CircleNodes.lst" +#undef CIRCLE_VNODE +#undef CIRCLE_NODE + + // default is null + const char *visit(const luci::CircleNode *) final { return nullptr; } +}; + +} // namespace + +namespace luci +{ + +std::string opcode_name(const CircleNode *node) +{ + QueryCircleName qcn; + auto cname = node->accept(&qcn); + if (cname != nullptr) + return std::string(cname); + + QueryOpCode qoc; + auto opcode = node->accept(&qoc); + auto name = circle::EnumNameBuiltinOperator(opcode); + return std::string(name); +} + +} // namespace luci diff --git a/compiler/luci/lang/src/CircleShapeSignature.cpp b/compiler/luci/partition/src/CircleOpCode.h index 970000203..d17b09261 100644 --- a/compiler/luci/lang/src/CircleShapeSignature.cpp +++ b/compiler/luci/partition/src/CircleOpCode.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,21 +14,18 @@ * limitations under the License. */ -#include "luci/IR/CircleShapeSignature.h" +#ifndef __LUCI_PARTITION_CIRCLE_OP_CODE_H__ +#define __LUCI_PARTITION_CIRCLE_OP_CODE_H__ -namespace luci -{ +#include <luci/IR/CircleNode.h> -bool operator==(const ShapeSignature &lhs, const ShapeSignature &rhs) -{ - if (lhs.rank() != rhs.rank()) - return false; +#include <string> - for (uint32_t i = 0; i < lhs.rank(); ++i) - if (lhs.dim(i) != rhs.dim(i)) - return false; +namespace luci +{ - return true; -} +std::string opcode_name(const CircleNode *node); } // namespace luci + +#endif // __LUCI_PARTITION_CIRCLE_OP_CODE_H__ diff --git a/compiler/luci/partition/src/CircleOpCode.test.cpp b/compiler/luci/partition/src/CircleOpCode.test.cpp new file mode 100644 index 000000000..d2524a2ef --- /dev/null +++ b/compiler/luci/partition/src/CircleOpCode.test.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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 "CircleOpCode.h" + +// NOTE any node will do for testing +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +TEST(CircleOpCodeTest, name) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleSqrt>(); + + auto name = luci::opcode_name(node); + ASSERT_EQ(name, "SQRT"); +} diff --git a/compiler/luci/partition/src/ConnectNode.cpp b/compiler/luci/partition/src/ConnectNode.cpp new file mode 100644 index 000000000..336be7c57 --- /dev/null +++ b/compiler/luci/partition/src/ConnectNode.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +#include <oops/UserExn.h> + +namespace luci +{ + +void clone_connect(const luci::CircleNode *node, luci::CloneContext &clonecontext) +{ + ConnectNode cn(clonecontext); + node->accept(&cn); +} + +luci::CircleNode *ConnectNode::find_clone(const luci::CircleNode *node) +{ + auto it = _clonecontext.find(node); + if (it == _clonecontext.end()) + throw oops::UserExn("Invalid node in ConnectNode"); + return it->second; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/ConnectNode.h b/compiler/luci/partition/src/ConnectNode.h new file mode 100644 index 000000000..017c587e5 --- /dev/null +++ b/compiler/luci/partition/src/ConnectNode.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITION_CONNECT_NODE_H__ +#define __LUCI_PARTITION_CONNECT_NODE_H__ + +#include <luci/IR/CircleNode.h> +#include <luci/IR/CircleNodeVisitor.h> + +namespace luci +{ + +/** + * @note MapNode2Clone is used as a map from original node to cloned node + * to find input of a cloned node + * + * (Original) (Clone) + * + * [A] [A'] + * | [B] | [B'] + * | | | | + * \ / \ / + * [C] [C'] + * + * From view of [C'] we need to find [A'] and [B']. We know [C] from [C'], + * then we can get from input of [C] as [A], [B] then [A]->[A'] and [B]->[B'] + * from the map. + */ +using MapNode2Clone = std::map<const CircleNode * /* ORG */, CircleNode * /* CLONE */>; + +struct CloneContext +{ + std::pair<MapNode2Clone::iterator, bool> emplace(const CircleNode *org, CircleNode *clone) + { + return node2clone.emplace(org, clone); + } + MapNode2Clone::iterator find(const CircleNode *org) { return node2clone.find(org); } + MapNode2Clone::iterator end(void) { return node2clone.end(); } + + MapNode2Clone node2clone; +}; + +class ConnectNode final : public luci::CircleNodeVisitor<void> +{ +public: + ConnectNode(luci::CloneContext &clonecontext) : _clonecontext(clonecontext){}; + +public: + // void visit(const luci::CircleAbs *) final; + void visit(const luci::CircleAdd *) final; + // void visit(const luci::CircleAddN *) final; + // void visit(const luci::CircleArgMax *) final; + // void visit(const luci::CircleArgMin *) final; + // void visit(const luci::CircleAveragePool2D *) final; + // void visit(const luci::CircleBatchMatMul *) final; + // void visit(const luci::CircleBatchToSpaceND *) final; + // void visit(const luci::CircleCast *) final; + // void visit(const luci::CircleCeil *) final; + // void visit(const luci::CircleConcatenation *) final; + void visit(const luci::CircleConst *) final; + // void visit(const luci::CircleConv2D *) final; + // void visit(const luci::CircleCos *) final; + // void visit(const luci::CircleCustom *) final; + // void visit(const luci::CircleDepthToSpace *) final; + // void visit(const luci::CircleDepthwiseConv2D *) final; + // void visit(const luci::CircleDequantize *) final; + void visit(const luci::CircleDiv *) final; + // void visit(const luci::CircleElu *) final; + // void visit(const luci::CircleEqual *) final; + // void visit(const luci::CircleExp *) final; + // void visit(const luci::CircleExpandDims *) final; + // void visit(const luci::CircleFakeQuant *) final; + // void visit(const luci::CircleFill *) final; + // void visit(const luci::CircleFloor *) final; + // void visit(const luci::CircleFloorDiv *) final; + // void visit(const luci::CircleFloorMod *) final; + // void visit(const luci::CircleFullyConnected *) final; + // void visit(const luci::CircleGather *) final; + // void visit(const luci::CircleGatherNd *) final; + // void visit(const luci::CircleGreater *) final; + // void visit(const luci::CircleGreaterEqual *) final; + // void visit(const luci::CircleIf *) final; + // void visit(const luci::CircleL2Normalize *) final; + // void visit(const luci::CircleL2Pool2D *) final; + // void visit(const luci::CircleLeakyRelu *) final; + // void visit(const luci::CircleLess *) final; + // void visit(const luci::CircleLessEqual *) final; + // void visit(const luci::CircleLocalResponseNormalization *) final; + // void visit(const luci::CircleLog *) final; + // void visit(const luci::CircleLogicalAnd *) final; + // void visit(const luci::CircleLogicalNot *) final; + // void visit(const luci::CircleLogicalOr *) final; + // void visit(const luci::CircleLogistic *) final; + // void visit(const luci::CircleLogSoftmax *) final; + // void visit(const luci::CircleMatrixDiag *) final; + // void visit(const luci::CircleMatrixSetDiag *) final; + // void visit(const luci::CircleMaximum *) final; + // void visit(const luci::CircleMaxPool2D *) final; + void visit(const luci::CircleMean *) final; + // void visit(const luci::CircleMinimum *) final; + // void visit(const luci::CircleMirrorPad *) final; + void visit(const luci::CircleMul *) final; + // void visit(const luci::CircleNeg *) final; + // void visit(const luci::CircleNonMaxSuppressionV4 *) final; + // void visit(const luci::CircleNonMaxSuppressionV5 *) final; + // void visit(const luci::CircleNotEqual *) final; + // void visit(const luci::CircleOneHot *) final; + // void visit(const luci::CirclePack *) final; + // void visit(const luci::CirclePad *) final; + // void visit(const luci::CirclePadV2 *) final; + void visit(const luci::CirclePow *) final; + // void visit(const luci::CirclePRelu *) final; + // void visit(const luci::CircleRange *) final; + // void visit(const luci::CircleRank *) final; + // void visit(const luci::CircleReduceAny *) final; + // void visit(const luci::CircleReduceMax *) final; + // void visit(const luci::CircleReduceMin *) final; + // void visit(const luci::CircleReduceProd *) final; + // void visit(const luci::CircleRelu *) final; + // void visit(const luci::CircleRelu6 *) final; + // void visit(const luci::CircleReluN1To1 *) final; + // void visit(const luci::CircleReshape *) final; + // void visit(const luci::CircleResizeBilinear *) final; + // void visit(const luci::CircleResizeNearestNeighbor *) final; + // void visit(const luci::CircleReverseSequence *) final; + // void visit(const luci::CircleReverseV2 *) final; + // void visit(const luci::CircleRound *) final; + void visit(const luci::CircleRsqrt *) final; + // void visit(const luci::CircleScatterNd *) final; + // void visit(const luci::CircleSegmentSum *) final; + // void visit(const luci::CircleSelect *) final; + // void visit(const luci::CircleSelectV2 *) final; + // void visit(const luci::CircleShape *) final; + // void visit(const luci::CircleSin *) final; + // void visit(const luci::CircleSlice *) final; + // void visit(const luci::CircleSoftmax *) final; + // void visit(const luci::CircleSpaceToBatchND *) final; + // void visit(const luci::CircleSpaceToDepth *) final; + // void visit(const luci::CircleSparseToDense *) final; + // void visit(const luci::CircleSplit *) final; + // void visit(const luci::CircleSplitV *) final; + void visit(const luci::CircleSqrt *) final; + // void visit(const luci::CircleSquare *) final; + void visit(const luci::CircleSquaredDifference *) final; + // void visit(const luci::CircleSqueeze *) final; + // void visit(const luci::CircleStridedSlice *) final; + void visit(const luci::CircleSub *) final; + // void visit(const luci::CircleSum *) final; + // void visit(const luci::CircleTanh *) final; + // void visit(const luci::CircleTile *) final; + // void visit(const luci::CircleTopKV2 *) final; + // void visit(const luci::CircleTranspose *) final; + // void visit(const luci::CircleTransposeConv *) final; + // void visit(const luci::CircleUnidirectionalSequenceLSTM *) final; + // void visit(const luci::CircleUnique *) final; + // void visit(const luci::CircleUnpack *) final; + // void visit(const luci::CircleWhere *) final; + // void visit(const luci::CircleWhile *) final; + // void visit(const luci::CircleZerosLike *) final; + + // Circle Only + // void visit(const luci::CircleBCQFullyConnected *) final; + // void visit(const luci::CircleBCQGather *) final; + // void visit(const luci::CircleInstanceNorm *) final; + + // Virtual + // void visit(const luci::CircleCustomOut *) final; + // void visit(const luci::CircleIfOut *) final; + // void visit(const luci::CircleInput *) final; + // void visit(const luci::CircleNonMaxSuppressionV4Out *) final; + // void visit(const luci::CircleNonMaxSuppressionV5Out *) final; + // void visit(const luci::CircleOutput *) final; + // void visit(const luci::CircleOutputDummy *) final; + // void visit(const luci::CircleOutputExclude *) final; + // void visit(const luci::CircleSplitOut *) final; + // void visit(const luci::CircleSplitVOut *) final; + // void visit(const luci::CircleTopKV2Out *) final; + // void visit(const luci::CircleUniqueOut *) final; + // void visit(const luci::CircleUnpackOut *) final; + // void visit(const luci::CircleWhileOut *) final; + +public: + luci::CircleNode *find_clone(const luci::CircleNode *node); + +protected: + luci::CloneContext &_clonecontext; +}; + +/** + * @brief Connect cloned node from input node + */ +void clone_connect(const luci::CircleNode *node, luci::CloneContext &clonecontext); + +} // namespace luci + +#endif // __LUCI_PARTITION_CONNECT_NODE_H__ diff --git a/compiler/stdex/include/stdex/Memory.h b/compiler/luci/partition/src/ConnectNode.test.cpp index 86751f073..a2009c654 100644 --- a/compiler/stdex/include/stdex/Memory.h +++ b/compiler/luci/partition/src/ConnectNode.test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,16 +14,6 @@ * limitations under the License. */ -#ifndef __STDEX_MEMORY_H__ -#define __STDEX_MEMORY_H__ +#include "ConnectNode.test.h" -#include <memory> - -namespace stdex -{ - -using std::make_unique; - -} // namespace stdex - -#endif // __STDEX_MEMORY_H__ +// This file validates "ConnectNode.test.h". Please DO NOT remove this file. diff --git a/compiler/luci/partition/src/ConnectNode.test.h b/compiler/luci/partition/src/ConnectNode.test.h new file mode 100644 index 000000000..f7333ff99 --- /dev/null +++ b/compiler/luci/partition/src/ConnectNode.test.h @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021 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 __CONNECT_NODE_TEST_H__ +#define __CONNECT_NODE_TEST_H__ + +#include "ConnectNode.h" + +#include <luci/Service/CircleNodeClone.h> +#include <luci/test/TestIOGraph.h> + +#include <loco/IR/Graph.h> + +#include <initializer_list> +#include <memory> +#include <stdexcept> +#include <vector> + +namespace luci +{ +namespace test +{ + +template <unsigned N> class TestIsOGraph : public TestIsGraphlet<N>, public TestOGraphlet +{ +public: + TestIsOGraph() = default; + +public: + virtual void init(const std::initializer_list<ShapeU32> shape_in, const ShapeU32 shape_out) + { + if (shape_in.size() != N) + throw std::runtime_error("Failed to init TestIsOGraph"); + + TestIsGraphlet<N>::init(TestIsGraphlet<N>::g(), shape_in); + TestOGraphlet::init(TestIsGraphlet<N>::g(), shape_out); + } +}; + +template <class T> class NodeGraphletT +{ +public: + virtual void init(loco::Graph *g) + { + _node = g->nodes()->create<T>(); + _node->dtype(loco::DataType::S32); + _node->name("node"); + } + + T *node(void) const { return _node; } + +protected: + T *_node{nullptr}; +}; + +template <class T> class NodeIsGraphletT +{ +public: + virtual void init(loco::Graph *g, uint32_t n) + { + _node = g->nodes()->create<T>(n); + _node->dtype(loco::DataType::S32); + _node->name("node"); + } + + T *node(void) const { return _node; } + +protected: + T *_node{nullptr}; +}; + +/** + * @brief ConnectionTestHelper provides common framework for testing + * cloned CircleNode connection + */ +class ConnectionTestHelper +{ +public: + ConnectionTestHelper() { _graph_clone = loco::make_graph(); } + +public: + template <unsigned N> void prepare_inputs(TestIsOGraph<N> *isograph) + { + assert(N == isograph->num_inputs()); + + for (uint32_t i = 0; i < N; ++i) + { + auto *input = _graph_clone->nodes()->create<luci::CircleInput>(); + luci::copy_common_attributes(isograph->input(i), input); + _clonectx.emplace(isograph->input(i), input); + _inputs.push_back(input); + } + } + + /** + * @note prepare_inputs_miss is for negative testing + */ + template <unsigned N> void prepare_inputs_miss(TestIsOGraph<N> *isograph) + { + assert(N == isograph->num_inputs()); + + for (uint32_t i = 0; i < N; ++i) + { + auto *input = _graph_clone->nodes()->create<luci::CircleInput>(); + luci::copy_common_attributes(isograph->input(i), input); + if (i != 0) + _clonectx.emplace(isograph->input(i), input); + _inputs.push_back(input); + } + } + + void clone_connect(luci::CircleNode *node, luci::CircleNode *clone) + { + _clonectx.emplace(node, clone); + + luci::clone_connect(node, _clonectx); + } + +public: + loco::Graph *graph_clone(void) { return _graph_clone.get(); } + + luci::CircleNode *inputs(uint32_t idx) { return _inputs.at(idx); } + +protected: + luci::CloneContext _clonectx; + std::vector<luci::CircleInput *> _inputs; + std::unique_ptr<loco::Graph> _graph_clone; // graph for clones +}; + +} // namespace test +} // namespace luci + +#endif // __CONNECT_NODE_TEST_H__ diff --git a/compiler/luci/partition/src/Nodes/CircleAdd.cpp b/compiler/luci/partition/src/Nodes/CircleAdd.cpp new file mode 100644 index 000000000..d393997e9 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleAdd.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleAdd *node) +{ + auto *cloned = loco::must_cast<luci::CircleAdd *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleAdd *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp b/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp new file mode 100644 index 000000000..e457b83d2 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleAdd.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleAdd> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) override + { + NodeGraphletT<luci::CircleAdd>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Add) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Add_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleAdd *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/service/src/Nodes/CircleInput.cpp b/compiler/luci/partition/src/Nodes/CircleConst.cpp index 24eab7bd6..118cd8de2 100644 --- a/compiler/luci/service/src/Nodes/CircleInput.cpp +++ b/compiler/luci/partition/src/Nodes/CircleConst.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,14 +14,14 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "ConnectNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleInput *node) +void ConnectNode::visit(const luci::CircleConst *) { - return node->shape_signature(); + // Nothing to do } } // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleDiv.cpp b/compiler/luci/partition/src/Nodes/CircleDiv.cpp new file mode 100644 index 000000000..480338542 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleDiv.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleDiv *node) +{ + auto *cloned = loco::must_cast<luci::CircleDiv *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleDiv *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp b/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp new file mode 100644 index 000000000..226932337 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleDiv.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleDiv> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) override + { + NodeGraphletT<luci::CircleDiv>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Div) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Div_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleDiv *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CircleMean.cpp b/compiler/luci/partition/src/Nodes/CircleMean.cpp new file mode 100644 index 000000000..b634e5838 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleMean.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleMean *node) +{ + auto *cloned = loco::must_cast<luci::CircleMean *>(cn->find_clone(node)); + + luci::CircleNode *input = loco::must_cast<luci::CircleNode *>(node->input()); + luci::CircleNode *reduction_indices = + loco::must_cast<luci::CircleNode *>(node->reduction_indices()); + + cloned->input(cn->find_clone(input)); + cloned->reduction_indices(cn->find_clone(reduction_indices)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleMean *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleMul.cpp b/compiler/luci/partition/src/Nodes/CircleMul.cpp new file mode 100644 index 000000000..2cd2b4038 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleMul.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleMul *node) +{ + auto *cloned = loco::must_cast<luci::CircleMul *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleMul *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleMul.test.cpp b/compiler/luci/partition/src/Nodes/CircleMul.test.cpp new file mode 100644 index 000000000..99cf0824d --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleMul.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleMul> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) + { + NodeGraphletT<luci::CircleMul>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Mul) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Mul_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleMul *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Nodes/CirclePow.cpp b/compiler/luci/partition/src/Nodes/CirclePow.cpp new file mode 100644 index 000000000..fb180ee69 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CirclePow.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CirclePow *node) +{ + auto *cloned = loco::must_cast<luci::CirclePow *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CirclePow *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp b/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp new file mode 100644 index 000000000..03e64aad0 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleRsqrt.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleRsqrt *node) +{ + auto *cloned = loco::must_cast<luci::CircleRsqrt *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + + cloned->x(cn->find_clone(x)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleRsqrt *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSqrt.cpp b/compiler/luci/partition/src/Nodes/CircleSqrt.cpp new file mode 100644 index 000000000..f737aac8d --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSqrt.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleSqrt *node) +{ + auto *cloned = loco::must_cast<luci::CircleSqrt *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + + cloned->x(cn->find_clone(x)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleSqrt *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp new file mode 100644 index 000000000..40dd31706 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSquaredDifference.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleSquaredDifference *node) +{ + auto *cloned = loco::must_cast<luci::CircleSquaredDifference *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleSquaredDifference *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSub.cpp b/compiler/luci/partition/src/Nodes/CircleSub.cpp new file mode 100644 index 000000000..8ac294b7b --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSub.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +namespace +{ + +void connect(luci::ConnectNode *cn, const luci::CircleSub *node) +{ + auto *cloned = loco::must_cast<luci::CircleSub *>(cn->find_clone(node)); + + luci::CircleNode *x = loco::must_cast<luci::CircleNode *>(node->x()); + luci::CircleNode *y = loco::must_cast<luci::CircleNode *>(node->y()); + + cloned->x(cn->find_clone(x)); + cloned->y(cn->find_clone(y)); +} + +} // namespace + +namespace luci +{ + +void ConnectNode::visit(const luci::CircleSub *node) { connect(this, node); } + +} // namespace luci diff --git a/compiler/luci/partition/src/Nodes/CircleSub.test.cpp b/compiler/luci/partition/src/Nodes/CircleSub.test.cpp new file mode 100644 index 000000000..7c0d83745 --- /dev/null +++ b/compiler/luci/partition/src/Nodes/CircleSub.test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 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 "ConnectNode.h" + +#include "ConnectNode.test.h" + +#include <luci/Service/CircleNodeClone.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class NodeGraphlet : public NodeGraphletT<luci::CircleSub> +{ +public: + NodeGraphlet() = default; + +public: + void init(loco::Graph *g) + { + NodeGraphletT<luci::CircleSub>::init(g); + + _node->fusedActivationFunction(luci::FusedActFunc::RELU); + } +}; + +class TestNodeGraph : public TestIsOGraph<2>, public NodeGraphlet +{ +public: + TestNodeGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIsOGraph<2>::init({shape, shape}, shape); + NodeGraphlet::init(g()); + + node()->x(input(0)); + node()->y(input(1)); + + output()->from(node()); + } +}; + +} // namespace + +TEST(ConnectNodeTest, connect_Sub) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(clone)); + + cth.clone_connect(node, clone); + + ASSERT_EQ(2, clone->arity()); + ASSERT_EQ(cth.inputs(0), clone->arg(0)); + ASSERT_EQ(cth.inputs(1), clone->arg(1)); +} + +TEST(ConnectNodeTest, connect_Sub_NEG) +{ + TestNodeGraph tng; + tng.init({2, 3}); + + ConnectionTestHelper cth; + cth.prepare_inputs_miss(&tng); + + auto *node = tng.node(); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(node)); + + auto *clone = luci::clone_node(node, cth.graph_clone()); + ASSERT_NO_THROW(loco::must_cast<luci::CircleSub *>(clone)); + + EXPECT_ANY_THROW(cth.clone_connect(node, clone)); +} diff --git a/compiler/luci/partition/src/Partition.cpp b/compiler/luci/partition/src/Partition.cpp new file mode 100644 index 000000000..cc7106ca9 --- /dev/null +++ b/compiler/luci/partition/src/Partition.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 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 "PartitionIR.h" +#include "PartitionIRDump.h" +#include "PartitionPGroups.h" +#include "PartitionMerge.h" +#include "PartitionCleanup.h" +#include "PartitionPModules.h" +#include "PartitionPModulesDump.h" + +#include "luci/Partition.h" +#include "luci/Log.h" + +#include <cassert> + +namespace luci +{ + +/** + * @brief This will return Partitioned Modules object + */ +PartedModules apply(Module *source, const PartitionTable &partition) +{ + assert(source != nullptr); + + LOGGER(l); + + auto pgroups = produce_pgroups(source, partition); + INFO(l) << "--- Partition Graph (1)------------------------"; + INFO(l) << pgroups.get(); + + auto mpgroups = merge_pgroups(pgroups.get()); + INFO(l) << "--- Partition Graph (2)------------------------"; + INFO(l) << mpgroups.get(); + + remove_unused_inputoutputs(mpgroups.get(), source); + INFO(l) << "--- Partition Graph (3)------------------------"; + INFO(l) << mpgroups.get(); + + auto pmodules = produce_pmodules(mpgroups.get()); + INFO(l) << "--- Modules -----------------------------------"; + INFO(l) << &pmodules; + + return pmodules; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/Partition.test.cpp b/compiler/luci/partition/src/Partition.test.cpp new file mode 100644 index 000000000..9e24c441c --- /dev/null +++ b/compiler/luci/partition/src/Partition.test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 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/Partition.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->dtype(loco::DataType::S32); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class SqrtGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + SqrtGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + SqrtGraphlet::init(g(), shape); + + _sqrt->x(input()); + + output()->from(_sqrt); + } +}; + +} // namespace + +TEST(PartitionTest, simple_apply) +{ + luci::Module module; + + SqrtGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + luci::PartitionTable pt; + pt.default_group = "A"; + + auto pms = apply(&module, pt); + + ASSERT_EQ(1, pms.pmodules.size()); + + auto &pm = *pms.pmodules.begin(); + ASSERT_NE(nullptr, pm.module->graph()); +} diff --git a/compiler/luci/partition/src/PartitionCleanup.cpp b/compiler/luci/partition/src/PartitionCleanup.cpp new file mode 100644 index 000000000..6545295df --- /dev/null +++ b/compiler/luci/partition/src/PartitionCleanup.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 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 "PartitionCleanup.h" + +#include "luci/Log.h" + +namespace +{ + +using CircleNodes = std::vector<luci::CircleNode *>; + +/** + * @note Original source outputs should be outputs + */ +void gather_graph_outputs(CircleNodes &nodes, const luci::Module *source) +{ + // graph outputs are treated as used + auto graph = source->graph(); + for (uint32_t n = 0; n < graph->outputs()->size(); ++n) + { + auto output = luci::output_node(graph, n); // output is CircleOutput + assert(output != nullptr); + + auto node = loco::must_cast<luci::CircleNode *>(output->from()); + + nodes.push_back(node); + } + + // TODO add unused virtual outputs +} + +/** + * @note If one PGroup requires an input, that input should be an output + * from another PGroup + */ +void gather_pgroups_outputs(CircleNodes &nodes, const luci::PGroups *pgroups) +{ + // input of a pgroup is used output + for (auto &pgroup : pgroups->pgroups) + { + for (auto input : pgroup->inputs) + { + nodes.push_back(input); + } + } +} + +} // namespace + +namespace luci +{ + +void remove_unused_inputoutputs(luci::PGroups *pgroups, const luci::Module *source) +{ + assert(source != nullptr); + assert(pgroups != nullptr); + + LOGGER(l); + + // TODO support multiple subgraph + assert(source->size() == 1); + + INFO(l) << "--- Cleanup unused inputs/outputs"; + + // remove input within same pgroup + for (auto &pgroup : pgroups->pgroups) + { + bool changed; + do + { + changed = false; + for (auto it = pgroup->inputs.begin(); it != pgroup->inputs.end(); ++it) + { + auto input = *it; + if (pgroups->pgroup_of(input) == pgroup.get()) + { + INFO(l) << " Cleanup input " << input->name() << " from group " << pgroup->group; + pgroup->inputs.erase(it); + changed = true; + break; + } + // NOTE CircleConst is one of input type, as they are registered as + // input to some node and then (should be) merged. + // Remove if this input is CircleConst + if (dynamic_cast<CircleConst *>(input) != nullptr) + { + INFO(l) << " Cleanup CircleConst " << input->name() << " from group " << pgroup->group; + pgroup->inputs.erase(it); + changed = true; + break; + } + } + } while (changed); + } + + // remove unused output(s) + // 'used_outputs' will hold actual used outputs for all PGroups + CircleNodes used_outputs; + + gather_graph_outputs(used_outputs, source); + gather_pgroups_outputs(used_outputs, pgroups); + + for (auto &pgroup : pgroups->pgroups) + { + bool changed; + do + { + changed = false; + for (auto it = pgroup->outputs.begin(); it != pgroup->outputs.end(); ++it) + { + auto output = *it; + auto oit = std::find(used_outputs.begin(), used_outputs.end(), output); + if (oit == used_outputs.end()) + { + INFO(l) << " Cleanup output " << output->name() << " from group " << pgroup->group; + pgroup->outputs.erase(it); + changed = true; + break; + } + } + } while (changed); + } +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionCleanup.h b/compiler/luci/partition/src/PartitionCleanup.h new file mode 100644 index 000000000..f81b4a7cb --- /dev/null +++ b/compiler/luci/partition/src/PartitionCleanup.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITON_CLEANUP_H__ +#define __LUCI_PARTITON_CLEANUP_H__ + +#include "PartitionIR.h" + +#include <luci/IR/Module.h> + +namespace luci +{ + +/** + * @brief This will remove unused inputs/outputs in each pgroup of pgroups + */ +void remove_unused_inputoutputs(luci::PGroups *, const luci::Module *); + +} // namespace luci + +#endif // __LUCI_PARTITON_CLEANUP_H__ diff --git a/compiler/luci/partition/src/PartitionIR.cpp b/compiler/luci/partition/src/PartitionIR.cpp new file mode 100644 index 000000000..ebd6b25fa --- /dev/null +++ b/compiler/luci/partition/src/PartitionIR.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 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 "PartitionIR.h" +#include "CircleOpCode.h" + +#include "luci/Log.h" + +#include <cassert> +#include <ostream> +#include <iostream> + +namespace luci +{ + +std::unique_ptr<PGroups> PGroups::make_copy(void) const +{ + auto d_pgroups = std::make_unique<luci::PGroups>(); + + for (auto &s_pgroup : pgroups) + { + // make a copy of s_pgroup to d_pgroup + std::unique_ptr<luci::PGroup> d_pgroup = std::make_unique<luci::PGroup>(); + + d_pgroup->group = s_pgroup->group; + d_pgroup->id = s_pgroup->id; + + for (auto &pnode : s_pgroup->pnodes) + { + auto pnodec = std::make_unique<luci::PNode>(); + pnodec->node = pnode->node; + pnodec->group = pnode->group; + pnodec->pgroup = d_pgroup.get(); + d_pgroup->pnodes.push_back(std::move(pnodec)); + } + + for (auto &input : s_pgroup->inputs) + d_pgroup->inputs.push_back(input); + + for (auto &output : s_pgroup->outputs) + d_pgroup->outputs.push_back(output); + + // copy node2group + for (auto it = node2group.begin(); it != node2group.end(); ++it) + d_pgroups->node2group[it->first] = it->second; + + // build id2pgroup + d_pgroups->id2pgroup[d_pgroup->id] = d_pgroup.get(); + + d_pgroups->pgroups.push_back(std::move(d_pgroup)); + // note: d_pgroup is now nullptr as it's moved + } + + return std::move(d_pgroups); +} + +std::string PGroups::group_of(luci::CircleNode *node) const +{ + assert(node != nullptr); + + LOGGER(l); + + auto it = node2group.find(node); + if (it == node2group.end()) + { + INFO(l) << "PGroups::group_of " << node << "(" << node->name() << ") not found" << std::endl; + return ""; + } + return it->second; +} + +const PGroup *PGroups::pgroup_of(luci::CircleNode *node) const +{ + assert(node != nullptr); + + for (auto &pgroup : pgroups) + { + for (auto &pnode : pgroup->pnodes) + { + if (node == pnode->node) + return pgroup.get(); + } + } + // node maybe graph input (CircleInput) + return nullptr; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionIR.h b/compiler/luci/partition/src/PartitionIR.h new file mode 100644 index 000000000..852e38cc0 --- /dev/null +++ b/compiler/luci/partition/src/PartitionIR.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITION_IR_H__ +#define __LUCI_PARTITION_IR_H__ + +#include <luci/IR/CircleNodes.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +namespace luci +{ + +struct PGroup; + +/** + * @brief Partition Node with CircleNode with group name + * @note node just points to source luci::CircleNode, NOT the cloned node + * CloneContext is used to find cloned node from source node + */ +struct PNode +{ + const luci::CircleNode *node = nullptr; + std::string group; + + const PGroup *pgroup = nullptr; +}; + +/** + * @brief Partition Group with Partition Nodes of same group and I/Os nodes + */ +struct PGroup +{ + std::vector<std::unique_ptr<PNode>> pnodes; + std::string group; + uint32_t id = 0; + + // I/O while partitioning + std::vector<luci::CircleNode *> inputs; + std::vector<luci::CircleNode *> outputs; +}; + +struct PGroups +{ + std::vector<std::unique_ptr<PGroup>> pgroups; + + // node2group is to find group key from source node + std::map<const luci::CircleNode *, std::string> node2group; + + // id2pngroup is to find *pngroup from pngroup id + std::map<uint32_t, PGroup *> id2pgroup; + + // default group key for reference + std::string default_group; + +public: + /** + * @brief return a copy of PGroups + */ + std::unique_ptr<PGroups> make_copy(void) const; + + /** + * @brief return group key of node, empty string if not found + */ + std::string group_of(luci::CircleNode *node) const; + + /** + * @brief return holding pgroup of node, nullptr if not found + */ + const PGroup *pgroup_of(luci::CircleNode *node) const; +}; + +} // namespace luci + +#endif // __LUCI_PARTITION_IR_H__ diff --git a/compiler/luci/partition/src/PartitionIR.test.cpp b/compiler/luci/partition/src/PartitionIR.test.cpp new file mode 100644 index 000000000..4c051a96d --- /dev/null +++ b/compiler/luci/partition/src/PartitionIR.test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 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 "PartitionIR.h" + +// NOTE any node will do for testing +#include <luci/IR/Nodes/CircleAdd.h> + +#include <gtest/gtest.h> + +#include <memory> + +TEST(PartitionIRTest, PNode_ctor) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleAdd>(); + + luci::PNode pnode; + pnode.node = node; + + ASSERT_NE(nullptr, pnode.node); + ASSERT_EQ(nullptr, pnode.pgroup); +} + +// TODO add more tests with luci::PNode + +TEST(PartitionIRTest, PGroup_ctor) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleAdd>(); + + luci::PGroup pgroup; + auto pnode = std::make_unique<luci::PNode>(); + pnode->node = node; + + pgroup.pnodes.push_back(std::move(pnode)); + + ASSERT_NE(pgroup.pnodes.end(), pgroup.pnodes.begin()); + ASSERT_EQ(0, pgroup.inputs.size()); + ASSERT_EQ(0, pgroup.outputs.size()); +} + +// TODO add more tests with luci::PGroup + +TEST(PartitionIRTest, PGroups_ctor) +{ + auto g = loco::make_graph(); + auto node = g->nodes()->create<luci::CircleAdd>(); + + auto pnode = std::make_unique<luci::PNode>(); + pnode->node = node; + + auto pgroup = std::make_unique<luci::PGroup>(); + pgroup->pnodes.push_back(std::move(pnode)); + + luci::PGroups pgroups; + pgroups.pgroups.push_back(std::move(pgroup)); + + ASSERT_NE(pgroups.pgroups.end(), pgroups.pgroups.begin()); +} + +// TODO add more tests with luci::PGroups diff --git a/compiler/luci/partition/src/PartitionIRDump.cpp b/compiler/luci/partition/src/PartitionIRDump.cpp new file mode 100644 index 000000000..4f2c26800 --- /dev/null +++ b/compiler/luci/partition/src/PartitionIRDump.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 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 "PartitionIRDump.h" + +#include "CircleOpCode.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PNode *pnode) +{ + os << "PNode: " << pnode->group << ", " << pnode->node << ":" << luci::opcode_name(pnode->node) + << ":" << pnode->node->name() << std::endl; +} + +void dump(std::ostream &os, const PGroup *pgroup) +{ + os << "--- PGroup: " << pgroup->group << std::endl; + os << "Input(s): "; + for (auto &node_in : pgroup->inputs) + os << node_in->name() << " "; + os << std::endl; + for (auto &pnode : pgroup->pnodes) + { + dump(os, pnode.get()); + } + os << "Output(s): "; + for (auto &node_out : pgroup->outputs) + os << node_out->name() << " "; + os << std::endl; +} + +void dump(std::ostream &os, const PGroups *pgroups) +{ + for (auto &pgroup : pgroups->pgroups) + { + dump(os, pgroup.get()); + } + os << "--- Node2Group items: " << std::endl; + for (auto it = pgroups->node2group.begin(); it != pgroups->node2group.end(); ++it) + { + auto node = it->first; + auto group = it->second; + os << " Node: " << node << "(" << node->name() << "): " << group << std::endl; + } +} + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PGroups *pgroups) +{ + luci::dump(os, pgroups); + return os; +} diff --git a/compiler/luci/partition/src/PartitionIRDump.h b/compiler/luci/partition/src/PartitionIRDump.h new file mode 100644 index 000000000..8a4b3f579 --- /dev/null +++ b/compiler/luci/partition/src/PartitionIRDump.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITION_IR_DUMP_H__ +#define __LUCI_PARTITION_IR_DUMP_H__ + +#include "PartitionIR.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PNode *pnode); +void dump(std::ostream &os, const PGroup *pgroup); +void dump(std::ostream &os, const PGroups *pgroups); + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PGroups *pgroups); + +#endif // __LUCI_PARTITION_IR_DUMP_H__ diff --git a/compiler/luci/partition/src/PartitionMerge.cpp b/compiler/luci/partition/src/PartitionMerge.cpp new file mode 100644 index 000000000..038fc2a0c --- /dev/null +++ b/compiler/luci/partition/src/PartitionMerge.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2021 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 "PartitionMerge.h" + +#include <algorithm> + +namespace +{ + +/** + * @brief return true if pgroup_i output is one of the inputs of pgroup + */ +bool is_input_of(const luci::PGroup *pgroup_i, const luci::PGroup *pgroup) +{ + for (auto *output : pgroup_i->outputs) + { + for (auto *input : pgroup->inputs) + { + if (input == output) + return true; + } + } + return false; +} + +/** + * @brief return true if there is only one input or all the inputs have same group + * @note pgroups is used to find group of pgroup + */ +bool is_input_same(const luci::PGroup *pgroup, const luci::PGroups *pgroups) +{ + assert(pgroups != nullptr); + assert(pgroup != nullptr); + + const luci::PGroup *input_pgroup = nullptr; + std::string group; + for (auto &input : pgroup->inputs) + { + auto input_group = pgroups->group_of(input); + // NOTE: all the nodes should be registered and return should be valid group. + // convert_to_proups() should ensure this. + // assert here to find if there is any problem with this. + assert(not input_group.empty()); + if (input_group.empty()) + input_group = pgroups->default_group; + + if (group.empty()) + group = input_group; + else + { + if (group != input_group) + return false; + } + // if there are multiple inputs, all the inputs should be in same pgroup + // https://github.com/Samsung/ONE/issues/6230#issuecomment-801618150 + // https://github.com/Samsung/ONE/issues/6230#issuecomment-801680531 + auto pgroup_input = pgroups->pgroup_of(input); + if (pgroup_input != nullptr) + { + if (input_pgroup == nullptr) + input_pgroup = pgroup_input; + else + { + if (input_pgroup != pgroup_input) + return false; + } + } + } + return true; +} + +/** + * @brief merge pgroup into pgroup_i + * @note output of pgroup_i should be input of pgroup + */ +void merge_into(luci::PGroup *pgroup, luci::PGroup *pgroup_i) +{ + for (auto &pnode : pgroup->pnodes) + { + // update pgroup for this pnode + pnode->pgroup = pgroup_i; + assert(pnode->group == pgroup_i->group); + + // we don't need to add this in topological order: + // all the nodes will be created first then connection will be held + pgroup_i->pnodes.push_back(std::move(pnode)); + // note: pnode is now nullptr as it's moved into pgroup_i->pnodes + } + + for (auto &input : pgroup->inputs) + { + // add inputs of pgroup to pgroup_i if not member of pgroup_i + bool found_in_pgroup_i = false; + for (auto &pnode : pgroup_i->pnodes) + { + if (input == pnode->node) + { + found_in_pgroup_i = true; + break; + } + } + // skip if this input is already in the inputs + auto fit = std::find(pgroup_i->inputs.begin(), pgroup_i->inputs.end(), input); + if (fit != pgroup_i->inputs.end()) + { + found_in_pgroup_i = true; + } + // note: if we force found_in_pgroup_i to false, for testing there will be + // unnecessary inputs + if (not found_in_pgroup_i) + { + // node input maybe in another pgroup + pgroup_i->inputs.push_back(input); + } + } + // add outputs of pgroup to pgroup_i outputs if not exist + for (auto &output : pgroup->outputs) + { + auto it = std::find(pgroup_i->outputs.begin(), pgroup_i->outputs.end(), output); + if (it == pgroup_i->outputs.end()) + { + pgroup_i->outputs.push_back(output); + } + } +} + +} // namespace + +namespace luci +{ + +/** + * @brief This will merge pgroups with same group values in topological order + */ +std::unique_ptr<luci::PGroups> merge_pgroups(const luci::PGroups *s_pgroups) +{ + // Make a copy of pgroups to apply merge action + // Q) do we really need a copy? + auto d_pgroups = s_pgroups->make_copy(); + + // Merge partition graphs + // - This is initial implementation that works for limited networks + // - if A and B is same group -> if A is input of B -> ... -> merge B into A + auto &pgroups = d_pgroups->pgroups; + bool changed; + do + { + changed = false; + for (auto &pgroup_i : pgroups) + { + bool merged = false; + for (auto it = pgroups.begin(); it != pgroups.end(); ++it) + { + auto &pgroup = *it; + + // skip if same object + if (pgroup->id == pgroup_i->id) + continue; + // skip if different group + if (pgroup->group != pgroup_i->group) + continue; + // skip if not connected + if (!is_input_of(pgroup_i.get(), pgroup.get())) + continue; + // skip if there are multiple inputs but inputs differ in group + if (!is_input_same(pgroup.get(), d_pgroups.get())) + continue; + // TODO add more condition may be needed + + merge_into(pgroup.get(), pgroup_i.get()); + + auto eit = d_pgroups->id2pgroup.find(pgroup->id); + assert(eit != d_pgroups->id2pgroup.end()); + d_pgroups->id2pgroup.erase(eit); + + // remove merged pgroup from pgroups + pgroups.erase(it); + + merged = true; + break; + } + if (merged) + { + changed = true; + break; + } + } + } while (changed); + + return std::move(d_pgroups); +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionMerge.h b/compiler/luci/partition/src/PartitionMerge.h new file mode 100644 index 000000000..5c9fec2d2 --- /dev/null +++ b/compiler/luci/partition/src/PartitionMerge.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITON_MERGE_H__ +#define __LUCI_PARTITON_MERGE_H__ + +#include "PartitionIR.h" + +#include <memory> + +namespace luci +{ + +std::unique_ptr<luci::PGroups> merge_pgroups(const luci::PGroups *s_pgroups); + +} // namespace luci + +#endif // __LUCI_PARTITON_MERGE_H__ diff --git a/compiler/luci/partition/src/PartitionPGroups.cpp b/compiler/luci/partition/src/PartitionPGroups.cpp new file mode 100644 index 000000000..594ed6c40 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPGroups.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 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 "PartitionPGroups.h" +#include "PartitionIR.h" +#include "CircleOpCode.h" + +#include "luci/Partition.h" +#include "luci/Log.h" +#include "luci/LogHelper.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> + +#include <loco.h> + +namespace +{ + +class IsVirtualNode final : public luci::CircleNodeVisitor<bool> +{ +public: + bool visit(const luci::CircleInput *) final { return true; } + bool visit(const luci::CircleOutput *) final { return true; } + // TODO add all virtual nodes + + // default is false + bool visit(const luci::CircleNode *) final { return false; } +}; + +bool check_allocate_partition(const luci::CircleNode *node) +{ + IsVirtualNode query; + if (node->accept(&query)) + return false; + /** + * @note About CircleConst + * CirleConst acts like a part of some CircleNode and managing mulitiple + * used(referenced) CircleConst is a bit difficult if it's used across + * different PGroup. So we treat this different to other types. + * https://github.com/Samsung/ONE/issues/6230#issuecomment-809802813 + */ + if (dynamic_cast<const luci::CircleConst *>(node) != nullptr) + return false; + return true; +} + +} // namespace + +namespace luci +{ + +std::unique_ptr<luci::PGroups> produce_pgroups(const luci::Module *source, + const luci::PartitionTable &partition) +{ + assert(source != nullptr); + // TODO support multiple subgraphs + assert(source->size() == 1); + + LOGGER(l); + + auto pgroups = std::make_unique<luci::PGroups>(); + + pgroups->default_group = partition.default_group; + + // Create a PGroup per CircleNode: each PGroup will have one CircleNode + auto graph = source->graph(); + auto nodes = graph->nodes(); + for (uint32_t idx = 0; idx < nodes->size(); ++idx) + { + auto node = loco::must_cast<luci::CircleNode *>(nodes->at(idx)); + + // check if node is normal node that we are interested + if (check_allocate_partition(node)) + { + auto opcodename = luci::opcode_name(node); + assert(!opcodename.empty()); + + auto group = partition.default_group; + auto it = partition.byopcodes.find(opcodename); + if (it != partition.byopcodes.end()) + group = it->second; + + INFO(l) << "Op: " << node->name() << ": " << opcodename << ", " << node << ", " << group + << std::endl; + + auto pgroup = std::make_unique<luci::PGroup>(); + pgroup->group = group; + pgroup->id = idx + 1; + + auto pnode = std::make_unique<luci::PNode>(); + pnode->node = node; + pnode->group = group; + pnode->pgroup = pgroup.get(); + + pgroup->pnodes.push_back(std::move(pnode)); + + // Set input of PGroup + for (uint32_t in = 0; in < node->arity(); ++in) + { + auto input = loco::must_cast<luci::CircleNode *>(node->arg(in)); + // this input maybe CircleInput in source graph + // --> not confident this is safe + pgroup->inputs.push_back(input); + } + // Set output of PGroup: node itself or multiple virtual outputs + // TODO support multiple virtual outputs + pgroup->outputs.push_back(node); + + pgroups->node2group[node] = group; + pgroups->id2pgroup[pgroup->id] = pgroup.get(); + + pgroups->pgroups.push_back(std::move(pgroup)); + } + else + { + INFO(l) << "Skip Op: " << node->name() << std::endl; + // record as default group + pgroups->node2group[node] = partition.default_group; + } + } + + return std::move(pgroups); +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionPGroups.h b/compiler/luci/partition/src/PartitionPGroups.h new file mode 100644 index 000000000..998e11cbd --- /dev/null +++ b/compiler/luci/partition/src/PartitionPGroups.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITON_PGROUPS_H__ +#define __LUCI_PARTITON_PGROUPS_H__ + +#include "PartitionIR.h" + +#include "luci/Partition.h" + +#include <luci/IR/Module.h> + +namespace luci +{ + +/** + * @brief This will produce a PGroups from Module and PartitionTable. + * @note Each PGroup will hold one CircleNode and partition key value as group. + * Supports only single Graph in the Module for now. + */ +std::unique_ptr<luci::PGroups> produce_pgroups(const luci::Module *source, + const luci::PartitionTable &partition); + +} // namespace luci + +#endif // __LUCI_PARTITON_PGROUPS_H__ diff --git a/compiler/luci/partition/src/PartitionPGroups.test.cpp b/compiler/luci/partition/src/PartitionPGroups.test.cpp new file mode 100644 index 000000000..960f3cde9 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPGroups.test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 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 "PartitionPGroups.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->dtype(loco::DataType::S32); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class SqrtGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + SqrtGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + SqrtGraphlet::init(g(), shape); + + _sqrt->x(input()); + + output()->from(_sqrt); + } +}; + +} // namespace + +TEST(PartitionPGroupsTest, simple_produce) +{ + luci::Module module; + + SqrtGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + luci::PartitionTable pt; + pt.default_group = "A"; + + auto pgs = produce_pgroups(&module, pt); + + ASSERT_EQ(1, pgs->pgroups.size()); +} diff --git a/compiler/luci/partition/src/PartitionPModules.cpp b/compiler/luci/partition/src/PartitionPModules.cpp new file mode 100644 index 000000000..36f4d47a4 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModules.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021 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 "PartitionPModules.h" +#include "ConnectNode.h" + +#include "luci/Service/CircleNodeClone.h" +#include "luci/Log.h" + +#include <loco.h> + +namespace +{ + +void add_graph_input(loco::Graph *graph, luci::CircleInput *input_node) +{ + assert(graph != nullptr); + assert(input_node != nullptr); + + auto graph_input = graph->inputs()->create(); + graph_input->name(input_node->name()); + + // Set GraphInputOutputIndex for graph + input_node->index(graph_input->index()); + + // Data type + graph_input->dtype(input_node->dtype()); + + // Shape of GraphInput + auto input_shape = std::make_unique<loco::TensorShape>(); + input_shape->rank(input_node->rank()); + for (uint32_t r = 0; r < input_node->rank(); ++r) + { + if (input_node->dim(r).known()) + input_shape->dim(r).set(input_node->dim(r).value()); + } + graph_input->shape(std::move(input_shape)); +} + +void add_graph_output(loco::Graph *graph, luci::CircleOutput *output_node) +{ + assert(graph != nullptr); + assert(output_node != nullptr); + + auto graph_output = graph->outputs()->create(); + graph_output->name(output_node->name()); + + // Set GraphInputOutputIndex for graph + output_node->index(graph_output->index()); + + // Data type + graph_output->dtype(output_node->dtype()); + + // Shape of GraphOutput + auto output_shape = std::make_unique<loco::TensorShape>(); + output_shape->rank(output_node->rank()); + for (uint32_t r = 0; r < output_node->rank(); ++r) + { + if (output_node->dim(r).known()) + output_shape->dim(r).set(output_node->dim(r).value()); + } + graph_output->shape(std::move(output_shape)); +} + +/** + * @brief Build loco::graph from pgroup into graph + */ +void build_graph(loco::Graph *graph, const luci::PGroup *pgroup) +{ + LOGGER(l); + + luci::CloneContext clonectx; + + // add input node(s) + for (auto *input : pgroup->inputs) + { + auto *input_clone = graph->nodes()->create<luci::CircleInput>(); + luci::copy_common_attributes(input, input_clone); + + add_graph_input(graph, input_clone); + clonectx.emplace(input, input_clone); + + INFO(l) << "MAP: " + << " input(" << input << ") -> " << input_clone << "(" << input_clone->name() << ")"; + } + + // add CircleConst for inputs + for (auto &pnode : pgroup->pnodes) + { + auto node = pnode->node; + uint32_t arity = node->arity(); + for (uint32_t a = 0; a < arity; ++a) + { + auto in_a_const = dynamic_cast<luci::CircleConst *>(node->arg(a)); + if (in_a_const != nullptr) + { + auto it = clonectx.find(in_a_const); + if (it == clonectx.end()) + { + auto *clone = clone_node(in_a_const, graph); + clonectx.emplace(in_a_const, clone); + + INFO(l) << "MAP: " + << " const(" << in_a_const << ") -> " << clone << "(" << clone->name() << ")"; + } + } + } + } + + // add nodes + for (auto &pnode : pgroup->pnodes) + { + auto *clone = clone_node(pnode->node, graph); + clonectx.emplace(pnode->node, clone); + + INFO(l) << "MAP: " + << " node(" << pnode->node << ") -> " << clone << "(" << clone->name() << ")"; + } + // connect nodes + for (auto &pnode : pgroup->pnodes) + { + clone_connect(pnode->node, clonectx); + } + + // add output node(s) + for (auto *output : pgroup->outputs) + { + auto *output_clone = graph->nodes()->create<luci::CircleOutput>(); + luci::copy_common_attributes(output, output_clone); + // note: we don't add output_clone to clonectx. + // logically, output is not used as an input to any other nodes. + + auto it = clonectx.find(output); + assert(it != clonectx.end()); + output_clone->from(it->second); + + add_graph_output(graph, output_clone); + + INFO(l) << "MAP: " + << "output(" << output << ") -> " << output_clone << "(" << output_clone->name() << ")" + << ": from " << it->second << "(" << it->second->name() << ")"; + } +} + +std::string make_name(const luci::PGroup *pgroup) +{ + auto &first_pnode = *pgroup->pnodes.begin(); + auto *first_node = first_pnode->node; + std::string name = first_node->graph()->name(); + name = name + "_" + pgroup->group; + return name; +} + +} // namespace + +namespace luci +{ + +/** + * @brief This will produce list of luci::Module as PartedModules from pgroups + */ +luci::PartedModules produce_pmodules(const luci::PGroups *pgroups) +{ + LOGGER(l); + + luci::PartedModules pms; + + for (auto &pgroup : pgroups->pgroups) + { + luci::PartedModule pm; + pm.module = std::make_unique<luci::Module>(); + pm.group = pgroup->group; + + auto graph = loco::make_graph(); + + auto graph_name = make_name(pgroup.get()); + graph->name(graph_name); + + INFO(l) << "--- Partition Graph build----------------------"; + INFO(l) << "--- name: " << graph_name; + build_graph(graph.get(), pgroup.get()); + + pm.module->add(std::move(graph)); + pms.pmodules.emplace_back(std::move(pm)); + } + + return pms; +} + +} // namespace luci diff --git a/compiler/luci/partition/src/PartitionPModules.h b/compiler/luci/partition/src/PartitionPModules.h new file mode 100644 index 000000000..628ada56c --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModules.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITON_PMODULES_H__ +#define __LUCI_PARTITON_PMODULES_H__ + +#include "PartitionIR.h" + +#include "luci/Partition.h" + +namespace luci +{ + +luci::PartedModules produce_pmodules(const luci::PGroups *pgroups); + +} // namespace luci + +#endif // __LUCI_PARTITON_PMODULES_H__ diff --git a/compiler/luci/partition/src/PartitionPModules.test.cpp b/compiler/luci/partition/src/PartitionPModules.test.cpp new file mode 100644 index 000000000..99c39e839 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModules.test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 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 "PartitionPModules.h" +#include "PartitionPGroups.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->dtype(loco::DataType::S32); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class SqrtGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + SqrtGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + SqrtGraphlet::init(g(), shape); + + _sqrt->x(input()); + + output()->from(_sqrt); + } +}; + +} // namespace + +TEST(PartitionPModulesTest, simple_convert) +{ + luci::Module module; + + SqrtGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + luci::PartitionTable pt; + pt.default_group = "A"; + + auto pgs = produce_pgroups(&module, pt); + auto pms = produce_pmodules(pgs.get()); + + ASSERT_EQ(1, pms.pmodules.size()); +} diff --git a/compiler/luci/partition/src/PartitionPModulesDump.cpp b/compiler/luci/partition/src/PartitionPModulesDump.cpp new file mode 100644 index 000000000..ee50bc6fb --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModulesDump.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 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 "PartitionPModulesDump.h" + +#include "luci/LogHelper.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PartedModule *pmodule) +{ + os << "--- PartedModule: " << pmodule->group << std::endl; + os << luci::fmt(pmodule->module->graph()); +} + +void dump(std::ostream &os, const PartedModules *pmodules) +{ + for (auto &pmodule : pmodules->pmodules) + { + dump(os, &pmodule); + } + os << std::endl; +} + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PartedModules *pmodules) +{ + luci::dump(os, pmodules); + return os; +} diff --git a/compiler/luci/partition/src/PartitionPModulesDump.h b/compiler/luci/partition/src/PartitionPModulesDump.h new file mode 100644 index 000000000..e77b235f4 --- /dev/null +++ b/compiler/luci/partition/src/PartitionPModulesDump.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 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 __LUCI_PARTITION_PMODULES_DUMP_H__ +#define __LUCI_PARTITION_PMODULES_DUMP_H__ + +#include "luci/Partition.h" + +#include <iostream> + +namespace luci +{ + +void dump(std::ostream &os, const PartedModule *pmodule); +void dump(std::ostream &os, const PartedModules *pmodules); + +} // namespace luci + +std::ostream &operator<<(std::ostream &os, const luci::PartedModules *pmodules); + +#endif // __LUCI_PARTITION_PMODULES_DUMP_H__ diff --git a/compiler/luci/pass/CMakeLists.txt b/compiler/luci/pass/CMakeLists.txt index 2c5fb3407..2977fbed7 100644 --- a/compiler/luci/pass/CMakeLists.txt +++ b/compiler/luci/pass/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries(luci_pass PRIVATE luci_lang) target_link_libraries(luci_pass PRIVATE luci_log) target_link_libraries(luci_pass PRIVATE luci_service) target_link_libraries(luci_pass PRIVATE luci_logex) +target_link_libraries(luci_pass PRIVATE luci_profile) target_link_libraries(luci_pass PRIVATE nncc_common) target_link_libraries(luci_pass PRIVATE oops) install(TARGETS luci_pass DESTINATION lib) @@ -26,4 +27,5 @@ GTest_AddTest(luci_pass_test ${TESTS}) target_include_directories(luci_pass_test PRIVATE src) target_link_libraries(luci_pass_test luci_pass) target_link_libraries(luci_pass_test luci_lang) +target_link_libraries(luci_pass_test luci_testhelper) #target_link_libraries(luci_pass_test oops) diff --git a/compiler/luci/pass/include/luci/CircleOptimizer.h b/compiler/luci/pass/include/luci/CircleOptimizer.h index 906760e0a..1f5e1c8b9 100644 --- a/compiler/luci/pass/include/luci/CircleOptimizer.h +++ b/compiler/luci/pass/include/luci/CircleOptimizer.h @@ -35,6 +35,8 @@ public: enum Algorithm { FuseAddWithTConv, + FuseBatchNormWithConv, + FuseBatchNormWithDwConv, FuseBatchNormWithTConv, FuseBCQ, FuseInstanceNorm, @@ -44,7 +46,11 @@ public: QuantizeDequantizeWeights, QuantizeWithMinMax, Requantize, + FoldAddV2, + FoldCast, FoldDequantize, + FoldSparseToDense, + ForwardReshapeToUnaryOp, SparsifyTensorPass, FusePreActivationBatchNorm, MakeBatchNormGammaPositive, @@ -53,6 +59,15 @@ public: RemoveRedundantTranspose, ReplaceMulAddWithDepthwiseConv, SubstitutePackToReshape, + SubstituteSqueezeToReshape, + ConvertNCHWToNHWC, + RemoveUnnecessarySlice, + RemoveUnnecessaryStridedSlice, + RemoveUnnecessarySplit, + RemoveUnnecessaryReshape, + TransformMinMaxToRelu6Pass, + SubstituteTransposeToReshape, + RemoveRedundantReshape, }; enum AlgorithmParameters @@ -68,6 +83,10 @@ public: Sparsify_format, Sparsify_block_size, Sparsify_block_map, + + // convert NCHW to NHWC + NCHW_to_NHWC_preserve_input_shape, + NCHW_to_NHWC_preserve_output_shape, }; virtual ~Options() = default; diff --git a/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h b/compiler/luci/pass/include/luci/Pass/CircleShapeInferencePass.h index 9d964bdd6..21d6d09d6 100644 --- a/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h +++ b/compiler/luci/pass/include/luci/Pass/CircleShapeInferencePass.h @@ -1,4 +1,3 @@ - /* * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved * @@ -15,8 +14,8 @@ * limitations under the License. */ -#ifndef __LUCI_TYPE_INFERENCE_PASS_H__ -#define __LUCI_TYPE_INFERENCE_PASS_H__ +#ifndef __LUCI_CIRCLE_SHAPE_INFERENCE_PASS_H__ +#define __LUCI_CIRCLE_SHAPE_INFERENCE_PASS_H__ #include <loco.h> @@ -26,12 +25,12 @@ namespace luci { /** - * @brief Pass to infer type of nodes + * @brief Pass to infer shape of circle nodes */ -class TypeInferencePass : public luci::Pass +class CircleShapeInferencePass : public luci::Pass { public: - virtual const char *name(void) const { return "luci::TypeInferencePass"; } + virtual const char *name(void) const { return "luci::CircleShapeInferencePass"; } public: bool run(luci::Module *m); @@ -40,4 +39,4 @@ public: } // namespace luci -#endif //__LUCI_TYPE_INFERENCE_PASS_H__ +#endif //__LUCI_CIRCLE_SHAPE_INFERENCE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/ConvertNCHWToNHWCPass.h b/compiler/luci/pass/include/luci/Pass/ConvertNCHWToNHWCPass.h new file mode 100644 index 000000000..ba2392596 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/ConvertNCHWToNHWCPass.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020 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 __LUCI_CONVERT_NCHW_TO_NHWC_PASS_H__ +#define __LUCI_CONVERT_NCHW_TO_NHWC_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to convert NCHW Ops to NHWC + * + * @details Find operators that use NCHW layout and make them use NHWC. + * Strictly speaking, it is impossible to distinguish whether + * an operator is using NCHW or NHWC without programmers' annotations. + * But we guess the data layout of each operator as much as possible + * based on the assumptions described in the comments. + * Note that this Pass does not change the execution result even + * for the false-positive cases. + */ +struct ConvertNCHWToNHWCPass final : public logo::Pass +{ +public: + ConvertNCHWToNHWCPass(bool preserve_input, bool preserve_output) + : _preserve_input(preserve_input), _preserve_output(preserve_output) + { + // Do nothing + } + + ConvertNCHWToNHWCPass() = delete; + + virtual ~ConvertNCHWToNHWCPass() = default; + + const char *name(void) const final { return "luci::ConvertNCHWToNHWCPass"; } + + bool run(loco::Graph *g) final; + +private: + bool _preserve_input = false; + bool _preserve_output = false; +}; + +} // namespace luci + +#endif // __LUCI_CONVERT_NCHW_TO_NHWC_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FoldAddV2Pass.h b/compiler/luci/pass/include/luci/Pass/FoldAddV2Pass.h new file mode 100644 index 000000000..cd260b916 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FoldAddV2Pass.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 __LUCI_FOLD_ADD_V2_PASS_H__ +#define __LUCI_FOLD_ADD_V2_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to fold AddV2 to a constant tensor + * + */ +struct FoldAddV2Pass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FoldAddV2Pass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FOLD_ADD_V2_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FoldCastPass.h b/compiler/luci/pass/include/luci/Pass/FoldCastPass.h new file mode 100644 index 000000000..5d7ce4ad3 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FoldCastPass.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 __LUCI_FOLD_CAST_PASS_H__ +#define __LUCI_FOLD_CAST_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to fold Cast to a constant tensor + * + */ +struct FoldCastPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FoldCastPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FOLD_CAST_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FoldSparseToDensePass.h b/compiler/luci/pass/include/luci/Pass/FoldSparseToDensePass.h new file mode 100644 index 000000000..00d2447a5 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FoldSparseToDensePass.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 __LUCI_FOLD_SPARSE_TO_DENSE_PASS_H__ +#define __LUCI_FOLD_SPARSE_TO_DENSE_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to fold SparseToDense to a constant tensor + * + */ +struct FoldSparseToDensePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FoldSparseToDensePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FOLD_SPARSE_TO_DENSE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/ForwardReshapeToUnaryOpPass.h b/compiler/luci/pass/include/luci/Pass/ForwardReshapeToUnaryOpPass.h new file mode 100644 index 000000000..4c308e531 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/ForwardReshapeToUnaryOpPass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_FORWARD_RESHAPE_TO_UNARYOP_PASS_H__ +#define __LUCI_FORWARD_RESHAPE_TO_UNARYOP_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to Forward send Reshape after UnaryOp. + */ +struct ForwardReshapeToUnaryOpPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::ForwardReshapeToUnaryOpPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FORWARD_RESHAPE_TO_UNARYOP_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithConvPass.h b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithConvPass.h new file mode 100644 index 000000000..1ed85447b --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithConvPass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_FUSE_BATCH_NORM_WITH_CONV_PASS_H__ +#define __LUCI_FUSE_BATCH_NORM_WITH_CONV_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to fuse Batch Normalization into CircleConv + */ +struct FuseBatchNormWithConvPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FuseBatchNormWithConvPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FUSE_BATCH_NORM_WITH_CONV_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithDwConvPass.h b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithDwConvPass.h new file mode 100644 index 000000000..32885c6b2 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithDwConvPass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_FUSE_BATCH_NORM_WITH_DWCONV_PASS_H__ +#define __LUCI_FUSE_BATCH_NORM_WITH_DWCONV_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to fuse Batch Normalization into CircleDepthWiseConv2D + */ +struct FuseBatchNormWithDwConvPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::FuseBatchNormWithDwConvPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_FUSE_BATCH_NORM_WITH_DWCONV_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConv.h b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConvPass.h index d3e930a36..d3e930a36 100644 --- a/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConv.h +++ b/compiler/luci/pass/include/luci/Pass/FuseBatchNormWithTConvPass.h diff --git a/compiler/luci/pass/include/luci/Pass/MigrateLegacyShapeDtypePass.h b/compiler/luci/pass/include/luci/Pass/MigrateLegacyShapeDtypePass.h deleted file mode 100644 index c0ebc4e5d..000000000 --- a/compiler/luci/pass/include/luci/Pass/MigrateLegacyShapeDtypePass.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020 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 __LUCI_MIGRATE_LEGACY_SHAPE_DTYPE_PASS_H__ -#define __LUCI_MIGRATE_LEGACY_SHAPE_DTYPE_PASS_H__ - -#include <loco.h> - -#include <luci/ModulePass.h> - -namespace luci -{ - -/** - * @brief Pass to copy shape/dtype of loco to circle node - * - * CAUTION : This pass will be removed after refactoring is finished - */ -class MigrateLegacyShapeDtypePass : public luci::Pass -{ -public: - virtual const char *name(void) const { return "luci::MigrateLegacyShapeDtypePass"; } - -public: - bool run(luci::Module *m); - bool run(loco::Graph *graph); -}; - -} // namespace luci - -#endif //__LUCI_MIGRATE_LEGACY_SHAPE_DTYPE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h b/compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h index 713b88f9d..78e7323f9 100644 --- a/compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h +++ b/compiler/luci/pass/include/luci/Pass/QuantizeDequantizeWeightsPass.h @@ -34,7 +34,7 @@ class QuantizeDequantizeWeightsPass : public logo::Pass public: QuantizeDequantizeWeightsPass(loco::DataType input_dtype, loco::DataType output_dtype, QuantizationGranularity granularity) - : _input_dtype{input_dtype}, _output_dtype{output_dtype}, _granularity{granularity} + : _input_dtype{input_dtype}, _output_dtype{output_dtype}, _granularity{granularity} { // DO NOTHING } diff --git a/compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h b/compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h index bb0d0ff40..9520910d5 100644 --- a/compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h +++ b/compiler/luci/pass/include/luci/Pass/QuantizeWithMinMaxPass.h @@ -34,7 +34,7 @@ class QuantizeWithMinMaxPass : public logo::Pass public: QuantizeWithMinMaxPass(loco::DataType input_dtype, loco::DataType output_dtype, QuantizationGranularity granularity) - : _input_dtype{input_dtype}, _output_dtype{output_dtype}, _granularity{granularity} + : _input_dtype{input_dtype}, _output_dtype{output_dtype}, _granularity{granularity} { // DO NOTHING } diff --git a/compiler/luci/pass/include/luci/Pass/RemoveRedundantReshapePass.h b/compiler/luci/pass/include/luci/Pass/RemoveRedundantReshapePass.h new file mode 100644 index 000000000..458ffc094 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/RemoveRedundantReshapePass.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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 __LUCI_REMOVE_REDUNDANT_RESHAPE_PASS_H__ +#define __LUCI_REMOVE_REDUNDANT_RESHAPE_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to remove redundant Reshape node into 1 Reshape node. + * @details This class will update consecutive two Reshape node into single Reshape node. + * As Reshape operation just change shape, not buffer, former reshape could be unnecessary. + */ +struct RemoveRedundantReshapePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::RemoveRedundantReshapePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REMOVE_REDUNDANT_RESHAPE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryReshapePass.h b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryReshapePass.h new file mode 100644 index 000000000..8fca35e5b --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryReshapePass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_REMOVE_UNNECESSARY_RESHAPE_PASS_H__ +#define __LUCI_REMOVE_UNNECESSARY_RESHAPE_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to Remove Unnecessary(input shape and output shape same) Reshape node. + */ +struct RemoveUnnecessaryReshapePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::RemoveUnnecessaryReshapePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REMOVE_UNNECESSARY_RESHAPE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RemoveUnnecessarySlicePass.h b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessarySlicePass.h new file mode 100644 index 000000000..a3b0f2f8c --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessarySlicePass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_REMOVE_NO_EFFECT_SLICE_PASS_H__ +#define __LUCI_REMOVE_NO_EFFECT_SLICE_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to Remove Unnecessary(input and output are same) Slice node. + */ +struct RemoveUnnecessarySlicePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::RemoveUnnecessarySlicePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REMOVE_NO_EFFECT_SLICE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RemoveUnnecessarySplitPass.h b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessarySplitPass.h new file mode 100644 index 000000000..0d9330fe7 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessarySplitPass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 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 __LUCI_REMOVE_UNNECESSARY_SPLIT_PASS_H__ +#define __LUCI_REMOVE_UNNECESSARY_SPLIT_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Remove unnecessary Split OP + */ +struct RemoveUnnecessarySplitPass final : public logo::Pass +{ + const char *name(void) const final { return "luci::RemoveUnnecessarySplitPass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REMOVE_UNNECESSARY_SPLIT_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryStridedSlicePass.h b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryStridedSlicePass.h new file mode 100644 index 000000000..0f6a61d43 --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/RemoveUnnecessaryStridedSlicePass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_REMOVE_UNNECESSARY_STRIDED_SLICE_PASS_H__ +#define __LUCI_REMOVE_UNNECESSARY_STRIDED_SLICE_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to Remove Unnecessary(input and output are same) StridedSlice node. + */ +struct RemoveUnnecessaryStridedSlicePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::RemoveUnnecessaryStridedSlicePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_REMOVE_UNNECESSARY_STRIDED_SLICE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/RequantizePass.h b/compiler/luci/pass/include/luci/Pass/RequantizePass.h index 2442b24ea..c6c424f1b 100644 --- a/compiler/luci/pass/include/luci/Pass/RequantizePass.h +++ b/compiler/luci/pass/include/luci/Pass/RequantizePass.h @@ -33,7 +33,7 @@ class RequantizePass : public logo::Pass { public: RequantizePass(loco::DataType input_dtype, loco::DataType output_dtype) - : _input_dtype{input_dtype}, _output_dtype{output_dtype} + : _input_dtype{input_dtype}, _output_dtype{output_dtype} { // DO NOTHING } diff --git a/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h b/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h index 41f43bf88..0ce142c55 100644 --- a/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h +++ b/compiler/luci/pass/include/luci/Pass/SparsifyTensorPass.h @@ -35,8 +35,8 @@ public: SparsifyTensorPass(const std::string &tensor_name, const std::vector<int32_t> &traversal_order, const std::vector<DimensionType> &format, const std::vector<int32_t> &block_size, const std::vector<int32_t> &block_map) - : _tensor_name{tensor_name}, _traversal_order{traversal_order}, _format{format}, - _block_size{block_size}, _block_map{block_map} + : _tensor_name{tensor_name}, _traversal_order{traversal_order}, _format{format}, + _block_size{block_size}, _block_map{block_map} { // DO NOTHING } diff --git a/compiler/luci/pass/include/luci/Pass/SubstituteSqueezeToReshapePass.h b/compiler/luci/pass/include/luci/Pass/SubstituteSqueezeToReshapePass.h new file mode 100644 index 000000000..d8df6ac3f --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/SubstituteSqueezeToReshapePass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_SUBSTITUTE_SQUEEZE_TO_RESHAPE_PASS_H__ +#define __LUCI_SUBSTITUTE_SQUEEZE_TO_RESHAPE_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to Substitute Squeeze to Reshape node for certain conditions. + */ +struct SubstituteSqueezeToReshapePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::SubstituteSqueezeToReshapePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_SUBSTITUTE_SQUEEZE_TO_RESHAPE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/SubstituteTransposeToReshapePass.h b/compiler/luci/pass/include/luci/Pass/SubstituteTransposeToReshapePass.h new file mode 100644 index 000000000..ee708585a --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/SubstituteTransposeToReshapePass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_SUBSTITUTE_TRANSPOSE_TO_RESHAPE_PASS_H__ +#define __LUCI_SUBSTITUTE_TRANSPOSE_TO_RESHAPE_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to Substitute Transpose with certain input shape condition to single reshape node. + */ +struct SubstituteTransposeToReshapePass final : public logo::Pass +{ + const char *name(void) const final { return "luci::SubstituteTransposeToReshapePass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_SUBSTITUTE_TRANSPOSE_TO_RESHAPE_PASS_H__ diff --git a/compiler/luci/pass/include/luci/Pass/TransformMinMaxToRelu6Pass.h b/compiler/luci/pass/include/luci/Pass/TransformMinMaxToRelu6Pass.h new file mode 100644 index 000000000..9ea39ee4e --- /dev/null +++ b/compiler/luci/pass/include/luci/Pass/TransformMinMaxToRelu6Pass.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 __LUCI_TRANSFORM_MIN_MAX_TO_RELU6_PASS_H__ +#define __LUCI_TRANSFORM_MIN_MAX_TO_RELU6_PASS_H__ + +#include <logo/Pass.h> + +namespace luci +{ + +/** + * @brief Class to transform Maximum(Minimum(input, 6), 0) to Relu6 + */ +struct TransformMinMaxToRelu6Pass final : public logo::Pass +{ + const char *name(void) const final { return "luci::TransformMinMaxToRelu6Pass"; } + + bool run(loco::Graph *g) final; +}; + +} // namespace luci + +#endif // __LUCI_TRANSFORM_MIN_MAX_TO_RELU6_PASS_H__ diff --git a/compiler/luci/pass/src/BatchNormPatternFinder.cpp b/compiler/luci/pass/src/BatchNormPatternFinder.cpp new file mode 100644 index 000000000..c1a06bfda --- /dev/null +++ b/compiler/luci/pass/src/BatchNormPatternFinder.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021 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 "BatchNormPatternFinder.h" + +#include <luci/IR/CircleNodes.h> + +namespace luci +{ + +bool is_batchnorm_add(const luci::CircleAdd *add, luci::CircleMul *&mul, luci::CircleConst *&beta) +{ + auto x = loco::must_cast<luci::CircleNode *>(add->x()); + auto y = loco::must_cast<luci::CircleNode *>(add->y()); + + luci::CircleMul *pred = nullptr; + luci::CircleConst *constant = nullptr; + + if (x->opcode() == luci::CircleOpcode::CIRCLECONST && y->opcode() == luci::CircleOpcode::MUL) + { + pred = loco::must_cast<luci::CircleMul *>(y); + constant = loco::must_cast<luci::CircleConst *>(x); + } + else if (x->opcode() == luci::CircleOpcode::MUL && y->opcode() == luci::CircleOpcode::CIRCLECONST) + { + pred = loco::must_cast<luci::CircleMul *>(x); + constant = loco::must_cast<luci::CircleConst *>(y); + } + else + { + return false; + } + + if (constant->rank() != 1) + return false; + + auto channel_dim = constant->dim(0); + // Assumption: Layout is channel-last + if (!(channel_dim == add->dim(add->rank() - 1))) + return false; + + mul = pred; + beta = constant; + return true; +} + +bool is_batchnorm_add(const luci::CircleAdd *add) +{ + // for dummy mul and beta + luci::CircleMul *mul = nullptr; + luci::CircleConst *beta = nullptr; + + return is_batchnorm_add(add, mul, beta); +} + +bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleNode *&pred_node, + luci::CircleConst *&gamma) +{ + auto x = dynamic_cast<luci::CircleConst *>(mul->x()); + auto y = dynamic_cast<luci::CircleConst *>(mul->y()); + + luci::CircleNode *pred = nullptr; + luci::CircleConst *constant = nullptr; + + if (x != nullptr && y == nullptr) + { + pred = loco::must_cast<luci::CircleNode *>(mul->y()); + constant = x; + } + else if (x == nullptr && y != nullptr) + { + pred = loco::must_cast<luci::CircleNode *>(mul->x()); + constant = y; + } + else + { + return false; + } + + if (constant->rank() != 1) + return false; + + auto channel_dim = constant->dim(0); + // Assumption: Layout is channel-last + if (!(channel_dim == mul->dim(mul->rank() - 1))) + return false; + + pred_node = pred; + gamma = constant; + return true; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/BatchNormPatternFinder.h b/compiler/luci/pass/src/BatchNormPatternFinder.h new file mode 100644 index 000000000..58cdbb464 --- /dev/null +++ b/compiler/luci/pass/src/BatchNormPatternFinder.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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 __LUCI_PASS_BATCH_NORM_PATTERN_FINDER_H__ +#define __LUCI_PASS_BATCH_NORM_PATTERN_FINDER_H__ + +#include <luci/IR/CircleNodes.h> + +namespace luci +{ + +/** + * @brief Find Mul-Add pattern and return Mul and beta as BatchNorm + */ +bool is_batchnorm_add(const luci::CircleAdd *add, luci::CircleMul *&mul, luci::CircleConst *&beta); + +/** + * @brief Find Mul-Add pattern + */ +bool is_batchnorm_add(const luci::CircleAdd *add); + +/** + * @brief Find Const-Mul pattern and return Node and gamma as BatchNorm + */ +bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleNode *&pred_node, + luci::CircleConst *&gamma); + +} // namespace luci + +#endif // __LUCI_PASS_BATCH_NORM_PATTERN_FINDER_H__ diff --git a/compiler/luci/pass/src/BatchNormPatternFinder.test.cpp b/compiler/luci/pass/src/BatchNormPatternFinder.test.cpp new file mode 100644 index 000000000..08e7fac1c --- /dev/null +++ b/compiler/luci/pass/src/BatchNormPatternFinder.test.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2021 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 "BatchNormPatternFinder.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace luci +{ +namespace test +{ + +/** + * @brief Graphlet with Add and Const as beta from BatchNorm + */ +class AddBetaGraphlet +{ +public: + AddBetaGraphlet() = default; + + void init(loco::Graph *g, const ShapeU32 shape, luci::FusedActFunc actf) + { + _add = g->nodes()->create<luci::CircleAdd>(); + _add_beta = g->nodes()->create<luci::CircleConst>(); + + _add->dtype(loco::DataType::FLOAT32); + _add_beta->dtype(loco::DataType::FLOAT32); + + _add->fusedActivationFunction(actf); + + assert(shape.size() > 0); + auto last_it = std::prev(shape.end(), 1); + auto channel_size = *last_it; + + _add->shape(shape); + _add_beta->shape({channel_size}); + _add_beta->size<loco::DataType::FLOAT32>(channel_size); + for (uint32_t i = 0; i < channel_size; i++) + _add_beta->at<loco::DataType::FLOAT32>(i) = i; + + _add->name("add"); + _add_beta->name("add_beta"); + } + +public: + luci::CircleAdd *add() { return _add; } + +protected: + luci::CircleAdd *_add = nullptr; + luci::CircleConst *_add_beta = nullptr; +}; + +/** + * @brief Graphlet with Mul and Const as gamma from BatchNorm + */ +class MulGammaGraphlet +{ +public: + MulGammaGraphlet() = default; + + void init(loco::Graph *g, const ShapeU32 shape, luci::FusedActFunc actf) + { + _mul = g->nodes()->create<luci::CircleMul>(); + _mul_gamma = g->nodes()->create<luci::CircleConst>(); + + _mul->dtype(loco::DataType::FLOAT32); + _mul_gamma->dtype(loco::DataType::FLOAT32); + + _mul->fusedActivationFunction(actf); + + assert(shape.size() > 0); + auto last_it = std::prev(shape.end(), 1); + auto channel_size = *last_it; + + _mul->shape(shape); + _mul_gamma->shape({channel_size}); + _mul_gamma->size<loco::DataType::FLOAT32>(channel_size); + for (uint32_t i = 0; i < channel_size; i++) + _mul_gamma->at<loco::DataType::FLOAT32>(i) = i; + + _mul->name("mul"); + _mul_gamma->name("mul_gamma"); + } + +public: + luci::CircleMul *mul(void) { return _mul; } + +protected: + luci::CircleMul *_mul = nullptr; + luci::CircleConst *_mul_gamma = nullptr; +}; + +/** + * @brief Graph of Mul-Add pattern from BatchNorm + */ +class MulAddGraph : public TestIOGraph, public AddBetaGraphlet, public MulGammaGraphlet +{ +public: + MulAddGraph() = default; + + void init(const ShapeU32 shape_in, const ShapeU32 shape_out) + { + TestIOGraph::init(shape_in, shape_out); + MulGammaGraphlet::init(g(), shape_in, luci::FusedActFunc::NONE); + AddBetaGraphlet::init(g(), shape_out, luci::FusedActFunc::RELU); + + // connect network + _mul->x(input()); + _mul->y(_mul_gamma); + _add->x(_mul); + _add->y(_add_beta); + output()->from(_add); + } +}; + +/** + * @brief Graph of Add with Const + */ +class AddGraph : public TestIOGraph, public AddBetaGraphlet +{ +public: + AddGraph() = default; + + void init(const ShapeU32 shape_in, const ShapeU32 shape_out) + { + TestIOGraph::init(shape_in, shape_out); + AddBetaGraphlet::init(g(), shape_in, luci::FusedActFunc::RELU); + + // connect network + _add->x(input()); + _add->y(_add_beta); + output()->from(_add); + } +}; + +} // namespace test +} // namespace luci + +class BatchNormPatternFinderMulAddTest : public ::testing::Test +{ +public: + BatchNormPatternFinderMulAddTest() = default; + +protected: + luci::test::MulAddGraph _mag; +}; + +class BatchNormPatternFinderAddTest : public ::testing::Test +{ +public: + BatchNormPatternFinderAddTest() = default; + +protected: + luci::test::AddGraph _ag; +}; + +TEST_F(BatchNormPatternFinderMulAddTest, is_batchnorm_add) +{ + _mag.init({1, 16, 16, 4}, {1, 16, 16, 4}); + + luci::CircleMul *mul = nullptr; + luci::CircleConst *beta = nullptr; + + auto res = luci::is_batchnorm_add(_mag.add(), mul, beta); + ASSERT_TRUE(res); + ASSERT_NE(nullptr, mul); + ASSERT_NE(nullptr, beta); +} + +TEST_F(BatchNormPatternFinderMulAddTest, is_batchnorm_add2) +{ + _mag.init({1, 16, 16, 4}, {1, 16, 16, 4}); + + auto res = luci::is_batchnorm_add(_mag.add()); + ASSERT_TRUE(res); +} + +TEST_F(BatchNormPatternFinderAddTest, is_batchnorm_add_NEG) +{ + _ag.init({1, 16, 16, 4}, {1, 16, 16, 4}); + + luci::CircleMul *mul = nullptr; + luci::CircleConst *beta = nullptr; + + auto res = luci::is_batchnorm_add(_ag.add(), mul, beta); + ASSERT_FALSE(res); +} + +TEST_F(BatchNormPatternFinderMulAddTest, is_batchnorm_mul) +{ + _mag.init({1, 16, 16, 4}, {1, 16, 16, 4}); + + luci::CircleNode *pred = nullptr; + luci::CircleConst *gamma = nullptr; + + auto res = luci::is_batchnorm_mul(_mag.mul(), pred, gamma); + ASSERT_TRUE(res); + ASSERT_NE(nullptr, pred); + ASSERT_NE(nullptr, gamma); +} diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp index cc9fe481c..bddad34fa 100644 --- a/compiler/luci/pass/src/CircleOptimizer.cpp +++ b/compiler/luci/pass/src/CircleOptimizer.cpp @@ -16,16 +16,28 @@ #include "luci/CircleOptimizer.h" +#include "luci/Pass/ConvertNCHWToNHWCPass.h" +#include "luci/Pass/FoldAddV2Pass.h" +#include "luci/Pass/FoldCastPass.h" #include "luci/Pass/FoldDequantizePass.h" +#include "luci/Pass/FoldSparseToDensePass.h" +#include "luci/Pass/ForwardReshapeToUnaryOpPass.h" #include "luci/Pass/FuseActivationFunctionPass.h" #include "luci/Pass/FuseAddWithTConvPass.h" -#include "luci/Pass/FuseBatchNormWithTConv.h" +#include "luci/Pass/FuseBatchNormWithConvPass.h" +#include "luci/Pass/FuseBatchNormWithDwConvPass.h" +#include "luci/Pass/FuseBatchNormWithTConvPass.h" #include "luci/Pass/FuseBCQPass.h" #include "luci/Pass/FuseInstanceNormPass.h" #include "luci/Pass/FusePreActivationBatchNormPass.h" #include "luci/Pass/MakeBatchNormGammaPositivePass.h" #include "luci/Pass/PropagateQuantParamPass.h" +#include "luci/Pass/RemoveRedundantReshapePass.h" #include "luci/Pass/RemoveRedundantTransposePass.h" +#include "luci/Pass/RemoveUnnecessaryReshapePass.h" +#include "luci/Pass/RemoveUnnecessarySlicePass.h" +#include "luci/Pass/RemoveUnnecessaryStridedSlicePass.h" +#include "luci/Pass/RemoveUnnecessarySplitPass.h" #include "luci/Pass/ReplaceMulAddWithDepthwiseConvPass.h" #include "luci/Pass/ResolveCustomOpAddPass.h" #include "luci/Pass/ResolveCustomOpBatchMatMulPass.h" @@ -36,21 +48,22 @@ #include "luci/Pass/SparsifyTensorPass.h" #include "luci/Pass/ShuffleWeightTo16x1Float32Pass.h" #include "luci/Pass/SubstitutePackToReshapePass.h" +#include "luci/Pass/SubstituteSqueezeToReshapePass.h" +#include "luci/Pass/SubstituteTransposeToReshapePass.h" +#include "luci/Pass/TransformMinMaxToRelu6Pass.h" // TODO add more passes -#include "luci/Pass/ShapeInferencePass.h" -#include "luci/Pass/ShapeSignatureInferencePass.h" -#include "luci/Pass/TypeInferencePass.h" - -// Following passes will be removed after refactoring is finished -#include "luci/Pass/MigrateLegacyShapeDtypePass.h" +#include "luci/Pass/CircleShapeInferencePass.h" +#include "luci/Pass/CircleTypeInferencePass.h" // logo passes #include <logo/RemoveDeadNodeWithQueryPass.h> #include "ModulePhase.h" #include "ProgressReporter.h" -#include "CircleOptimizerUtils.h" +#include "helpers/Strings.h" + +#include "QuantizedModelVerifier.h" #include <luci/IR/CircleNodes.h> #include <logo/Phase.h> @@ -61,20 +74,6 @@ namespace { -std::vector<int> parseIntFromCommadelimitedStr(std::string str) -{ - std::vector<int> ret; - std::istringstream is(str); - for (uint32_t i; is >> i;) - { - assert(i != ','); - ret.push_back(i); - if (is.peek() == ',') - is.ignore(); - } - return ret; -} - using namespace luci; class OptimizeOptionsImpl final : public luci::CircleOptimizer::Options @@ -138,13 +137,9 @@ void CircleOptimizer::optimize(luci::Module *m) const { luci::Phase phase; - // Following passes will be deprecated after refactoring is finished. - phase.emplace_back(std::make_unique<luci::MigrateLegacyShapeDtypePass>()); - // Following passes are needed everytime when other passes create new node or modify some nodes. - phase.emplace_back(std::make_unique<luci::ShapeInferencePass>()); - phase.emplace_back(std::make_unique<luci::ShapeSignatureInferencePass>()); - phase.emplace_back(std::make_unique<luci::TypeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleTypeInferencePass>()); if (_options->query(Options::Algorithm::FuseBCQ)) { @@ -164,13 +159,9 @@ void CircleOptimizer::optimize(loco::Graph *g) const /* TRANSFORM DECLARATION BEGIN */ phase.emplace_back(std::make_unique<logo::RemoveDeadNodeWithQueryPass>()); - // Following passes will be deprecated after refactoring is finished. - phase.emplace_back(std::make_unique<luci::MigrateLegacyShapeDtypePass>()); - // Following passes are needed everytime when other passes create new node or modify some nodes. - phase.emplace_back(std::make_unique<luci::TypeInferencePass>()); - phase.emplace_back(std::make_unique<luci::ShapeInferencePass>()); - phase.emplace_back(std::make_unique<luci::ShapeSignatureInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleTypeInferencePass>()); if (_options->query(Options::Algorithm::ResolveCustomOpAdd)) { @@ -188,6 +179,14 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique<FuseInstanceNormPass>()); } + if (_options->query(Options::Algorithm::FuseBatchNormWithConv)) + { + phase.emplace_back(std::make_unique<FuseBatchNormWithConvPass>()); + } + if (_options->query(Options::Algorithm::FuseBatchNormWithDwConv)) + { + phase.emplace_back(std::make_unique<FuseBatchNormWithDwConvPass>()); + } if (_options->query(Options::Algorithm::FuseBatchNormWithTConv)) { phase.emplace_back(std::make_unique<FuseBatchNormWithTConvPass>()); @@ -200,10 +199,26 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique<FuseActivationFunctionPass>()); } + if (_options->query(Options::Algorithm::FoldAddV2)) + { + phase.emplace_back(std::make_unique<luci::FoldAddV2Pass>()); + } + if (_options->query(Options::Algorithm::FoldCast)) + { + phase.emplace_back(std::make_unique<luci::FoldCastPass>()); + } if (_options->query(Options::Algorithm::FoldDequantize)) { phase.emplace_back(std::make_unique<luci::FoldDequantizePass>()); } + if (_options->query(Options::Algorithm::FoldSparseToDense)) + { + phase.emplace_back(std::make_unique<luci::FoldSparseToDensePass>()); + } + if (_options->query(Options::Algorithm::ForwardReshapeToUnaryOp)) + { + phase.emplace_back(std::make_unique<luci::ForwardReshapeToUnaryOpPass>()); + } if (_options->query(Options::Algorithm::FusePreActivationBatchNorm)) { phase.emplace_back(std::make_unique<luci::FusePreActivationBatchNormPass>()); @@ -216,6 +231,26 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique<luci::ShuffleWeightTo16x1Float32Pass>()); } + if (_options->query(Options::Algorithm::RemoveUnnecessaryReshape)) + { + phase.emplace_back(std::make_unique<luci::RemoveUnnecessaryReshapePass>()); + } + if (_options->query(Options::Algorithm::RemoveUnnecessarySlice)) + { + phase.emplace_back(std::make_unique<luci::RemoveUnnecessarySlicePass>()); + } + if (_options->query(Options::Algorithm::RemoveUnnecessaryStridedSlice)) + { + phase.emplace_back(std::make_unique<luci::RemoveUnnecessaryStridedSlicePass>()); + } + if (_options->query(Options::Algorithm::RemoveUnnecessarySplit)) + { + phase.emplace_back(std::make_unique<luci::RemoveUnnecessarySplitPass>()); + } + if (_options->query(Options::Algorithm::RemoveRedundantReshape)) + { + phase.emplace_back(std::make_unique<luci::RemoveRedundantReshapePass>()); + } if (_options->query(Options::Algorithm::RemoveRedundantTranspose)) { phase.emplace_back(std::make_unique<luci::RemoveRedundantTransposePass>()); @@ -228,6 +263,28 @@ void CircleOptimizer::optimize(loco::Graph *g) const { phase.emplace_back(std::make_unique<luci::SubstitutePackToReshapePass>()); } + if (_options->query(Options::Algorithm::SubstituteSqueezeToReshape)) + { + phase.emplace_back(std::make_unique<luci::SubstituteSqueezeToReshapePass>()); + } + if (_options->query(Options::Algorithm::SubstituteTransposeToReshape)) + { + phase.emplace_back(std::make_unique<luci::SubstituteTransposeToReshapePass>()); + } + if (_options->query(Options::Algorithm::TransformMinMaxToRelu6Pass)) + { + phase.emplace_back(std::make_unique<luci::TransformMinMaxToRelu6Pass>()); + } + if (_options->query(Options::Algorithm::ConvertNCHWToNHWC)) + { + bool preserve_input = + _options->param(Options::AlgorithmParameters::NCHW_to_NHWC_preserve_input_shape) == "true"; + bool preserve_output = + _options->param(Options::AlgorithmParameters::NCHW_to_NHWC_preserve_output_shape) == "true"; + + phase.emplace_back( + std::make_unique<luci::ConvertNCHWToNHWCPass>(preserve_input, preserve_output)); + } /* TRANSFORM DECLARATION END */ @@ -275,7 +332,7 @@ void CircleOptimizer::quantize(loco::Graph *g) const } luci::QuantizeDequantizeWeightsPass fake_quantizer( - str_to_dtype(input_dtype), str_to_dtype(output_dtype), str_to_granularity(granularity)); + str_to_dtype(input_dtype), str_to_dtype(output_dtype), str_to_granularity(granularity)); fake_quantizer.run(g); } @@ -315,14 +372,19 @@ void CircleOptimizer::quantize(loco::Graph *g) const phase.emplace_back(std::make_unique<luci::PropagateQuantParamPass>()); - phase.emplace_back(std::make_unique<luci::ShapeInferencePass>()); - phase.emplace_back(std::make_unique<luci::TypeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleTypeInferencePass>()); phase.emplace_back(std::make_unique<logo::RemoveDeadNodeWithQueryPass>()); ProgressReporter prog(g, logo::PhaseStrategy::Saturate); logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g}; phase_runner.attach(&prog); phase_runner.run(phase); + + // Verify the type/granularity of the quantized model + luci::QuantizedModelVerifier verifier(str_to_dtype(output_dtype), + str_to_granularity(granularity)); + verifier.verify(g); } // Requantize @@ -349,8 +411,8 @@ void CircleOptimizer::quantize(loco::Graph *g) const logo::Phase phase; // Do Shape/Type inference - phase.emplace_back(std::make_unique<luci::ShapeInferencePass>()); - phase.emplace_back(std::make_unique<luci::TypeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleTypeInferencePass>()); ProgressReporter prog(g, logo::PhaseStrategy::Saturate); logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g}; @@ -364,13 +426,13 @@ void CircleOptimizer::sparsify(loco::Graph *g) const { std::string tensor_name = _options->param(Options::AlgorithmParameters::Sparsify_tensor_name); std::string str_tarversal_order = - _options->param(Options::AlgorithmParameters::Sparsify_traversal_order); + _options->param(Options::AlgorithmParameters::Sparsify_traversal_order); std::string str_format = _options->param(Options::AlgorithmParameters::Sparsify_format); std::string str_block_size = _options->param(Options::AlgorithmParameters::Sparsify_block_size); std::string str_block_map = _options->param(Options::AlgorithmParameters::Sparsify_block_map); // traversal order - std::vector<int32_t> traversal_order = parseIntFromCommadelimitedStr(str_tarversal_order); + std::vector<int32_t> traversal_order = csv_to_vector<int32_t>(str_tarversal_order); // format std::vector<DimensionType> format; std::istringstream is(str_format); @@ -385,9 +447,9 @@ void CircleOptimizer::sparsify(loco::Graph *g) const is.ignore(); } // block size - std::vector<int32_t> block_size = parseIntFromCommadelimitedStr(str_block_size); + std::vector<int32_t> block_size = csv_to_vector<int32_t>(str_block_size); // block map - std::vector<int32_t> block_map = parseIntFromCommadelimitedStr(str_block_map); + std::vector<int32_t> block_map = csv_to_vector<int32_t>(str_block_map); luci::SparsifyTensorPass sparsifier{tensor_name, traversal_order, format, block_size, block_map}; diff --git a/compiler/luci/pass/src/CircleOptimizer.test.cpp b/compiler/luci/pass/src/CircleOptimizer.test.cpp new file mode 100644 index 000000000..ca6dc77f3 --- /dev/null +++ b/compiler/luci/pass/src/CircleOptimizer.test.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2021 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/CircleOptimizer.h" + +#include <gtest/gtest.h> + +using namespace luci; +using Algorithms = luci::CircleOptimizer::Options::Algorithm; +using AlgorithmParameters = luci::CircleOptimizer::Options::AlgorithmParameters; + +TEST(CircleOptimizerTest, optimize_algorithms) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + // NOTE these are added to cover the test + // TODO add more if needed + options->enable(Algorithms::FoldAddV2); + options->enable(Algorithms::FoldCast); + options->enable(Algorithms::FoldDequantize); + options->enable(Algorithms::FoldSparseToDense); + options->enable(Algorithms::FusePreActivationBatchNorm); + options->enable(Algorithms::MakeBatchNormGammaPositive); + options->enable(Algorithms::ShuffleWeightTo16x1Float32); + options->enable(Algorithms::RemoveUnnecessaryReshape); + options->enable(Algorithms::RemoveUnnecessarySlice); + options->enable(Algorithms::RemoveUnnecessarySplit); + options->enable(Algorithms::ReplaceMulAddWithDepthwiseConv); + options->enable(Algorithms::SubstituteTransposeToReshape); + options->enable(Algorithms::ConvertNCHWToNHWC); + + o.optimize(&g); + + SUCCEED(); +} + +TEST(CircleOptimizerTest, sparsify_simple) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::SparsifyTensorPass); + options->param(AlgorithmParameters::Sparsify_tensor_name, "dummy"); + options->param(AlgorithmParameters::Sparsify_traversal_order, "dummy"); + options->param(AlgorithmParameters::Sparsify_format, "ds"); + options->param(AlgorithmParameters::Sparsify_block_size, "1,1"); + options->param(AlgorithmParameters::Sparsify_block_map, "1,1"); + + o.sparsify(&g); + + SUCCEED(); +} + +TEST(CircleOptimizerTest, quantize_quantdequant_simple) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeDequantizeWeights); + options->param(AlgorithmParameters::Quantize_input_dtype, "float32"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + options->param(AlgorithmParameters::Quantize_granularity, "layer"); + + o.quantize(&g); + + SUCCEED(); +} + +TEST(CircleOptimizerTest, quantize_quantdequant_input_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeDequantizeWeights); + options->param(AlgorithmParameters::Quantize_input_dtype, "invalid"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + options->param(AlgorithmParameters::Quantize_granularity, "layer"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} + +TEST(CircleOptimizerTest, quantize_quantdequant_output_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeDequantizeWeights); + options->param(AlgorithmParameters::Quantize_input_dtype, "float32"); + options->param(AlgorithmParameters::Quantize_output_dtype, "invalid"); + options->param(AlgorithmParameters::Quantize_granularity, "layer"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} + +TEST(CircleOptimizerTest, quantize_quantdequant_gran_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeDequantizeWeights); + options->param(AlgorithmParameters::Quantize_input_dtype, "float32"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + options->param(AlgorithmParameters::Quantize_granularity, "invalid"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} + +TEST(CircleOptimizerTest, quantize_minmax_simple) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeWithMinMax); + options->param(AlgorithmParameters::Quantize_input_dtype, "float32"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + options->param(AlgorithmParameters::Quantize_granularity, "layer"); + + o.quantize(&g); + + SUCCEED(); +} + +TEST(CircleOptimizerTest, quantize_minmax_input_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeWithMinMax); + options->param(AlgorithmParameters::Quantize_input_dtype, "invalid"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + options->param(AlgorithmParameters::Quantize_granularity, "layer"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} + +TEST(CircleOptimizerTest, quantize_minmax_output_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeWithMinMax); + options->param(AlgorithmParameters::Quantize_input_dtype, "float32"); + options->param(AlgorithmParameters::Quantize_output_dtype, "invalid"); + options->param(AlgorithmParameters::Quantize_granularity, "layer"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} + +TEST(CircleOptimizerTest, quantize_minmax_gran_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::QuantizeWithMinMax); + options->param(AlgorithmParameters::Quantize_input_dtype, "float32"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + options->param(AlgorithmParameters::Quantize_granularity, "invalid"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} + +TEST(CircleOptimizerTest, quantize_requant_simple) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::Requantize); + options->param(AlgorithmParameters::Quantize_input_dtype, "int8"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + + o.quantize(&g); + + SUCCEED(); +} + +TEST(CircleOptimizerTest, quantize_requant_input_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::Requantize); + options->param(AlgorithmParameters::Quantize_input_dtype, "invalid"); + options->param(AlgorithmParameters::Quantize_output_dtype, "uint8"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} + +TEST(CircleOptimizerTest, quantize_requant_output_NEG) +{ + loco::Graph g; + luci::CircleOptimizer o; + + auto options = o.options(); + + options->enable(Algorithms::Requantize); + options->param(AlgorithmParameters::Quantize_input_dtype, "int8"); + options->param(AlgorithmParameters::Quantize_output_dtype, "invalid"); + + EXPECT_THROW(o.quantize(&g), std::runtime_error); +} diff --git a/compiler/luci/pass/src/CircleOptimizerUtils.cpp b/compiler/luci/pass/src/CircleOptimizerUtils.cpp index ffc372392..127573db4 100644 --- a/compiler/luci/pass/src/CircleOptimizerUtils.cpp +++ b/compiler/luci/pass/src/CircleOptimizerUtils.cpp @@ -16,74 +16,18 @@ #include "CircleOptimizerUtils.h" -namespace luci -{ - -bool in_array(const std::string &str, const std::vector<std::string> &array) -{ - return std::find(array.begin(), array.end(), str) != array.end(); -} +#include <luci/IR/CircleNode.h> -std::string to_string(const std::vector<std::string> &strings) -{ - assert(!strings.empty()); - - std::string res; - for (unsigned int i = 0; i < strings.size() - 1; i++) - res += strings[i] + ", "; - - res += strings[strings.size() - 1]; - return res; -} - -std::string to_lower_case(std::string s) -{ - std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); - return s; -} - -loco::DataType str_to_dtype(const std::string &str) +namespace luci { - if (to_lower_case(str).compare("uint8") == 0) - return loco::DataType::U8; - if (to_lower_case(str).compare("uint16") == 0) - return loco::DataType::U16; - if (to_lower_case(str).compare("uint32") == 0) - return loco::DataType::U32; - if (to_lower_case(str).compare("uint64") == 0) - return loco::DataType::U64; - - if (to_lower_case(str).compare("int8") == 0) - return loco::DataType::S8; - if (to_lower_case(str).compare("int16") == 0) - return loco::DataType::S16; - if (to_lower_case(str).compare("int32") == 0) - return loco::DataType::S32; - if (to_lower_case(str).compare("int64") == 0) - return loco::DataType::S64; - - if (to_lower_case(str).compare("float16") == 0) - return loco::DataType::FLOAT16; - if (to_lower_case(str).compare("float32") == 0) - return loco::DataType::FLOAT32; - if (to_lower_case(str).compare("float64") == 0) - return loco::DataType::FLOAT64; - if (to_lower_case(str).compare("bool") == 0) - return loco::DataType::BOOL; - - return loco::DataType::Unknown; -} - -QuantizationGranularity str_to_granularity(const std::string &str) +bool has_dynamic_shape(const loco::Node *node) { - if (to_lower_case(str).compare("layer") == 0) - return QuantizationGranularity::LayerWise; - - if (to_lower_case(str).compare("channel") == 0) - return QuantizationGranularity::ChannelWise; - - throw std::runtime_error("Quantization granularity must be either 'layer' or 'channel'"); + const auto circle_node = loco::must_cast<const luci::CircleNode *>(node); + for (uint32_t i = 0; i < circle_node->rank(); ++i) + if (!circle_node->dim(i).known()) + return true; + return false; } } // namespace luci diff --git a/compiler/luci/pass/src/CircleOptimizerUtils.h b/compiler/luci/pass/src/CircleOptimizerUtils.h index 7e577a05f..e04942bfa 100644 --- a/compiler/luci/pass/src/CircleOptimizerUtils.h +++ b/compiler/luci/pass/src/CircleOptimizerUtils.h @@ -17,25 +17,12 @@ #ifndef __LUCI_CIRCLE_OPTIMIZER_UTILS_H__ #define __LUCI_CIRCLE_OPTIMIZER_UTILS_H__ -#include "luci/Pass/QuantizeDequantizeWeightsPass.h" -#include "luci/Pass/QuantizeWithMinMaxPass.h" - #include <loco.h> -#include <algorithm> - namespace luci { -bool in_array(const std::string &, const std::vector<std::string> &); - -std::string to_string(const std::vector<std::string> &); - -std::string to_lower_case(std::string); - -loco::DataType str_to_dtype(const std::string &); - -QuantizationGranularity str_to_granularity(const std::string &); +bool has_dynamic_shape(const loco::Node *node); } // namespace luci diff --git a/compiler/luci/pass/src/CircleShapeInferencePass.cpp b/compiler/luci/pass/src/CircleShapeInferencePass.cpp new file mode 100644 index 000000000..ddab22421 --- /dev/null +++ b/compiler/luci/pass/src/CircleShapeInferencePass.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 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 "helpers/InferenceCandidates.h" + +#include "luci/Pass/CircleShapeInferencePass.h" + +#include <luci/Service/CircleShapeInference.h> + +#include <loco.h> + +namespace +{ + +bool is_same_shape(luci::CircleNode *node, loco::TensorShape shape) +{ + if (node->shape_status() != luci::ShapeStatus::VALID) + return false; + + if (node->rank() != shape.rank()) + return false; + + for (uint32_t i = 0; i < node->rank(); ++i) + { + if (node->dim(i).known() != shape.dim(i).known()) + return false; + + if (node->dim(i).value() != shape.dim(i).value()) + return false; + } + + return true; +} + +} // namespace + +namespace luci +{ + +bool CircleShapeInferencePass::run(luci::Module *m) +{ + bool changed = false; + + for (size_t g = 0; g < m->size(); ++g) + { + if (run(m->graph(g))) + changed = true; + } + + return changed; +} + +bool CircleShapeInferencePass::run(loco::Graph *g) +{ + luci::sinf::Rule shape_infer_rule; + bool changed = false; + + for (auto node : inference_candidates(g)) + { + loco::TensorShape shape; + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + + if (shape_infer_rule.infer(circle_node, shape) && !is_same_shape(circle_node, shape)) + { + circle_node->rank(shape.rank()); + for (uint32_t i = 0; i < shape.rank(); ++i) + circle_node->dim(i) = shape.dim(i); + + circle_node->shape_status(luci::ShapeStatus::VALID); + + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/CircleShapeInferencePass.test.cpp b/compiler/luci/pass/src/CircleShapeInferencePass.test.cpp new file mode 100644 index 000000000..cb3f1fe5f --- /dev/null +++ b/compiler/luci/pass/src/CircleShapeInferencePass.test.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2021 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/Pass/CircleShapeInferencePass.h" + +#include <loco.h> + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +TEST(CircleShapeInferencePassTest, name) +{ + luci::CircleShapeInferencePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +/** + * This test is to check whether shape inference is done by topological order. + * + * When perm() of "transpose1" is changed from "old_perm" to "new_perm" + * by some of luci/Pass like below diagram, shape_status of "transpose1" is + * still VALID even the shape should be changed. + * If "transpose2" is visited first before shape of "transpose1" is updated, + * "transpose2" can reference the shape of "relu" which is not updated yet. + * Then shape of "transpose2" becomes 3x5x5x1 and it causes an error at "conv2d". + * + * <Initial graph> + * 4x1x1x3 + * [old_perm] ----------+ [filter] ----------+ + * (0,2,1,3) | | + * | [bias] ----------+ + * | | + * input ------> [transpose1] ------> [relu] ------> [conv2d] ------> output + * 1x5x5x3 1x5x5x3 1x5x5x3 1x5x5x4 + * + * + * <Right after transformation> + * 4x1x1x3 + * [new_perm] ----------+-----------------------------------+ [filter] ------+ + * (3,2,1,0) | | | + * | | [bias] ------+ + * | | | + * input ------> [transpose1] ------> [relu] ------> [transpose2] ------> [conv2d] ------> output + * 1x5x5x3 1x5x5x3 1x5x5x3 ? 1x5x5x4 + * + * + * <Expected result> + * 4x1x1x3 + * [new_perm] ----------+-----------------------------------+ [filter] ------+ + * (3,2,1,0) | | | + * | | [bias] ------+ + * | | | + * input ------> [transpose1] ------> [relu] ------> [transpose2] ------> [conv2d] ------> output + * 1x5x5x3 3x5x5x1 3x5x5x1 1x5x5x3 1x5x5x4 + * + */ +TEST(CircleShapeInferencePassTest, original_node_change) +{ + luci::CircleShapeInferencePass pass; + auto g = loco::make_graph(); + + // Have to be packed into lambda to check throw + auto shape_inference_run = [&]() { + while (pass.run(g.get()) == true) + ; + }; + + // Create nodes to make relu traversed first + auto input = g->nodes()->create<luci::CircleInput>(); + auto relu = g->nodes()->create<luci::CircleRelu>(); + auto old_perm = g->nodes()->create<luci::CircleConst>(); + auto transpose1 = g->nodes()->create<luci::CircleTranspose>(); + auto filter = g->nodes()->create<luci::CircleConst>(); + auto bias = g->nodes()->create<luci::CircleConst>(); + auto conv2d = g->nodes()->create<luci::CircleConv2D>(); + auto output = g->nodes()->create<luci::CircleOutput>(); + auto new_perm = g->nodes()->create<luci::CircleConst>(); + auto transpose2 = g->nodes()->create<luci::CircleTranspose>(); + + // Build up initial graph + auto graph_input = g->inputs()->create(); + graph_input->shape({1, 5, 5, 3}); + + input->index(graph_input->index()); + input->shape({1, 5, 5, 3}); + input->shape_status(luci::ShapeStatus::VALID); + + old_perm->dtype(loco::DataType::S32); + old_perm->size<loco::DataType::S32>(4); + old_perm->shape({4}); + old_perm->at<loco::DataType::S32>(0) = 0; + old_perm->at<loco::DataType::S32>(1) = 2; + old_perm->at<loco::DataType::S32>(2) = 1; + old_perm->at<loco::DataType::S32>(3) = 3; + old_perm->shape_status(luci::ShapeStatus::VALID); + + transpose1->a(input); + transpose1->perm(old_perm); + + relu->features(transpose1); + + filter->dtype(loco::DataType::FLOAT32); + filter->size<loco::DataType::FLOAT32>(4 * 1 * 1 * 3); + filter->shape({4, 1, 1, 3}); + filter->shape_status(luci::ShapeStatus::VALID); + + bias->dtype(loco::DataType::FLOAT32); + bias->size<loco::DataType::FLOAT32>(4); + bias->shape({4}); + bias->shape_status(luci::ShapeStatus::VALID); + + conv2d->input(relu); + conv2d->filter(filter); + conv2d->bias(bias); + conv2d->padding(luci::Padding::VALID); + conv2d->stride()->h(1); + conv2d->stride()->w(1); + conv2d->dilation()->h(1); + conv2d->dilation()->w(1); + + output->from(conv2d); + auto graph_output = g->outputs()->create(); + output->index(graph_output->index()); + graph_output->shape({1, 5, 5, 4}); + + ASSERT_NO_THROW(shape_inference_run()); + + // Transform graph + new_perm->dtype(loco::DataType::S32); + new_perm->size<loco::DataType::S32>(4); + new_perm->shape({4}); + new_perm->at<loco::DataType::S32>(0) = 3; + new_perm->at<loco::DataType::S32>(1) = 2; + new_perm->at<loco::DataType::S32>(2) = 1; + new_perm->at<loco::DataType::S32>(3) = 0; + new_perm->shape_status(luci::ShapeStatus::VALID); + + transpose1->perm(new_perm); + + transpose2->a(relu); + transpose2->perm(new_perm); + + conv2d->input(transpose2); + + ASSERT_NO_THROW(shape_inference_run()); + + // Check result of shape inference is correct + ASSERT_EQ(3, transpose1->dim(0).value()); + ASSERT_EQ(5, transpose1->dim(1).value()); + ASSERT_EQ(5, transpose1->dim(2).value()); + ASSERT_EQ(1, transpose1->dim(3).value()); + + ASSERT_EQ(3, relu->dim(0).value()); + ASSERT_EQ(5, relu->dim(1).value()); + ASSERT_EQ(5, relu->dim(2).value()); + ASSERT_EQ(1, relu->dim(3).value()); + + ASSERT_EQ(1, transpose2->dim(0).value()); + ASSERT_EQ(5, transpose2->dim(1).value()); + ASSERT_EQ(5, transpose2->dim(2).value()); + ASSERT_EQ(3, transpose2->dim(3).value()); + + ASSERT_EQ(1, conv2d->dim(0).value()); + ASSERT_EQ(5, conv2d->dim(1).value()); + ASSERT_EQ(5, conv2d->dim(2).value()); + ASSERT_EQ(4, conv2d->dim(3).value()); + + SUCCEED(); +} + +/** + * This test is for checking when imported shape is wrong. + * + * Even "concat1" has wrong shape at first, correct shape should be inferred. + * + * <Initial graph> + * + * 1x1x1x1 + * input1 ------+ 8x7x6x5 + * +-----> [concat1] ------+ + * input2 ------+ (axis=3) | 1x1x2x3 + * 1x1x1x2 +------> [concat2] ------> output + * | (axis=2) + * 1x1x1x3 | + * input3 ------------------------------+ + * + * + * <Expected result> + * + * 1x1x1x1 + * input1 ------+ 1x1x1x3 + * +-----> [concat1] ------+ + * input2 ------+ (axis=3) | 1x1x2x3 + * 1x1x1x2 +------> [concat2] ------> output + * | (axis=2) + * 1x1x1x3 | + * input3 ------------------------------+ + */ +TEST(CircleShapeInferencePassTest, wrong_imported_shape) +{ + luci::CircleShapeInferencePass pass; + auto g = loco::make_graph(); + + // Have to be packed into lambda to check throw + auto shape_inference_run = [&]() { + while (pass.run(g.get()) == true) + ; + }; + + // Create nodes to make concat2 traversed first + auto concat2 = g->nodes()->create<luci::CircleConcatenation>(2); + auto concat1 = g->nodes()->create<luci::CircleConcatenation>(2); + auto input1 = g->nodes()->create<luci::CircleInput>(); + auto input2 = g->nodes()->create<luci::CircleInput>(); + auto input3 = g->nodes()->create<luci::CircleInput>(); + + // Build up initial graph + auto graph_input1 = g->inputs()->create(); + auto graph_input2 = g->inputs()->create(); + auto graph_input3 = g->inputs()->create(); + graph_input1->shape({1, 1, 1, 1}); + graph_input2->shape({1, 1, 1, 2}); + graph_input2->shape({1, 1, 1, 3}); + + input1->index(graph_input1->index()); + input1->shape({1, 1, 1, 1}); + input1->shape_status(luci::ShapeStatus::VALID); + + input2->index(graph_input2->index()); + input2->shape({1, 1, 1, 2}); + input2->shape_status(luci::ShapeStatus::VALID); + + input3->index(graph_input3->index()); + input3->shape({1, 1, 1, 3}); + input3->shape_status(luci::ShapeStatus::VALID); + + concat1->values(0, input1); + concat1->values(1, input2); + concat1->axis(3); + concat1->shape({8, 7, 6, 5}); // Intentionally set wrong shape + concat1->shape_status(luci::ShapeStatus::VALID); + + concat2->values(0, concat1); + concat2->values(1, input3); + concat2->axis(2); + + auto output = g->nodes()->create<luci::CircleOutput>(); + output->from(concat2); + auto graph_output = g->outputs()->create(); + output->index(graph_output->index()); + graph_output->shape({1, 1, 2, 3}); + + ASSERT_NO_THROW(shape_inference_run()); + + // Check result of shape inference is correct + ASSERT_EQ(1, concat1->dim(0).value()); + ASSERT_EQ(1, concat1->dim(1).value()); + ASSERT_EQ(1, concat1->dim(2).value()); + ASSERT_EQ(3, concat1->dim(3).value()); + + ASSERT_EQ(1, concat2->dim(0).value()); + ASSERT_EQ(1, concat2->dim(1).value()); + ASSERT_EQ(2, concat2->dim(2).value()); + ASSERT_EQ(3, concat2->dim(3).value()); + + SUCCEED(); +} + +/** + * This test is for checking that virtual operations which is not used for graph output + * but shape should be exported. + * + * Although "split_out2" is not used for graph output, shape should be inferenced. + * + * <Initial graph> + * + * + * 1x6 +----> [split_out1] ----> output + * input ------> [split] -----+ + * (split_dim=1) +----> [split_out2] + * (num_split=2) + * + * + * <Expected result> + * 1x3 1x3 + * 1x6 +----> [split_out1] ----> output + * input ------> [split] -----+ + * (split_dim=1) +----> [split_out2] + * (num_split=2) 1x3 + */ +TEST(CircleShapeInferencePassTest, not_used_virtual_op) +{ + luci::CircleShapeInferencePass pass; + auto g = loco::make_graph(); + + // Have to be packed into lambda to check throw + auto shape_inference_run = [&]() { + while (pass.run(g.get()) == true) + ; + }; + + // Create nodes + auto input = g->nodes()->create<luci::CircleInput>(); + auto split = g->nodes()->create<luci::CircleSplit>(); + auto split_out1 = g->nodes()->create<luci::CircleSplitOut>(); + auto split_out2 = g->nodes()->create<luci::CircleSplitOut>(); + auto split_dim = g->nodes()->create<luci::CircleConst>(); + + // Build up initial graph + auto graph_input1 = g->inputs()->create(); + graph_input1->shape({1, 6}); + + input->index(graph_input1->index()); + input->shape({1, 6}); + input->shape_status(luci::ShapeStatus::VALID); + + split_dim->dtype(loco::DataType::S32); + split_dim->size<loco::DataType::S32>(1); + split_dim->shape({1}); + split_dim->at<loco::DataType::S32>(0) = 1; + split_dim->shape_status(luci::ShapeStatus::VALID); + + split->split_dim(split_dim); + split->input(input); + split->num_split(2); + + split_out1->input(split); + split_out1->index(0); + + split_out2->input(split); + split_out2->index(1); + + auto output = g->nodes()->create<luci::CircleOutput>(); + output->from(split_out1); + auto graph_output = g->outputs()->create(); + output->index(graph_output->index()); + graph_output->shape({1, 3}); + + ASSERT_NO_THROW(shape_inference_run()); + + // Check result of shape inference is correct + ASSERT_EQ(1, split_out1->dim(0).value()); + ASSERT_EQ(3, split_out1->dim(1).value()); + + ASSERT_EQ(1, split_out2->dim(0).value()); + ASSERT_EQ(3, split_out2->dim(1).value()); + + SUCCEED(); +} diff --git a/compiler/luci/pass/src/CircleTypeInferencePass.cpp b/compiler/luci/pass/src/CircleTypeInferencePass.cpp index 67bd253e0..fb3755ffa 100644 --- a/compiler/luci/pass/src/CircleTypeInferencePass.cpp +++ b/compiler/luci/pass/src/CircleTypeInferencePass.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "helpers/InferenceCandidates.h" + #include "luci/Pass/CircleTypeInferencePass.h" #include <luci/Service/CircleTypeInference.h> @@ -41,7 +43,7 @@ bool CircleTypeInferencePass::run(loco::Graph *g) luci::tinf::Rule type_infer_rule; bool changed = false; - for (auto node : loco::postorder_traversal(loco::output_nodes(g))) + for (auto node : inference_candidates(g)) { loco::DataType dtype; auto circle_node = loco::must_cast<luci::CircleNode *>(node); diff --git a/compiler/stdex/src/Queue.test.cpp b/compiler/luci/pass/src/CircleTypeInferencePass.test.cpp index d76cd3ee6..415424a6f 100644 --- a/compiler/stdex/src/Queue.test.cpp +++ b/compiler/luci/pass/src/CircleTypeInferencePass.test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,19 +14,13 @@ * limitations under the License. */ -#include "stdex/Queue.h" +#include "luci/Pass/CircleTypeInferencePass.h" #include <gtest/gtest.h> -TEST(QueueTest, take) +TEST(CircleTypeInferencePassTest, name) { - std::queue<int> q; - - q.emplace(3); - q.emplace(4); - q.emplace(5); - - ASSERT_EQ(stdex::take(q), 3); - ASSERT_EQ(stdex::take(q), 4); - ASSERT_EQ(stdex::take(q), 5); + luci::CircleTypeInferencePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); } diff --git a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp new file mode 100644 index 000000000..c9022f122 --- /dev/null +++ b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.cpp @@ -0,0 +1,698 @@ +/* + * Copyright (c) 2020 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/Pass/ConvertNCHWToNHWCPass.h" +#include "CircleOptimizerUtils.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Profile/CircleNodeOrigin.h> +#include <luci/Log.h> + +namespace +{ + +enum class DataFormat +{ + NCHW, + NHWC +}; + +/** + * @brief Set annotation for DataFormat (NCHW, NHWC) + * + * @note DataFormatAnnotation will live longer than this Pass (until the + * annotated loco::Node is erased). So, do not use large data in the + * annotation to avoid excessive memory usage. + */ +class DataFormatAnnotation final : public loco::NodeAnnotation +{ +public: + DataFormatAnnotation(const DataFormat &format) : _format{format} + { + // DO NOTHING + } + +public: + const DataFormat &format(void) const { return _format; } + +private: + DataFormat _format; +}; + +void set_data_format(loco::Node *node, const DataFormat &format) +{ + node->annot(std::make_unique<DataFormatAnnotation>(format)); +} + +DataFormat get_data_format(loco::Node *node) +{ + assert(node->annot<DataFormatAnnotation>() != nullptr); + return node->annot<DataFormatAnnotation>()->format(); +} + +bool has_data_format(loco::Node *node) { return node->annot<DataFormatAnnotation>() != nullptr; } + +luci::CircleTranspose *create_4d_transpose(luci::CircleNode *node, + const std::vector<int32_t> indices) +{ + assert(indices.size() == 4); + + auto name = node->name(); + assert(name.length() > 0); + + auto perm = node->graph()->nodes()->create<luci::CircleConst>(); + perm->dtype(loco::DataType::S32); + perm->size<loco::DataType::S32>(4); + perm->rank(1); + perm->dim(0) = 4; + for (uint32_t i = 0; i < 4; i++) + perm->at<loco::DataType::S32>(i) = indices[i]; + perm->shape_status(luci::ShapeStatus::VALID); + + auto make_string = [](const std::vector<int32_t> &nums) { + std::string str; + for (auto num : nums) + { + if (str.length() > 0) + str += "."; + str += std::to_string(num); + } + return str; + }; + + auto str_indices = make_string(indices); + + perm->name(name + "/Transpose_" + str_indices + "/perm"); + + auto trans = node->graph()->nodes()->create<luci::CircleTranspose>(); + trans->perm(perm); + trans->name(name + "/Transpose_" + str_indices); + luci::add_origin(trans, luci::get_origin(node)); + + return trans; +} + +int32_t nchw_axis_to_nhwc(int32_t axis) +{ + uint32_t pos_axis = axis >= 0 ? static_cast<uint32_t>(axis) : static_cast<uint32_t>(axis + 4); + static const uint32_t to_nhwc[4] = {0, 3, 1, 2}; + if (pos_axis > 3) + throw std::runtime_error("Concat axis must be in range [-4, 4)"); + return to_nhwc[pos_axis]; +} + +luci::CircleTranspose *create_post_transpose(luci::CircleNode *node) +{ + return create_4d_transpose(node, {0, 3, 1, 2}); +} + +luci::CircleTranspose *create_pre_transpose(luci::CircleNode *node) +{ + return create_4d_transpose(node, {0, 2, 3, 1}); +} + +uint32_t cal_offset(const loco::TensorShape &dimension, const uint32_t *indices) +{ + return indices[0] * dimension.dim(1).value() * dimension.dim(2).value() * + dimension.dim(3).value() + + indices[1] * dimension.dim(2).value() * dimension.dim(3).value() + + indices[2] * dimension.dim(3).value() + indices[3]; +} + +luci::CircleConst *create_NHWC_paddings(luci::CircleConst *paddings) +{ + // paddings shape is (4,2) (it was checked by is_NCHW) + assert(paddings != nullptr); + assert(paddings->rank() == 2); + assert(paddings->dim(0).value() == 4); + assert(paddings->dim(1).value() == 2); + + // paddings for idx 0~3 are 0 (checked by is_NCHW) + assert(paddings->at<loco::DataType::S32>(0) == 0); + assert(paddings->at<loco::DataType::S32>(1) == 0); + assert(paddings->at<loco::DataType::S32>(2) == 0); + assert(paddings->at<loco::DataType::S32>(3) == 0); + + auto name = paddings->name(); + assert(name.length() > 0); + + auto nhwc_paddings = paddings->graph()->nodes()->create<luci::CircleConst>(); + nhwc_paddings->dtype(loco::DataType::S32); + nhwc_paddings->shape({4, 2}); + nhwc_paddings->shape_status(luci::ShapeStatus::VALID); + nhwc_paddings->size<loco::DataType::S32>(4 * 2); + nhwc_paddings->name(name + "_NHWC"); + + for (uint32_t dim = 0; dim < 4; dim++) + { + for (uint32_t i = 0; i < 2; i++) + { + int32_t data = 0; + + if (dim == 1) + { + // get third dimension (H in NCHW) + data = paddings->at<loco::DataType::S32>(2 * 2 + i); + } + else if (dim == 2) + { + // get fourth dimension (W in NCHW) + data = paddings->at<loco::DataType::S32>(3 * 2 + i); + } + + nhwc_paddings->at<loco::DataType::S32>(dim * 2 + i) = data; + } + } + return nhwc_paddings; +} + +luci::CircleConst *create_NHWC_from_NCHW(luci::CircleConst *constant) +{ + LOGGER(l); + assert(constant->rank() == 4); + + // TODO: Support non-float types + if (constant->dtype() != loco::DataType::FLOAT32) + { + INFO(l) << "Non-float type constant: " << constant->name() << std::endl; + return nullptr; + } + + loco::TensorShape nchw_dimension{constant->dim(0), constant->dim(1), constant->dim(2), + constant->dim(3)}; + loco::TensorShape nhwc_dimension{constant->dim(0), constant->dim(2), constant->dim(3), + constant->dim(1)}; + + auto name = constant->name(); + assert(name.length() > 0); + + auto nhwc_const = constant->graph()->nodes()->create<luci::CircleConst>(); + nhwc_const->dtype(constant->dtype()); + nhwc_const->rank(4); + nhwc_const->dim(0).set(constant->dim(0).value()); + nhwc_const->dim(1).set(constant->dim(2).value()); + nhwc_const->dim(2).set(constant->dim(3).value()); + nhwc_const->dim(3).set(constant->dim(1).value()); + nhwc_const->shape_status(luci::ShapeStatus::VALID); + nhwc_const->size<loco::DataType::FLOAT32>(constant->size<loco::DataType::FLOAT32>()); + nhwc_const->name(name + "_NHWC"); + + for (uint32_t n = 0; n < nchw_dimension.dim(0).value(); n++) + { + for (uint32_t c = 0; c < nchw_dimension.dim(1).value(); c++) + { + for (uint32_t h = 0; h < nchw_dimension.dim(2).value(); h++) + { + for (uint32_t w = 0; w < nchw_dimension.dim(3).value(); w++) + { + uint32_t nchw_indices[4] = {n, c, h, w}; + uint32_t nhwc_indices[4] = {n, h, w, c}; + auto data = + constant->at<loco::DataType::FLOAT32>(cal_offset(nchw_dimension, nchw_indices)); + nhwc_const->at<loco::DataType::FLOAT32>(cal_offset(nhwc_dimension, nhwc_indices)) = data; + } + } + } + } + return nhwc_const; +} + +// NOTE Following conditions can be extended later +// +// Find PAD with an NCHW pattern described below +// - Paddings shape : [4, 2] +// - Paddings value : [[0, 0], [0, 0], [h_t, h_b], [w_t, w_b]]] +bool is_NCHW(const luci::CirclePad *node) +{ + const auto paddings = dynamic_cast<luci::CircleConst *>(node->paddings()); + // Non-const paddings is not supported + if (paddings == nullptr) + return false; + + if (paddings->rank() != 2) + return false; + + if (paddings->dim(0).value() != 4 || paddings->dim(1).value() != 2) + return false; + + // Only check the first two dimensions + for (uint32_t dim = 0; dim < 2; dim++) + { + for (uint32_t i = 0; i < 2; i++) + { + auto data = paddings->at<loco::DataType::S32>(dim * 2 + i); + if (data != 0) + return false; + } + } + + return true; +} + +// NOTE Following conditions can be extended later +// +// Find MUL with an NCHW pattern described below +// - Input (non-constant) shape : [N, C, H, W] +// - Input (constant) shape : [1, C, 1, 1] +// - Output shape : [N, C, H, W] +bool is_NCHW_with_const(const luci::CircleMul *node, luci::CircleNode *&pred_node, + luci::CircleConst *&multiplier) +{ + auto x = dynamic_cast<luci::CircleConst *>(node->x()); + auto y = dynamic_cast<luci::CircleConst *>(node->y()); + + if (x != nullptr && y == nullptr) + { + pred_node = loco::must_cast<luci::CircleNode *>(node->y()); + multiplier = x; + } + else if (x == nullptr && y != nullptr) + { + pred_node = loco::must_cast<luci::CircleNode *>(node->x()); + multiplier = y; + } + else + { + // Ignore if MUL does not have a multiplier input. + return false; + } + + if (pred_node->rank() != 4) + return false; + + const auto const_rank = multiplier->rank(); + if (const_rank != 4) + return false; + + for (uint32_t i = 0; i < const_rank; i++) + { + if (i != 1 && multiplier->dim(i).value() != 1) + return false; + } + + const auto const_cdim = multiplier->dim(1); + const auto input_cdim = pred_node->dim(1); + const auto output_cdim = node->dim(1); + + if (const_cdim == input_cdim && input_cdim == output_cdim) + return true; + else + return false; +} + +// We assume ADD with const input is NCHW if, +// Input shape: (N, C, H, W) +// Output shape: (N, C, H, W) +// 1. Const shape is (1, C, 1, 1) +// 2. Input, Output, Const have the same C. +bool is_NCHW_with_const(const luci::CircleAdd *node, luci::CircleNode *&pred_node, + luci::CircleConst *&beta) +{ + auto x = dynamic_cast<luci::CircleConst *>(node->x()); + auto y = dynamic_cast<luci::CircleConst *>(node->y()); + + if (x != nullptr && y == nullptr) + { + pred_node = loco::must_cast<luci::CircleNode *>(node->y()); + beta = x; + } + else if (x == nullptr && y != nullptr) + { + pred_node = loco::must_cast<luci::CircleNode *>(node->x()); + beta = y; + } + else + { + // Ignore if ADD does not have a constant input. + return false; + } + + if (pred_node->rank() != 4) + return false; + + const auto const_rank = beta->rank(); + if (const_rank != 4) + return false; + + // Check the shape is (1, C, 1, 1) + for (uint32_t i = 0; i < const_rank; i++) + { + if (i == 1) + continue; + + if (beta->dim(i).value() != 1) + return false; + } + + const auto const_cdim = beta->dim(1); + const auto input_cdim = pred_node->dim(1); + const auto output_cdim = node->dim(1); + + // Check Input, Output, Const have the same channel size + if (const_cdim == input_cdim && input_cdim == output_cdim) + return true; + else + return false; +} + +template <class T> bool convert_unary_features(T *node) +{ + const auto pred_node = loco::must_cast<luci::CircleNode *>(node->features()); + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + node->features(pre_trans); + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + return true; +} + +class ConvertNCHWToNHWC final : public luci::CircleNodeMutableVisitor<bool> +{ + // Default + bool visit(luci::CircleNode *node) + { + throw std::runtime_error(node->name() + " is an unsupported operator."); + } + + bool visit(luci::CircleInput *node) + { + const auto n = node->dim(0); + const auto c = node->dim(1); + const auto h = node->dim(2); + const auto w = node->dim(3); + + node->dim(1) = h; + node->dim(2) = w; + node->dim(3) = c; + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + // Insert post-tranpose + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + // Update graph input + auto graph_inputs = node->graph()->inputs(); + auto graph_input = graph_inputs->at(node->index()); + graph_input->shape({n, h, w, c}); + + return true; + } + + bool visit(luci::CircleOutput *node) + { + // Insert pre-transpose + auto pre_trans = create_pre_transpose(node); + pre_trans->a(node->from()); + + node->from(pre_trans); + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + // Update graph output + const auto n = node->dim(0).value(); + const auto c = node->dim(1).value(); + const auto h = node->dim(2).value(); + const auto w = node->dim(3).value(); + + auto graph_outputs = node->graph()->outputs(); + auto graph_output = graph_outputs->at(node->index()); + graph_output->shape({n, h, w, c}); + + return true; + } + + bool visit(luci::CircleAdd *node) + { + luci::CircleNode *pred_node = nullptr; + luci::CircleConst *beta = nullptr; + + if (is_NCHW_with_const(node, pred_node, beta)) + { + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + + auto nhwc_const = create_NHWC_from_NCHW(beta); + if (nhwc_const == nullptr) + return false; + + node->x(pre_trans); + node->y(nhwc_const); + } + else if (beta == nullptr) + { + // Both inputs are not constant. + // In this case, we cannot distinguish NCHW from NHWC, + // so just insert Transpose Ops. + auto pre_trans_x = create_pre_transpose(node); + pre_trans_x->a(node->x()); + node->x(pre_trans_x); + + auto pre_trans_y = create_pre_transpose(node); + pre_trans_y->a(node->y()); + node->y(pre_trans_y); + } + else + { + return false; + } + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + return true; + } + + bool visit(luci::CircleConcatenation *node) + { + const auto num_values = node->numValues(); + for (uint32_t i = 0; i < num_values; i++) + { + auto pred_node = loco::must_cast<luci::CircleNode *>(node->values(i)); + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + node->values(i, pre_trans); + } + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + node->axis(nchw_axis_to_nhwc(node->axis())); + + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + return true; + } + + bool visit(luci::CircleLeakyRelu *node) + { + return convert_unary_features<luci::CircleLeakyRelu>(node); + } + + bool visit(luci::CircleMul *node) + { + LOGGER(l); + + luci::CircleNode *pred_node = nullptr; + luci::CircleConst *multiplier = nullptr; + + if (is_NCHW_with_const(node, pred_node, multiplier)) + { + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + node->x(pre_trans); + + auto nhwc_const = create_NHWC_from_NCHW(multiplier); + node->y(nhwc_const); + } + else if (multiplier == nullptr) + { + // TODO : Implement this case. + INFO(l) << "Not yet implemented. Both inputs of MUL are non-const." << std::endl; + return false; + } + else + { + return false; + } + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + return true; + } + + bool visit(luci::CircleNeg *node) + { + const auto pred_node = loco::must_cast<luci::CircleNode *>(node->x()); + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + node->x(pre_trans); + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + return true; + } + + bool visit(luci::CirclePad *node) + { + if (!is_NCHW(node)) + return false; + + const auto pred_node = loco::must_cast<luci::CircleNode *>(node->input()); + auto pre_trans = create_pre_transpose(node); + pre_trans->a(pred_node); + node->input(pre_trans); + + auto nchw_paddings = loco::must_cast<luci::CircleConst *>(node->paddings()); + const auto nhwc_paddings = create_NHWC_paddings(nchw_paddings); + node->paddings(nhwc_paddings); + + // Do shape inference for this node again. + node->shape_status(luci::ShapeStatus::UNDEFINED); + + auto post_trans = create_post_transpose(node); + loco::replace(node).with(post_trans); + + post_trans->a(node); + + return true; + } + + bool visit(luci::CircleRelu *node) { return convert_unary_features<luci::CircleRelu>(node); } + + bool visit(luci::CircleRelu6 *node) { return convert_unary_features<luci::CircleRelu6>(node); } +}; + +} // namespace + +namespace luci +{ + +bool ConvertNCHWToNHWCPass::run(loco::Graph *g) +{ + LOGGER(l); + INFO(l) << "ConvertNCHWToNHWCPass Start" << std::endl; + + // Annotate NCHW operators + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + switch (circle_node->opcode()) + { + // List of supported Ops + case luci::CircleOpcode::CIRCLEINPUT: + if (!_preserve_input && !has_data_format(node)) + { + set_data_format(node, DataFormat::NCHW); + } + break; + case luci::CircleOpcode::CIRCLEOUTPUT: + if (!_preserve_output && !has_data_format(node)) + { + set_data_format(node, DataFormat::NCHW); + } + break; + case luci::CircleOpcode::ADD: + case luci::CircleOpcode::CONCATENATION: + case luci::CircleOpcode::LEAKY_RELU: + case luci::CircleOpcode::MUL: + case luci::CircleOpcode::NEG: + case luci::CircleOpcode::PAD: + case luci::CircleOpcode::RELU: + case luci::CircleOpcode::RELU6: + if (!has_data_format(node)) + { + set_data_format(node, DataFormat::NCHW); + } + break; + default: + break; + } + } + + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (!has_data_format(node)) + { + // Unsupported Op + continue; + } + else if (get_data_format(node) == DataFormat::NHWC) + { + // Already converted to NHWC + continue; + } + else if (has_dynamic_shape(node)) + { + // This pass only works for static-shaped node + INFO(l) << "Skip the node with a dynamic shape." << std::endl; + continue; + } + else + { + ConvertNCHWToNHWC converter; + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + if (circle_node->rank() != 4) + continue; + + if (circle_node->accept(&converter)) + { + set_data_format(node, DataFormat::NHWC); + changed = true; + } + else + { + continue; + } + } + } + + INFO(l) << "ConvertNCHWToNHWCPass End" << std::endl; + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp new file mode 100644 index 000000000..831d5f89a --- /dev/null +++ b/compiler/luci/pass/src/ConvertNCHWToNHWCPass.test.cpp @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2020 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 <logo/Phase.h> + +#include "luci/Pass/ConvertNCHWToNHWCPass.h" +#include "luci/Pass/CircleShapeInferencePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +/** + * Graph with a single Op (example: Add). + * + * BEFORE + * - All Ops including Input/Output are NCHW. + * + * [Input] [beta] + * | / + * [Add] + * | + * [Output] + * + * AFTER + * - All Ops including Input/Output are NHWC. + * + * [Input] + * | + * [Transpose] + * | + * [Transpose] [beta] + * | / + * [Add] + * | + * [Transpose] + * | + * [Transpose] + * | + * [Output] + */ +class SimpleGraph +{ +public: + SimpleGraph() = default; + +public: + void init() + { + input = g.nodes()->create<luci::CircleInput>(); + output = g.nodes()->create<luci::CircleOutput>(); + input->name("input"); + output->name("output"); + + auto graph_input = g.inputs()->create(); + input->index(graph_input->index()); + auto graph_output = g.outputs()->create(); + output->index(graph_output->index()); + + graph_input->dtype(loco::DataType::FLOAT32); + input->dtype(loco::DataType::FLOAT32); + output->dtype(loco::DataType::FLOAT32); + graph_output->dtype(loco::DataType::FLOAT32); + + uint32_t channel_size = 16; + graph_input->shape({1, channel_size, 4, 4}); + input->shape({1, channel_size, 4, 4}); + output->shape({1, channel_size, 4, 4}); + graph_output->shape({1, channel_size, 4, 4}); + + auto graph_body = insertGraphBody(input); + output->from(graph_body); + } + + virtual ~SimpleGraph() = default; + +protected: + virtual loco::Node *insertGraphBody(loco::Node *input) = 0; + +public: + loco::Graph g; + luci::CircleInput *input = nullptr; + luci::CircleOutput *output = nullptr; +}; + +class AddGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + add = g.nodes()->create<luci::CircleAdd>(); + beta = g.nodes()->create<luci::CircleConst>(); + + add->dtype(loco::DataType::FLOAT32); + beta->dtype(loco::DataType::FLOAT32); + + uint32_t channel_size = 16; + add->shape({1, channel_size, 4, 4}); + beta->shape({1, channel_size, 1, 1}); + + beta->size<loco::DataType::FLOAT32>(channel_size); + for (uint32_t i = 0; i < channel_size; i++) + { + beta->at<loco::DataType::FLOAT32>(i) = i; + } + + add->x(input); + add->y(beta); + + add->name("add"); + beta->name("beta"); + + return add; + } + +public: + luci::CircleAdd *add = nullptr; + luci::CircleConst *beta = nullptr; +}; + +class ConcatenationGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + concat = g.nodes()->create<luci::CircleConcatenation>(2); + concat->values(0, input); + concat->axis(1); + + input2 = g.nodes()->create<luci::CircleConst>(); + input2->dtype(loco::DataType::FLOAT32); + input2->shape({1, 16, 4, 4}); + input2->size<loco::DataType::FLOAT32>(16 * 4 * 4); + for (uint32_t i = 0; i < 16 * 4 * 4; i++) + { + input2->at<loco::DataType::FLOAT32>(i) = i; + } + concat->values(1, input2); + + concat->name("concat"); + input2->name("input2"); + + return concat; + } + +public: + luci::CircleConcatenation *concat = nullptr; + luci::CircleConst *input2 = nullptr; +}; + +class LeakyReluGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + leakyrelu = g.nodes()->create<luci::CircleLeakyRelu>(); + leakyrelu->features(input); + leakyrelu->name("leakyrelu"); + + return leakyrelu; + } + +public: + luci::CircleLeakyRelu *leakyrelu = nullptr; +}; + +class MulGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + mul = g.nodes()->create<luci::CircleMul>(); + multiplier = g.nodes()->create<luci::CircleConst>(); + + mul->dtype(loco::DataType::FLOAT32); + multiplier->dtype(loco::DataType::FLOAT32); + + uint32_t channel_size = 16; + mul->shape({1, channel_size, 4, 4}); + multiplier->shape({1, channel_size, 1, 1}); + + multiplier->size<loco::DataType::FLOAT32>(channel_size); + for (uint32_t i = 0; i < channel_size; i++) + { + multiplier->at<loco::DataType::FLOAT32>(i) = i; + } + + mul->x(input); + mul->y(multiplier); + + mul->name("mul"); + multiplier->name("multiplier"); + + return mul; + } + +public: + luci::CircleMul *mul = nullptr; + luci::CircleConst *multiplier = nullptr; +}; + +class NegGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + neg = g.nodes()->create<luci::CircleNeg>(); + neg->x(input); + neg->name("neg"); + + return neg; + } + +public: + luci::CircleNeg *neg = nullptr; +}; + +class PadGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + pad = g.nodes()->create<luci::CirclePad>(); + paddings = g.nodes()->create<luci::CircleConst>(); + + pad->dtype(loco::DataType::FLOAT32); + paddings->dtype(loco::DataType::S32); + + uint32_t channel_size = 16; + pad->shape({1, channel_size, 4, 4}); + paddings->shape({4, 2}); + + // paddings data (NCHW) + // [[0,0], [0,0], [1,1], [2,2]] + paddings->size<loco::DataType::S32>(8); + for (uint32_t dim = 0; dim < 4; dim++) + { + for (uint32_t i = 0; i < 2; i++) + { + int32_t data = 0; + + if (dim == 2) + data = 1; + else if (dim == 3) + data = 2; + + paddings->at<loco::DataType::S32>(dim * 2 + i) = data; + } + } + + pad->input(input); + pad->paddings(paddings); + + pad->name("pad"); + paddings->name("paddings"); + + return pad; + } + +public: + luci::CirclePad *pad = nullptr; + luci::CircleConst *paddings = nullptr; +}; + +class ReluGraph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + relu = g.nodes()->create<luci::CircleRelu>(); + relu->features(input); + relu->name("Relu"); + + return relu; + } + +public: + luci::CircleRelu *relu = nullptr; +}; + +class Relu6Graph final : public SimpleGraph +{ +protected: + loco::Node *insertGraphBody(loco::Node *input) override + { + relu6 = g.nodes()->create<luci::CircleRelu6>(); + relu6->features(input); + relu6->name("relu6"); + + return relu6; + } + +public: + luci::CircleRelu6 *relu6 = nullptr; +}; + +void check_pre_trans(loco::Node *node) +{ + auto pre_trans = dynamic_cast<luci::CircleTranspose *>(node); + EXPECT_NE(nullptr, pre_trans); + auto pre_trans_perm = dynamic_cast<luci::CircleConst *>(pre_trans->perm()); + EXPECT_NE(nullptr, pre_trans_perm); + EXPECT_EQ(1, pre_trans_perm->rank()); + EXPECT_EQ(4, pre_trans_perm->dim(0).value()); + EXPECT_EQ(loco::DataType::S32, pre_trans_perm->dtype()); + EXPECT_EQ(0, pre_trans_perm->at<loco::DataType::S32>(0)); + EXPECT_EQ(2, pre_trans_perm->at<loco::DataType::S32>(1)); + EXPECT_EQ(3, pre_trans_perm->at<loco::DataType::S32>(2)); + EXPECT_EQ(1, pre_trans_perm->at<loco::DataType::S32>(3)); +} + +void check_post_trans(loco::Node *node) +{ + auto post_trans = dynamic_cast<luci::CircleTranspose *>(node); + EXPECT_NE(nullptr, post_trans); + auto post_trans_perm = dynamic_cast<luci::CircleConst *>(post_trans->perm()); + EXPECT_NE(nullptr, post_trans_perm); + EXPECT_EQ(1, post_trans_perm->rank()); + EXPECT_EQ(4, post_trans_perm->dim(0).value()); + EXPECT_EQ(loco::DataType::S32, post_trans_perm->dtype()); + EXPECT_EQ(0, post_trans_perm->at<loco::DataType::S32>(0)); + EXPECT_EQ(3, post_trans_perm->at<loco::DataType::S32>(1)); + EXPECT_EQ(1, post_trans_perm->at<loco::DataType::S32>(2)); + EXPECT_EQ(2, post_trans_perm->at<loco::DataType::S32>(3)); +} + +void run_phase(loco::Graph *g, bool preserve_input, bool preserve_output) +{ + logo::Phase phase; + + // Default passes. + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + + // Pass to test + phase.emplace_back( + std::make_unique<luci::ConvertNCHWToNHWCPass>(preserve_input, preserve_output)); + + logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g}; + phase_runner.run(phase); +} + +} // namespace + +TEST(ConvertNCHWToNHWCPassTest, name) +{ + luci::ConvertNCHWToNHWCPass pass(false, false); + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(ConvertNCHWToNHWC, Add) +{ + AddGraph g; + g.init(); + + run_phase(&g.g, false, false); + + auto input_succs = loco::succs(g.input); + EXPECT_EQ(1, input_succs.size()); + check_post_trans(*input_succs.begin()); + + check_pre_trans(g.add->x()); + + auto add_succs = loco::succs(g.add); + EXPECT_EQ(1, add_succs.size()); + check_post_trans(*add_succs.begin()); + + uint32_t channel_size = 16; + auto new_beta = dynamic_cast<luci::CircleConst *>(g.add->y()); + EXPECT_NE(nullptr, new_beta); + EXPECT_EQ(4, new_beta->rank()); + EXPECT_EQ(1, new_beta->dim(0).value()); + EXPECT_EQ(1, new_beta->dim(1).value()); + EXPECT_EQ(1, new_beta->dim(2).value()); + EXPECT_EQ(channel_size, new_beta->dim(3).value()); + + check_pre_trans(g.output->from()); +} + +TEST(ConvertNCHWToNHWC, Concatenation) +{ + ConcatenationGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.concat->values(0)); + check_pre_trans(g.concat->values(1)); + + auto concat_succs = loco::succs(g.concat); + EXPECT_EQ(1, concat_succs.size()); + check_post_trans(*concat_succs.begin()); + + // Check concat shape, axis + EXPECT_EQ(1, g.concat->dim(0).value()); + EXPECT_EQ(4, g.concat->dim(1).value()); + EXPECT_EQ(4, g.concat->dim(2).value()); + EXPECT_EQ(32, g.concat->dim(3).value()); + EXPECT_EQ(3, g.concat->axis()); +} + +TEST(ConvertNCHWToNHWC, LeakyRelu) +{ + LeakyReluGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.leakyrelu->features()); + + auto leakyrelu_succs = loco::succs(g.leakyrelu); + EXPECT_EQ(1, leakyrelu_succs.size()); + check_post_trans(*leakyrelu_succs.begin()); + + // Check leakyrelu shape + EXPECT_EQ(1, g.leakyrelu->dim(0).value()); + EXPECT_EQ(4, g.leakyrelu->dim(1).value()); + EXPECT_EQ(4, g.leakyrelu->dim(2).value()); + EXPECT_EQ(16, g.leakyrelu->dim(3).value()); +} + +TEST(ConvertNCHWToNHWC, Mul) +{ + MulGraph g; + g.init(); + + run_phase(&g.g, false, false); + + auto input_succs = loco::succs(g.input); + EXPECT_EQ(1, input_succs.size()); + check_post_trans(*input_succs.begin()); + + check_pre_trans(g.mul->x()); + + auto mul_succs = loco::succs(g.mul); + EXPECT_EQ(1, mul_succs.size()); + check_post_trans(*mul_succs.begin()); + + uint32_t channel_size = 16; + auto new_multiplier = dynamic_cast<luci::CircleConst *>(g.mul->y()); + EXPECT_NE(nullptr, new_multiplier); + EXPECT_EQ(4, new_multiplier->rank()); + EXPECT_EQ(1, new_multiplier->dim(0).value()); + EXPECT_EQ(1, new_multiplier->dim(1).value()); + EXPECT_EQ(1, new_multiplier->dim(2).value()); + EXPECT_EQ(channel_size, new_multiplier->dim(3).value()); + + check_pre_trans(g.output->from()); +} + +TEST(ConvertNCHWToNHWC, Neg) +{ + NegGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.neg->x()); + + auto neg_succs = loco::succs(g.neg); + EXPECT_EQ(1, neg_succs.size()); + check_post_trans(*neg_succs.begin()); + + // Check leakyrelu shape + EXPECT_EQ(1, g.neg->dim(0).value()); + EXPECT_EQ(4, g.neg->dim(1).value()); + EXPECT_EQ(4, g.neg->dim(2).value()); + EXPECT_EQ(16, g.neg->dim(3).value()); +} + +TEST(ConvertNCHWToNHWC, Pad) +{ + PadGraph g; + g.init(); + + run_phase(&g.g, false, false); + + auto input_succs = loco::succs(g.input); + EXPECT_EQ(1, input_succs.size()); + check_post_trans(*input_succs.begin()); + + check_pre_trans(g.pad->input()); + + auto pad_succs = loco::succs(g.pad); + EXPECT_EQ(1, pad_succs.size()); + check_post_trans(*pad_succs.begin()); + + auto new_paddings = dynamic_cast<luci::CircleConst *>(g.pad->paddings()); + EXPECT_NE(nullptr, new_paddings); + EXPECT_EQ(2, new_paddings->rank()); + EXPECT_EQ(4, new_paddings->dim(0).value()); + EXPECT_EQ(2, new_paddings->dim(1).value()); + EXPECT_EQ(0, new_paddings->at<loco::DataType::S32>(0)); + EXPECT_EQ(0, new_paddings->at<loco::DataType::S32>(1)); + EXPECT_EQ(1, new_paddings->at<loco::DataType::S32>(2)); + EXPECT_EQ(1, new_paddings->at<loco::DataType::S32>(3)); + EXPECT_EQ(2, new_paddings->at<loco::DataType::S32>(4)); + EXPECT_EQ(2, new_paddings->at<loco::DataType::S32>(5)); + EXPECT_EQ(0, new_paddings->at<loco::DataType::S32>(6)); + EXPECT_EQ(0, new_paddings->at<loco::DataType::S32>(7)); + + check_pre_trans(g.output->from()); +} + +TEST(ConvertNCHWToNHWC, Unknown_Shape_NEG) +{ + AddGraph g; + g.init(); + + // Unknown shape + g.input->dim(0).unset(); + g.add->dim(0).unset(); + g.output->dim(0).unset(); + + luci::ConvertNCHWToNHWCPass pass(false, false); + EXPECT_EQ(false, pass.run(&g.g)); +} + +TEST(ConvertNCHWToNHWC, Preserve_Input_Output) +{ + // Preserve input + { + AddGraph g; + g.init(); + + run_phase(&g.g, true, false); + + // Check input shape + EXPECT_EQ(1, g.input->dim(0).value()); + EXPECT_EQ(16, g.input->dim(1).value()); + EXPECT_EQ(4, g.input->dim(2).value()); + EXPECT_EQ(4, g.input->dim(3).value()); + + // Check output shape + EXPECT_EQ(1, g.output->dim(0).value()); + EXPECT_EQ(4, g.output->dim(1).value()); + EXPECT_EQ(4, g.output->dim(2).value()); + EXPECT_EQ(16, g.output->dim(3).value()); + } + + // Preserve output + { + AddGraph g; + g.init(); + + run_phase(&g.g, false, true); + + // Check input shape + EXPECT_EQ(1, g.input->dim(0).value()); + EXPECT_EQ(4, g.input->dim(1).value()); + EXPECT_EQ(4, g.input->dim(2).value()); + EXPECT_EQ(16, g.input->dim(3).value()); + + // Check output shape + EXPECT_EQ(1, g.output->dim(0).value()); + EXPECT_EQ(16, g.output->dim(1).value()); + EXPECT_EQ(4, g.output->dim(2).value()); + EXPECT_EQ(4, g.output->dim(3).value()); + } + + // Preserve both input and output + { + AddGraph g; + g.init(); + + run_phase(&g.g, true, true); + + // Check input shape + EXPECT_EQ(1, g.input->dim(0).value()); + EXPECT_EQ(16, g.input->dim(1).value()); + EXPECT_EQ(4, g.input->dim(2).value()); + EXPECT_EQ(4, g.input->dim(3).value()); + + // Check output shape + EXPECT_EQ(1, g.output->dim(0).value()); + EXPECT_EQ(16, g.output->dim(1).value()); + EXPECT_EQ(4, g.output->dim(2).value()); + EXPECT_EQ(4, g.output->dim(3).value()); + } +} + +TEST(ConvertNCHWToNHWC, Relu) +{ + ReluGraph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.relu->features()); + + auto relu_succs = loco::succs(g.relu); + EXPECT_EQ(1, relu_succs.size()); + check_post_trans(*relu_succs.begin()); + + // Check relu shape + EXPECT_EQ(1, g.relu->dim(0).value()); + EXPECT_EQ(4, g.relu->dim(1).value()); + EXPECT_EQ(4, g.relu->dim(2).value()); + EXPECT_EQ(16, g.relu->dim(3).value()); +} + +TEST(ConvertNCHWToNHWC, Relu6) +{ + Relu6Graph g; + g.init(); + + run_phase(&g.g, true, true); + + check_pre_trans(g.relu6->features()); + + auto relu6_succs = loco::succs(g.relu6); + EXPECT_EQ(1, relu6_succs.size()); + check_post_trans(*relu6_succs.begin()); + + // Check relu6 shape + EXPECT_EQ(1, g.relu6->dim(0).value()); + EXPECT_EQ(4, g.relu6->dim(1).value()); + EXPECT_EQ(4, g.relu6->dim(2).value()); + EXPECT_EQ(16, g.relu6->dim(3).value()); +} diff --git a/compiler/luci/pass/src/FoldAddV2Pass.cpp b/compiler/luci/pass/src/FoldAddV2Pass.cpp new file mode 100644 index 000000000..20c1022f8 --- /dev/null +++ b/compiler/luci/pass/src/FoldAddV2Pass.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021 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/Pass/FoldAddV2Pass.h" + +#include <luci/IR/CircleNodes.h> + +#include <iostream> + +namespace +{ + +bool same_shape(const luci::CircleConst *x, const luci::CircleConst *y) +{ + if (x->rank() != y->rank()) + return false; + + for (uint32_t i = 0; i < x->rank(); i++) + { + if (!(x->dim(i) == y->dim(i))) + return false; + } + + return true; +} + +/** + * Fold AddV2 to const if both inputs are const + **/ +template <loco::DataType T> bool fold_add_v2(luci::CircleCustom *add_v2) +{ + // This should hold for AddV2 + if (add_v2->numInputs() != 2) + return false; + + // Check first input is const + auto x = dynamic_cast<luci::CircleConst *>(add_v2->inputs(0)); + if (not x) + return false; + + // Check second input is const + auto y = dynamic_cast<luci::CircleConst *>(add_v2->inputs(1)); + if (not y) + return false; + + if (x->dtype() != y->dtype()) + return false; + + if (!same_shape(x, y)) + return false; + + auto name_x = x->name(); + auto name_y = y->name(); + assert(name_x.length() > 0); + assert(name_y.length() > 0); + auto constant = add_v2->graph()->nodes()->create<luci::CircleConst>(); + constant->dtype(x->dtype()); + constant->rank(x->rank()); + for (uint32_t i = 0; i < x->rank(); i++) + constant->dim(i).set(x->dim(i).value()); + + const auto size = x->size<T>(); + constant->size<T>(size); + for (uint32_t i = 0; i < size; i++) + constant->at<T>(i) = x->at<T>(i) + y->at<T>(i); + + constant->shape_status(luci::ShapeStatus::VALID); + constant->name(name_x + ";" + name_y); + + for (auto succ : loco::succs(add_v2)) + { + auto custom_out = loco::must_cast<luci::CircleCustomOut *>(succ); + loco::replace(custom_out).with(constant); + } + + return true; +} + +} // namespace + +namespace luci +{ + +/** + * Constant Folding for AddV2 Op + **/ +bool FoldAddV2Pass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto custom = dynamic_cast<luci::CircleCustom *>(node)) + { + if (custom->custom_code() == "AddV2") + { + // TODO: Support more data types + if (custom->dtype() == loco::DataType::S64) + { + if (fold_add_v2<loco::DataType::S64>(custom)) + changed = true; + } + } + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FoldAddV2Pass.test.cpp b/compiler/luci/pass/src/FoldAddV2Pass.test.cpp new file mode 100644 index 000000000..438d7f077 --- /dev/null +++ b/compiler/luci/pass/src/FoldAddV2Pass.test.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 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/Pass/FoldAddV2Pass.h" +#include "PassTestGraphs.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +/** + * Graph has an AddV2 Op with constant inputs + * + * BEFORE + * + * [CircleConst] [CircleConst] + * | | + * [CircleCustom (AddV2)] + * | + * [CircleCustomOut] + * + * AFTER + * + * [CircleConst] + */ +template <loco::DataType T> class FoldAddV2Test : public luci::ConstantFoldingAddTestGraph +{ +public: + FoldAddV2Test(std::initializer_list<uint32_t> shape) : luci::ConstantFoldingAddTestGraph(shape, T) + { + _addV2 = _g.nodes()->create<luci::CircleCustom>(2, 1); + _x = _g.nodes()->create<luci::CircleConst>(); + _y = _g.nodes()->create<luci::CircleConst>(); + _addV2_out = _g.nodes()->create<luci::CircleCustomOut>(); + + _addV2->dtype(T); + _x->dtype(T); + _y->dtype(T); + _addV2_out->dtype(T); + + _addV2->shape(shape); + _x->shape(shape); + _y->shape(shape); + _addV2_out->shape(shape); + + uint32_t num_elems = 1; + for (auto dim = shape.begin(); dim != shape.end(); dim++) + num_elems *= *dim; + + _x->size<T>(num_elems); + _y->size<T>(num_elems); + + for (uint32_t i = 0; i < num_elems; i++) + { + _x->at<T>(i) = i + 1; + _y->at<T>(i) = i + 1; + } + + _addV2->custom_code("AddV2"); + _addV2->inputs(0, _x); + _addV2->inputs(1, _y); + _addV2_out->input(_addV2); + + _addV2->name("addV2"); + _x->name("x"); + _y->name("y"); + } + + loco::Node *createFoldedPattern() override { return _addV2_out; } + + virtual ~FoldAddV2Test() = default; + +protected: + luci::CircleCustom *_addV2 = nullptr; + luci::CircleCustomOut *_addV2_out = nullptr; + luci::CircleConst *_x = nullptr; + luci::CircleConst *_y = nullptr; +}; + +class FoldS64AddV2Test : public FoldAddV2Test<loco::DataType::S64>, public ::testing::Test +{ +public: + FoldS64AddV2Test() : FoldAddV2Test<loco::DataType::S64>({3}) {} + + virtual void SetUp() { init(); } +}; + +} // namespace + +TEST(FoldAddV2PassTest, name) +{ + luci::FoldAddV2Pass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(FoldS64AddV2Test, fold_addV2) +{ + luci::FoldAddV2Pass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Check type, shape, values of folded const + EXPECT_EQ(loco::DataType::S64, folded_const->dtype()); + EXPECT_EQ(1, folded_const->rank()); + EXPECT_EQ(3, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->at<loco::DataType::S64>(0)); + EXPECT_EQ(4, folded_const->at<loco::DataType::S64>(1)); + EXPECT_EQ(6, folded_const->at<loco::DataType::S64>(2)); +} + +TEST_F(FoldS64AddV2Test, input_type_mismatch_NEG) +{ + _x->dtype(loco::DataType::S32); + + luci::FoldAddV2Pass pass; + EXPECT_FALSE(pass.run(graph())); +} diff --git a/compiler/luci/pass/src/FoldCastPass.cpp b/compiler/luci/pass/src/FoldCastPass.cpp new file mode 100644 index 000000000..00b86fe48 --- /dev/null +++ b/compiler/luci/pass/src/FoldCastPass.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 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/Pass/FoldCastPass.h" + +#include <luci/IR/CircleNodes.h> + +namespace +{ + +luci::CircleConst *cast_const(luci::CircleConst *node, loco::DataType from_dtype, + loco::DataType to_dtype) +{ + assert(node->dtype() == from_dtype); + + auto name = node->name(); + assert(name.length() > 0); + auto constant = node->graph()->nodes()->create<luci::CircleConst>(); + constant->dtype(to_dtype); + constant->rank(node->rank()); + uint32_t num_elems = 1; + for (uint32_t i = 0; i < node->rank(); i++) + { + constant->dim(i).set(node->dim(i).value()); + num_elems *= node->dim(i).value(); + } + + constant->shape_status(luci::ShapeStatus::VALID); + + // TODO: Support more data types + if (from_dtype == loco::DataType::S64) + { + if (to_dtype == loco::DataType::S32) + { + constant->size<loco::DataType::S32>(num_elems); + for (uint32_t i = 0; i < num_elems; i++) + constant->at<loco::DataType::S32>(i) = + static_cast<int32_t>(node->at<loco::DataType::S64>(i)); + + constant->name(name + "_S32"); + return constant; + } + return nullptr; + } + + return nullptr; +} + +/** + * Fold Cast to const if it has const input + **/ +bool fold_cast(luci::CircleCast *cast) +{ + // Check cast has const input + auto const_x = dynamic_cast<luci::CircleConst *>(cast->x()); + if (not const_x) + return false; + + const auto in_dtype = const_x->dtype(); + const auto out_dtype = cast->dtype(); + + auto casted_const = cast_const(const_x, in_dtype, out_dtype); + if (not casted_const) + return false; + + loco::replace(cast).with(casted_const); + + return true; +} + +} // namespace + +namespace luci +{ + +/** + * Constant Folding for Cast Op + **/ +bool FoldCastPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto cast = dynamic_cast<luci::CircleCast *>(node)) + { + if (fold_cast(cast)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FoldCastPass.test.cpp b/compiler/luci/pass/src/FoldCastPass.test.cpp new file mode 100644 index 000000000..5911adf11 --- /dev/null +++ b/compiler/luci/pass/src/FoldCastPass.test.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 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/Pass/FoldCastPass.h" +#include "PassTestGraphs.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +template <loco::DataType FromT, loco::DataType ToT> +class FoldCastTest : public luci::ConstantFoldingAddTestGraph +{ +public: + FoldCastTest(std::initializer_list<uint32_t> shape) + : luci::ConstantFoldingAddTestGraph(shape, ToT) + { + _cast = _g.nodes()->create<luci::CircleCast>(); + _x = _g.nodes()->create<luci::CircleConst>(); + + _cast->dtype(ToT); + _x->dtype(FromT); + + _cast->shape(shape); + _x->shape(shape); + + uint32_t num_elems = 1; + for (auto dim = shape.begin(); dim != shape.end(); dim++) + num_elems *= *dim; + + _x->size<FromT>(num_elems); + for (uint32_t i = 0; i < num_elems; i++) + _x->at<FromT>(i) = i + 1; + + _cast->x(_x); + + _cast->name("cast"); + _x->name("x"); + } + + loco::Node *createFoldedPattern() override { return _cast; } + +protected: + luci::CircleCast *_cast = nullptr; + luci::CircleConst *_x = nullptr; +}; + +/** + * Graph that has a Cast Op with constant input + * + * BEFORE + * + * [CircleConst] + * | + * [Cast] + * + * AFTER + * + * [CircleConst] + * + */ +class FoldS64ToS32CastTest : public FoldCastTest<loco::DataType::S64, loco::DataType::S32>, + public ::testing::Test +{ +public: + FoldS64ToS32CastTest() : FoldCastTest<loco::DataType::S64, loco::DataType::S32>({3}) {} + + virtual void SetUp() { init(); } +}; + +} // namespace + +TEST(FoldCastPassTest, name) +{ + luci::FoldCastPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(FoldS64ToS32CastTest, fold_cast_s64_to_s32) +{ + luci::FoldCastPass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Check type, shape, values of folded const + EXPECT_EQ(loco::DataType::S32, folded_const->dtype()); + EXPECT_EQ(1, folded_const->rank()); + EXPECT_EQ(3, folded_const->dim(0).value()); + EXPECT_EQ(1, folded_const->at<loco::DataType::S32>(0)); + EXPECT_EQ(2, folded_const->at<loco::DataType::S32>(1)); + EXPECT_EQ(3, folded_const->at<loco::DataType::S32>(2)); +} diff --git a/compiler/luci/pass/src/FoldDequantizePass.cpp b/compiler/luci/pass/src/FoldDequantizePass.cpp index 01c04f478..3dd4f8cea 100644 --- a/compiler/luci/pass/src/FoldDequantizePass.cpp +++ b/compiler/luci/pass/src/FoldDequantizePass.cpp @@ -17,8 +17,7 @@ #include "luci/Pass/FoldDequantizePass.h" #include <luci/IR/CircleNodes.h> - -#include <loco/Service/TypeInference.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { @@ -51,6 +50,8 @@ luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node) throw std::runtime_error("Given constant node has no quantization parameter"); } + auto name = const_node->name(); + assert(name.length() > 0); auto g = const_node->graph(); auto new_const_node = g->nodes()->create<luci::CircleConst>(); @@ -64,6 +65,7 @@ luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node) } new_const_node->size<loco::DataType::FLOAT32>(dim_size); new_const_node->shape_status(luci::ShapeStatus::VALID); + new_const_node->name(name + "_DQ"); const int32_t q_dim = const_node->quantparam()->quantized_dimension; const int32_t q_dim_value = const_node->dim(q_dim).value(); @@ -81,8 +83,8 @@ luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node) qd = 0; new_const_node->at<loco::DataType::FLOAT32>(i) = - (float)(const_node->at<loco::DataType::S8>(i) - const_node->quantparam()->zerop.at(qd)) * - const_node->quantparam()->scale.at(qd); + (float)(const_node->at<loco::DataType::S8>(i) - const_node->quantparam()->zerop.at(qd)) * + const_node->quantparam()->scale.at(qd); } } else @@ -94,9 +96,9 @@ luci::CircleConst *dequantized_const_node(luci::CircleConst *const_node) qd = 0; new_const_node->at<loco::DataType::FLOAT32>(i) = - (float)((int)const_node->at<loco::DataType::U8>(i) - - const_node->quantparam()->zerop.at(qd)) * - const_node->quantparam()->scale.at(qd); + (float)((int)const_node->at<loco::DataType::U8>(i) - + const_node->quantparam()->zerop.at(qd)) * + const_node->quantparam()->scale.at(qd); } } @@ -192,6 +194,8 @@ bool FoldDequantizePass::run(loco::Graph *g) if (replace_const_node(const_node_user, const_node)) { loco::replace(dequant).with(const_node_user); + luci::add_origin(loco::must_cast<luci::CircleNode *>(const_node_user), + luci::get_origin(dequant)); changed = true; } } diff --git a/compiler/luci/service/src/Nodes/CircleOutput.cpp b/compiler/luci/pass/src/FoldDequantizePass.test.cpp index d4c8da2d8..d82a7bc87 100644 --- a/compiler/luci/service/src/Nodes/CircleOutput.cpp +++ b/compiler/luci/pass/src/FoldDequantizePass.test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,14 +14,13 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "luci/Pass/FoldDequantizePass.h" -namespace luci -{ +#include <gtest/gtest.h> -ShapeSignature ssinf::Algorithm::visit(const luci::CircleOutput *node) +TEST(FoldDequantizePassTest, name) { - return input_arg_signature(node, 0); + luci::FoldDequantizePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); } - -} // namespace luci diff --git a/compiler/luci/pass/src/FoldSparseToDensePass.cpp b/compiler/luci/pass/src/FoldSparseToDensePass.cpp new file mode 100644 index 000000000..0c6fc43ed --- /dev/null +++ b/compiler/luci/pass/src/FoldSparseToDensePass.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021 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/Pass/FoldSparseToDensePass.h" +#include "CircleOptimizerUtils.h" + +#include <luci/IR/CircleNodes.h> + +namespace +{ + +/** + * Fold to const if + * + * 1. indices has 0-sized static shape such as [0] + * (i.e., output is filled with default value) + * 2. default_value: const scalar + * 3. output_shape: const + * + * TODO: Support more general patterns + **/ +template <loco::DataType IndexT, loco::DataType ValueT> +bool fold_sparse_to_dense(luci::CircleSparseToDense *stod) +{ + const auto indices = loco::must_cast<luci::CircleNode *>(stod->indices()); + const auto default_value = loco::must_cast<luci::CircleConst *>(stod->default_value()); + const auto output_shape = loco::must_cast<luci::CircleConst *>(stod->output_shape()); + + bool has_zero = false; + for (uint32_t i = 0; i < indices->rank(); i++) + { + if (indices->dim(i).known() && indices->dim(i).value() == 0) + has_zero = true; + } + if (!has_zero) + return false; + + if (default_value->rank() != 0 || default_value->size<ValueT>() != 1) + return false; + + auto rank = output_shape->size<IndexT>(); + std::vector<uint32_t> shape; + for (uint32_t i = 0; i < rank; i++) + { + auto dim = output_shape->at<IndexT>(i); + assert(dim >= 0 && dim <= std::numeric_limits<uint32_t>::max()); + if (!(dim >= 0 && dim <= std::numeric_limits<uint32_t>::max())) + return false; + + shape.push_back(dim); + } + + auto name = stod->name(); + assert(name.length() > 0); + auto constant = stod->graph()->nodes()->create<luci::CircleConst>(); + constant->dtype(default_value->dtype()); + constant->rank(rank); + uint32_t dim_size = 1; + for (uint32_t i = 0; i < rank; i++) + { + constant->dim(i).set(shape[i]); + dim_size *= shape[i]; + } + + constant->size<ValueT>(dim_size); + const auto value = default_value->scalar<ValueT>(); + for (uint32_t i = 0; i < dim_size; i++) + constant->at<ValueT>(i) = value; + + constant->shape_status(luci::ShapeStatus::VALID); + constant->name(name + "_D"); + + loco::replace(stod).with(constant); + + return true; +} + +bool fold_sparse_to_dense(luci::CircleSparseToDense *stod) +{ + auto indices = loco::must_cast<luci::CircleNode *>(stod->indices()); + auto default_value = dynamic_cast<luci::CircleConst *>(stod->default_value()); + if (not default_value) + return false; + + auto output_shape = dynamic_cast<luci::CircleConst *>(stod->output_shape()); + if (not output_shape) + return false; + + // Illegal input check + if (indices->dtype() != output_shape->dtype()) + throw std::runtime_error("indices and output_shape of SparseToDense must have the same dtype"); + + // TODO: Support more data types + if (indices->dtype() == loco::DataType::S64) + { + if (default_value->dtype() == loco::DataType::S64) + { + return fold_sparse_to_dense<loco::DataType::S64, loco::DataType::S64>(stod); + } + } + return false; +} + +} // namespace + +namespace luci +{ + +/** + * Constant Folding for SparseToDense Op + **/ +bool FoldSparseToDensePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto stod = dynamic_cast<luci::CircleSparseToDense *>(node)) + { + if (fold_sparse_to_dense(stod)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FoldSparseToDensePass.test.cpp b/compiler/luci/pass/src/FoldSparseToDensePass.test.cpp new file mode 100644 index 000000000..7c6dcb033 --- /dev/null +++ b/compiler/luci/pass/src/FoldSparseToDensePass.test.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021 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/Pass/FoldSparseToDensePass.h" +#include "PassTestGraphs.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +/** + * Graph that has a SparseToDense Op with zero-sized indices + * + * BEFORE + * - shape of indices: [0,1] + * - output_shape: [3] + * - default_value: scalar 2 + * + * [indices] [output_shape] [values] [default_value] + * | | | | + * +------[SparseToDense]------+ + * + * AFTER + * + * [Const] (shape: [3], values: [2, 2, 2]) + * + */ +class S64SparseToDenseZeroIndicesTest : public luci::ConstantFoldingAddTestGraph, + public ::testing::Test +{ +public: + S64SparseToDenseZeroIndicesTest() : luci::ConstantFoldingAddTestGraph({3}, loco::DataType::S64) {} + + virtual void SetUp() { init(); } + + loco::Node *createFoldedPattern() override + { + _stod = _g.nodes()->create<luci::CircleSparseToDense>(); + _indices = _g.nodes()->create<luci::CircleConst>(); + _output_shape = _g.nodes()->create<luci::CircleConst>(); + _values = _g.nodes()->create<luci::CircleConst>(); + _default_value = _g.nodes()->create<luci::CircleConst>(); + + _stod->dtype(loco::DataType::S64); + _indices->dtype(loco::DataType::S64); + _output_shape->dtype(loco::DataType::S64); + _values->dtype(loco::DataType::S64); + _default_value->dtype(loco::DataType::S64); + + _indices->shape({0, 1}); + _output_shape->shape({1}); + _values->shape({0}); + _default_value->rank(0); + + _indices->size<loco::DataType::S64>(0); + _output_shape->size<loco::DataType::S64>(1); + _output_shape->at<loco::DataType::S64>(0) = 3; + _values->size<loco::DataType::S64>(0); + _default_value->size<loco::DataType::S64>(1); + _default_value->at<loco::DataType::S64>(0) = 2; + + _stod->indices(_indices); + _stod->output_shape(_output_shape); + _stod->values(_values); + _stod->default_value(_default_value); + + _stod->name("stod"); + _indices->name("indices"); + _output_shape->name("output_shape"); + _values->name("values"); + _default_value->name("default_value"); + + return _stod; + } + +protected: + luci::CircleSparseToDense *_stod = nullptr; + luci::CircleConst *_indices = nullptr; + luci::CircleConst *_output_shape = nullptr; + luci::CircleConst *_values = nullptr; + luci::CircleConst *_default_value = nullptr; +}; + +} // namespace + +TEST(FoldSparseToDensePassTest, name) +{ + luci::FoldSparseToDensePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(S64SparseToDenseZeroIndicesTest, fold_stod_with_zero_indices) +{ + luci::FoldSparseToDensePass pass; + while (pass.run(graph())) + ; + + auto folded_const = getFoldedPattern(); + EXPECT_NE(nullptr, folded_const); + + // Chec type, shape, values of folded const + EXPECT_EQ(loco::DataType::S64, folded_const->dtype()); + EXPECT_EQ(1, folded_const->rank()); + EXPECT_EQ(3, folded_const->dim(0).value()); + EXPECT_EQ(2, folded_const->at<loco::DataType::S64>(0)); + EXPECT_EQ(2, folded_const->at<loco::DataType::S64>(1)); + EXPECT_EQ(2, folded_const->at<loco::DataType::S64>(2)); +} + +TEST_F(S64SparseToDenseZeroIndicesTest, illegal_input_NEG) +{ + _indices->dtype(loco::DataType::S32); + + luci::FoldSparseToDensePass pass; + EXPECT_ANY_THROW(pass.run(graph())); +} diff --git a/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.cpp b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.cpp new file mode 100644 index 000000000..2c990f0a5 --- /dev/null +++ b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2021 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/Pass/ForwardReshapeToUnaryOpPass.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Log.h> +#include <luci/Profile/CircleNodeOrigin.h> +#include <luci/Service/CircleShapeInference.h> +#include <luci/Service/Nodes/CircleConst.h> + +namespace +{ + +luci::CircleReshape *as_reshape(loco::Node *node) +{ + return dynamic_cast<luci::CircleReshape *>(node); +} + +luci::CircleConst *clone_shape(luci::CircleReshape *reshape) +{ + const auto shape = dynamic_cast<luci::CircleConst *>(reshape->shape()); + // only support CircleConst for now + if (shape == nullptr) + return nullptr; + + // NOTE tflite and circle only supports S32 + // TODO just check with assert() after import handles this + auto dtype = shape->dtype(); + if (dtype != loco::DataType::S32) + return nullptr; + + return luci::clone(shape); +} + +void copy_shape(luci::CircleReshape *reshape, luci::CircleReshape *new_reshape) +{ + auto ns_rank = reshape->newShape()->rank(); + new_reshape->newShape()->rank(ns_rank); + for (uint32_t r = 0; r < ns_rank; ++r) + new_reshape->newShape()->dim(r) = reshape->newShape()->dim(r); +} + +bool forward_reshape(luci::CircleReshape *reshape, luci::CircleNeg *neg) +{ + assert(reshape != nullptr); + assert(neg != nullptr); + + luci::CircleConst *cloned_shape = clone_shape(reshape); + if (cloned_shape == nullptr) + return false; + + auto name = reshape->name(); + assert(name.length() > 0); + loco::Graph *graph = neg->graph(); + // create reshape placed after neg + luci::CircleReshape *new_reshape = graph->nodes()->create<luci::CircleReshape>(); + copy_shape(reshape, new_reshape); + new_reshape->shape(cloned_shape); + new_reshape->name(name + "_C"); + luci::add_origin(new_reshape, luci::get_origin(reshape)); + + // reconnect network + loco::replace(neg).with(new_reshape); + neg->x(reshape->tensor()); + new_reshape->tensor(neg); + + // Do shape inference for this node again. + neg->shape_status(luci::ShapeStatus::UNDEFINED); + + return true; +} + +class ForwardReshape final : public luci::CircleNodeMutableVisitor<bool> +{ +protected: + bool visit(luci::CircleNode *node) + { + LOGGER(l); + INFO(l) << "ForwardReshape: Unsupported operator: " << node->name() << std::endl; + return false; + } + + bool visit(luci::CircleNeg *node) + { + auto reshape = as_reshape(node->x()); + if (reshape == nullptr) + return false; + return forward_reshape(reshape, node); + } + + // TODO add more unary operators +}; + +} // namespace + +namespace luci +{ + +/** + * BEFORE + * | + * [CircleNode] [CircleConst] + * | / + * [CircleReshape] + * / | + * [CircleNode] [(UnaryOp)] + * | | \ + * | | [CircleNode] + * | | | + * + * UnaryOp: CircleNeg, ... + * + * AFTER + * | + * [CircleConst] [CircleNode] + * | / | + * [CircleReshape] [(UnaryOp)] [CircleConst] + * | | / + * [CircleNode] [CircleReshape] + * | | \ + * | | [CircleNode] + * | | | + * + * Note: new [CircleReshape] after [(UnaryOp)] added + */ +bool ForwardReshapeToUnaryOpPass::run(loco::Graph *g) +{ + bool changed = false; + ForwardReshape forward; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + if (circle_node->accept(&forward)) + changed = true; + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.test.cpp b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.test.cpp new file mode 100644 index 000000000..2593a014c --- /dev/null +++ b/compiler/luci/pass/src/ForwardReshapeToUnaryOpPass.test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021 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/Pass/ForwardReshapeToUnaryOpPass.h" +#include "luci/Pass/CircleShapeInferencePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <luci/test/TestIOGraph.h> + +#include <gtest/gtest.h> + +#include <vector> + +namespace +{ + +using namespace luci::test; + +class ReshapeNegGraphlet +{ +public: + ReshapeNegGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 shape_in, const ShapeU32 shape_out) + { + std::vector<uint32_t> shape_out_v = shape_out; + + _reshape_shape = g->nodes()->create<luci::CircleConst>(); + _reshape = g->nodes()->create<luci::CircleReshape>(); + _neg = g->nodes()->create<luci::CircleNeg>(); + + _reshape_shape->dtype(loco::DataType::S32); + _reshape_shape->rank(1); + _reshape_shape->dim(0).set(shape_out_v.size()); + _reshape_shape->shape_status(luci::ShapeStatus::VALID); + // values + const auto size = shape_out_v.size(); + _reshape_shape->size<loco::DataType::S32>(size); + for (uint32_t i = 0; i < size; i++) + _reshape_shape->at<loco::DataType::S32>(i) = shape_out_v[i]; + + _reshape_shape->name("reshape_shape"); + _reshape->name("reshape"); + _neg->name("neg"); + } + +protected: + luci::CircleReshape *_reshape = nullptr; + luci::CircleNeg *_neg = nullptr; + luci::CircleConst *_reshape_shape = nullptr; +}; + +class ForwardReshapeToNegGraph : public TestIOGraph, public ReshapeNegGraphlet +{ +public: + ForwardReshapeToNegGraph() = default; + +public: + void init(const ShapeU32 shape_in, const ShapeU32 shape_out) + { + TestIOGraph::init(shape_in, shape_out); + ReshapeNegGraphlet::init(g(), shape_in, shape_out); + + // connect network + _reshape->tensor(input()); + _reshape->shape(_reshape_shape); + _neg->x(_reshape); + + output()->from(_neg); + } +}; + +class ForwardReshapeToNegGraphTest : public ::testing::Test +{ +public: + ForwardReshapeToNegGraphTest() = default; + + void run_pass(void) + { + while (_pass.run(_graph.g())) + ; + } + +protected: + ForwardReshapeToNegGraph _graph; + luci::ForwardReshapeToUnaryOpPass _pass; +}; + +} // namespace + +TEST(ForwardReshapeToUnaryOpPassTest, name) +{ + luci::ForwardReshapeToUnaryOpPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(ForwardReshapeToNegGraphTest, simple_forward) +{ + _graph.init({2, 2, 2}, {2, 4}); + + run_pass(); + + auto reshape = dynamic_cast<luci::CircleReshape *>(_graph.output()->from()); + auto neg = dynamic_cast<luci::CircleNeg *>(_graph.output()->from()); + ASSERT_NE(nullptr, reshape); + ASSERT_EQ(nullptr, neg); + neg = dynamic_cast<luci::CircleNeg *>(reshape->tensor()); + ASSERT_NE(nullptr, neg); +} diff --git a/compiler/luci/pass/src/FuseActivationFunctionPass.cpp b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp index 844541d2d..66e341518 100644 --- a/compiler/luci/pass/src/FuseActivationFunctionPass.cpp +++ b/compiler/luci/pass/src/FuseActivationFunctionPass.cpp @@ -17,7 +17,9 @@ #include "luci/Pass/FuseActivationFunctionPass.h" #include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeMixins.h> #include <luci/IR/CircleOpcode.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace luci { @@ -32,10 +34,15 @@ bool fuse_activation_function(luci::CircleNode *node) return false; auto node_with_fused_act = - dynamic_cast<luci::LuciNodeMixin<luci::LuciNodeTrait::FusedActFunc> *>(pred_node); + dynamic_cast<luci::CircleNodeMixin<luci::CircleNodeTrait::FusedActFunc> *>(pred_node); if (node_with_fused_act == nullptr) return false; + // TODO remove this work-around + // This will skip fuse for concat as luci-interpreter doesn't support this yet + if (dynamic_cast<luci::CircleConcatenation *>(pred_node) != nullptr) + return false; + auto fused_act = node_with_fused_act->fusedActivationFunction(); luci::FusedActFunc target_func = luci::FusedActFunc::UNDEFINED; @@ -76,6 +83,7 @@ bool fuse_activation_function(luci::CircleNode *node) return false; node_with_fused_act->fusedActivationFunction(target_func); + luci::add_origin(pred_node, luci::get_origin(node)); loco::replace(node).with(pred_node); node->drop(); diff --git a/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp b/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp index 226a303a1..56b414143 100644 --- a/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp +++ b/compiler/luci/pass/src/FuseActivationFunctionPass.test.cpp @@ -14,15 +14,19 @@ * limitations under the License. */ -#include "FuseActivationFunctionPassInternal.h" +#include "luci/Pass/FuseActivationFunctionPass.h" #include <luci/IR/CircleNodes.h> +#include <luci/test/TestIOGraph.h> + #include <gtest/gtest.h> namespace { +using namespace luci::test; + /** * Simple graph for test * @@ -41,60 +45,148 @@ namespace * [Conv2] * */ -class SimpleGraph +class ConvReluConvGraphlet +{ +public: + ConvReluConvGraphlet() = default; + + void init(loco::Graph *g) + { + _conv1 = g->nodes()->create<luci::CircleConv2D>(); + _conv2 = g->nodes()->create<luci::CircleConv2D>(); + _relu = g->nodes()->create<luci::CircleRelu>(); + _conv1_f = g->nodes()->create<luci::CircleConst>(); + _conv1_b = g->nodes()->create<luci::CircleConst>(); + _conv2_f = g->nodes()->create<luci::CircleConst>(); + _conv2_b = g->nodes()->create<luci::CircleConst>(); + + _conv1->fusedActivationFunction(luci::FusedActFunc::NONE); + + _conv1->name("conv1"); + _conv2->name("conv2"); + _relu->name("relu"); + _conv1_f->name("conv1f"); + _conv1_b->name("conv1b"); + _conv2_f->name("conv2f"); + _conv2_b->name("conv2b"); + } + +public: + luci::CircleRelu *relu() { return _relu; } + luci::CircleConv2D *conv1() { return _conv1; } + luci::CircleConv2D *conv2() { return _conv2; } + +protected: + luci::CircleConv2D *_conv1 = nullptr; + luci::CircleConv2D *_conv2 = nullptr; + luci::CircleRelu *_relu = nullptr; + luci::CircleConst *_conv1_f = nullptr; + luci::CircleConst *_conv1_b = nullptr; + luci::CircleConst *_conv2_f = nullptr; + luci::CircleConst *_conv2_b = nullptr; +}; + +class FuseActTestGraph : public TestIOGraph, public ConvReluConvGraphlet { public: - SimpleGraph() + FuseActTestGraph() = default; + + void init(void) { - conv1 = g.nodes()->create<luci::CircleConv2D>(); - conv2 = g.nodes()->create<luci::CircleConv2D>(); - relu = g.nodes()->create<luci::CircleRelu>(); + TestIOGraph::init({1}, {1}); + ConvReluConvGraphlet::init(g()); - conv1->fusedActivationFunction(luci::FusedActFunc::NONE); + _conv1->input(input()); + _conv1->filter(_conv1_f); + _conv1->bias(_conv1_b); - relu->features(conv1); - conv2->input(relu); + _relu->features(_conv1); + + _conv2->input(_relu); + _conv2->filter(_conv2_f); + _conv2->bias(_conv2_b); + + output()->from(_conv2); } +}; +class ConvHasMultiSuccGraph : public TestIOGraph, public ConvReluConvGraphlet +{ public: - loco::Graph g; - luci::CircleConv2D *conv1; - luci::CircleConv2D *conv2; - luci::CircleRelu *relu; + ConvHasMultiSuccGraph() = default; + + void init(void) + { + TestIOGraph::init({1}, {1}); + ConvReluConvGraphlet::init(g()); + + _conv1->input(input()); + _conv1->filter(_conv1_f); + _conv1->bias(_conv1_b); + + _relu->features(_conv1); + + _conv2->input(_conv1); + _conv2->filter(_conv2_f); + _conv2->bias(_conv2_b); + + output()->from(_relu); // We need to check from relu + } }; +// TODO use ::testing::Test + } // namespace +TEST(FuseActivationFunctionPassTest, name) +{ + luci::FuseActivationFunctionPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + TEST(FusePreActivationBatchNorm, fuse_activation_function) { - SimpleGraph g; + FuseActTestGraph g; + luci::FuseActivationFunctionPass pass; - EXPECT_TRUE(luci::fuse_activation_function(g.relu)); + g.init(); - EXPECT_EQ(g.conv1, g.conv2->input()); + EXPECT_TRUE(pass.run(g.g())); + EXPECT_EQ(g.conv1(), g.conv2()->input()); } TEST(FusePreActivationBatchNorm, fuse_activation_function_dup_relu) { - SimpleGraph g; - g.conv1->fusedActivationFunction(luci::FusedActFunc::RELU); + FuseActTestGraph g; + luci::FuseActivationFunctionPass pass; - EXPECT_TRUE(luci::fuse_activation_function(g.relu)); + g.init(); + g.conv1()->fusedActivationFunction(luci::FusedActFunc::RELU); - EXPECT_EQ(g.conv1, g.conv2->input()); + EXPECT_TRUE(pass.run(g.g())); + EXPECT_EQ(g.conv1(), g.conv2()->input()); } -TEST(FusePreActivationBatchNorm, fuse_activation_function_NEG) +TEST(FusePreActivationBatchNorm, fuse_activation_function_mulsucc_NEG) { - SimpleGraph g; - g.conv2->input(g.conv1); + ConvHasMultiSuccGraph g; + luci::FuseActivationFunctionPass pass; + + g.init(); - // Conv1 has multiple successors - EXPECT_FALSE(luci::fuse_activation_function(g.relu)); + // Relu input Conv2D has multiple successors + EXPECT_FALSE(pass.run(g.g())); +} + +TEST(FusePreActivationBatchNorm, fuse_activation_function_tanh_NEG) +{ + FuseActTestGraph g; + luci::FuseActivationFunctionPass pass; - g.conv2->input(g.relu); - g.conv1->fusedActivationFunction(luci::FusedActFunc::TANH); + g.init(); + g.conv1()->fusedActivationFunction(luci::FusedActFunc::TANH); - // Conv1 already has activation function - EXPECT_FALSE(luci::fuse_activation_function(g.relu)); + // Relu input Conv2D already has activation function + EXPECT_FALSE(pass.run(g.g())); } diff --git a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp index bd7805f6a..2bca57014 100644 --- a/compiler/luci/pass/src/FuseAddWithTConvPass.cpp +++ b/compiler/luci/pass/src/FuseAddWithTConvPass.cpp @@ -17,20 +17,30 @@ #include "luci/Pass/FuseAddWithTConvPass.h" #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { /** - * Fuse add to TCONV if possible + * Fuse Add to TransposeConv if possible * * BEFORE - * - * [CircleTransposeConv] + * | + * [CircleConst] [CircleTransposeConv] + * \ | + * [CircleAdd] * | - * [add] + * * AFTER + * | + * [CircleConst] | + * \ | + * [CircleTransposeConv] [CircleAdd] + * | + * ([CircleRelu6]) + * | * - * [CircleTransposeConv] + * Note: CircleRelu6 is inserted if Add activation is ReLU6 */ bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv) { @@ -81,9 +91,13 @@ bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv) if (add->fusedActivationFunction() == luci::FusedActFunc::RELU6) { + auto name = addition->name(); + assert(name.length() > 0); // separate relu op from add op auto relu = add->graph()->nodes()->create<luci::CircleRelu6>(); relu->features(tconv); + relu->name(name + "/Relu6"); + luci::add_origin(relu, luci::get_origin(add)); // remove add node replace(add).with(relu); @@ -93,6 +107,9 @@ bool fuse_add_with_tconv(luci::CircleTransposeConv *tconv) replace(add).with(tconv); } + // set origin + luci::add_origin(tconv, luci::get_origin(add)); + return true; } diff --git a/compiler/luci/pass/src/FuseAddWithTConvPass.test.cpp b/compiler/luci/pass/src/FuseAddWithTConvPass.test.cpp new file mode 100644 index 000000000..8748d73ef --- /dev/null +++ b/compiler/luci/pass/src/FuseAddWithTConvPass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/FuseAddWithTConvPass.h" + +#include <gtest/gtest.h> + +TEST(FuseAddWithTConvPassTest, name) +{ + luci::FuseAddWithTConvPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/FuseBCQPass.cpp b/compiler/luci/pass/src/FuseBCQPass.cpp index c0583d848..09180d8c1 100644 --- a/compiler/luci/pass/src/FuseBCQPass.cpp +++ b/compiler/luci/pass/src/FuseBCQPass.cpp @@ -17,6 +17,7 @@ #include "luci/Pass/FuseBCQPass.h" #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> #include <luci/Log.h> #include <cassert> @@ -111,7 +112,7 @@ template <> class BCQFuser<1> { public: BCQFuser<1>(int32_t original_output_cnt, int32_t bundle_cnt) - : _original_output_cnt{original_output_cnt}, _bundle_cnt{bundle_cnt} + : _original_output_cnt{original_output_cnt}, _bundle_cnt{bundle_cnt} { // Do nothing } @@ -133,7 +134,7 @@ public: { const auto prefix = (output_node->index() - (_original_output_cnt + 1)) / (_bundle_cnt); const MetadataType metadata_type = static_cast<MetadataType>( - (output_node->index() - (_original_output_cnt + 1)) % (_bundle_cnt)); + (output_node->index() - (_original_output_cnt + 1)) % (_bundle_cnt)); const auto circle_node = loco::must_cast<luci::CircleNode *>(output_node->from()); add_BCQ_info_node(prefix, metadata_type, circle_node); } @@ -156,13 +157,18 @@ public: if (prefix == -1 || !is_valid_prefix(prefix)) continue; + auto name = gather->name(); + assert(name.length() > 0); + auto bcq_gather = g->nodes()->create<luci::CircleBCQGather>(); + luci::add_origin(bcq_gather, luci::get_origin(gather)); bcq_gather->op_version(1); bcq_gather->input_scales(alpha(g, prefix)); bcq_gather->input_binary(packed_binary_code(g, prefix)); bcq_gather->indices(gather->indices()); bcq_gather->input_clusters(packed_clusters(g, prefix)); + bcq_gather->name(name + "/BCQGather"); if (_do_w_x[prefix]->at<loco::DataType::BOOL>(0)) { @@ -177,7 +183,7 @@ public: bcq_gather->axis(axis_transpose); const auto indices_rank = - loco::must_cast<luci::CircleNode *>(gather->indices())->rank(); + loco::must_cast<luci::CircleNode *>(gather->indices())->rank(); auto perm = g->nodes()->create<luci::CircleConst>(); perm->dtype(loco::DataType::S32); @@ -188,10 +194,13 @@ public: perm->at<loco::DataType::S32>(idx) = idx + 1; perm->at<loco::DataType::S32>(indices_rank) = 0; perm->shape_status(luci::ShapeStatus::VALID); + perm->name(name + "/Transpose/perm"); auto output_transpose = g->nodes()->create<luci::CircleTranspose>(); + luci::add_origin(output_transpose, luci::get_origin(gather)); output_transpose->a(bcq_gather); output_transpose->perm(perm); + output_transpose->name(name + "/Transpose"); loco::replace(gather).with(output_transpose); } @@ -209,7 +218,11 @@ public: if (prefix == -1 || !is_valid_prefix(prefix)) continue; + auto name = fully_connected->name(); + assert(name.length() > 0); + auto bcq_fc = g->nodes()->create<luci::CircleBCQFullyConnected>(); + luci::add_origin(bcq_fc, luci::get_origin(fully_connected)); bcq_fc->op_version(1); bcq_fc->weights_scales(alpha(g, prefix)); @@ -217,6 +230,7 @@ public: bcq_fc->bias(fully_connected->bias()); bcq_fc->weights_clusters(packed_clusters(g, prefix)); bcq_fc->fusedActivationFunction(fully_connected->fusedActivationFunction()); + bcq_fc->name(name + "/BCQFullyConnected"); loco::Node *bcq_input = fully_connected->input(); @@ -231,18 +245,16 @@ public: new_shape->rank(1); new_shape->dim(0) = 2; - auto batch_size = 1; - for (uint32_t i = 0; i < original_input->rank() - 1; ++i) - batch_size *= original_input->dim(i).value(); - - new_shape->at<loco::DataType::S32>(0) = batch_size; - new_shape->at<loco::DataType::S32>(1) = - original_input->dim(original_input->rank() - 1).value(); + new_shape->at<loco::DataType::S32>(0) = -1; + new_shape->at<loco::DataType::S32>(1) = weights->dim(1).value(); new_shape->shape_status(luci::ShapeStatus::VALID); + new_shape->name(name + "/Reshape/shape"); auto reshape = g->nodes()->create<luci::CircleReshape>(); + luci::add_origin(reshape, luci::get_origin(fully_connected)); reshape->tensor(original_input); reshape->shape(new_shape); + reshape->name(name + "/Reshape"); bcq_input = reshape; } @@ -258,23 +270,28 @@ public: perm->at<loco::DataType::S32>(0) = 1; perm->at<loco::DataType::S32>(1) = 0; perm->shape_status(luci::ShapeStatus::VALID); + perm->name(name + "/Transpose/perm"); auto input_transpose = g->nodes()->create<luci::CircleTranspose>(); + luci::add_origin(input_transpose, luci::get_origin(fully_connected)); input_transpose->a(bcq_input); input_transpose->perm(perm); + input_transpose->name(name + "_input/Transpose"); bcq_fc->input(input_transpose); auto output_transpose = g->nodes()->create<luci::CircleTranspose>(); + luci::add_origin(output_transpose, luci::get_origin(fully_connected)); output_transpose->a(bcq_fc); output_transpose->perm(perm); + output_transpose->name(name + "_output/Transpose"); loco::replace(fully_connected).with(output_transpose); return true; } else if (auto weights_as_input = - dynamic_cast<luci::CircleConst *>(fully_connected->input())) + dynamic_cast<luci::CircleConst *>(fully_connected->input())) { auto prefix = get_prefix_of_const(weights_as_input); if (prefix == -1 || !is_valid_prefix(prefix)) @@ -282,6 +299,9 @@ public: assert(_do_w_x[prefix]->at<loco::DataType::BOOL>(0) == true); + auto name = weights_as_input->name(); + assert(name.length() > 0); + auto perm = g->nodes()->create<luci::CircleConst>(); perm->dtype(loco::DataType::S32); perm->size<loco::DataType::S32>(2); @@ -290,12 +310,16 @@ public: perm->at<loco::DataType::S32>(0) = 1; perm->at<loco::DataType::S32>(1) = 0; perm->shape_status(luci::ShapeStatus::VALID); + perm->name(name + "/Transpose/perm"); auto input_transpose = g->nodes()->create<luci::CircleTranspose>(); + luci::add_origin(input_transpose, luci::get_origin(fully_connected)); input_transpose->a(fully_connected->weights()); input_transpose->perm(perm); + input_transpose->name(name + "/Transpose"); auto bcq_fc = g->nodes()->create<luci::CircleBCQFullyConnected>(); + luci::add_origin(bcq_fc, luci::get_origin(fully_connected)); assert(dynamic_cast<luci::CircleOutputExclude *>(fully_connected->bias()) != nullptr); @@ -308,6 +332,8 @@ public: bcq_fc->weights_hidden_size(weights_as_input->dim(1).value()); bcq_fc->input(input_transpose); + bcq_fc->name(name + "/BCQFullyConnected"); + loco::replace(fully_connected).with(bcq_fc); return true; @@ -533,7 +559,7 @@ private: new_beta->dim(1) = _packed_binary_code[prefix]->dim(1); for (uint32_t i = 0; i < _packed_binary_code[prefix]->size<loco::DataType::S32>(); ++i) new_beta->at<loco::DataType::S32>(i) = - _packed_binary_code[prefix]->at<loco::DataType::S32>(i); + _packed_binary_code[prefix]->at<loco::DataType::S32>(i); new_beta->shape_status(luci::ShapeStatus::VALID); return new_beta; @@ -556,9 +582,9 @@ private: for (int i = 0; i < number_of_clusters; ++i) { packed_clusters->at<loco::DataType::S32>(i * 2) = - qbits_of_clusters->at<loco::DataType::S32>(i); + qbits_of_clusters->at<loco::DataType::S32>(i); packed_clusters->at<loco::DataType::S32>(i * 2 + 1) = - size_of_clusters->at<loco::DataType::S32>(i); + size_of_clusters->at<loco::DataType::S32>(i); } return packed_clusters; diff --git a/compiler/luci/pass/src/FuseBCQPass.test.cpp b/compiler/luci/pass/src/FuseBCQPass.test.cpp new file mode 100644 index 000000000..73677affd --- /dev/null +++ b/compiler/luci/pass/src/FuseBCQPass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/FuseBCQPass.h" + +#include <gtest/gtest.h> + +TEST(FuseBCQPassTest, name) +{ + luci::FuseBCQPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/FuseBatchNormWithConvPass.cpp b/compiler/luci/pass/src/FuseBatchNormWithConvPass.cpp new file mode 100644 index 000000000..062da7058 --- /dev/null +++ b/compiler/luci/pass/src/FuseBatchNormWithConvPass.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2021 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/Pass/FuseBatchNormWithConvPass.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> + +namespace +{ +/** + * Fuse Mul-Add to Conv2D if possible. + * + * NOTE TF's BatchNormalization is converted to Mul and Add. + * + * BEFORE + * | [CircleConst] + * | / [CircleConst] + * | / / + * [CircleConv2D] [CircleConst] + * | / + * [CircleMul] [CircleConst] + * | / + * [CircleAdd] + * | + * + * AFTER + * | [CircleConst] + * +--------------+ / [CircleConst] + * | | / / + * | [CircleConv2D] [CircleConst] + * [CircleConst] | | / + * [CircleConst] \ | [CircleMul] [CircleConst] + * \ \ | | / + * [CircleConv2D] [CircleAdd] + * | + */ +bool fused_batch_norm_with_conv(luci::CircleAdd *add) +{ + luci::CircleMul *mul = nullptr; + luci::CircleConst *shift = nullptr; + if (auto add_lhs = dynamic_cast<luci::CircleMul *>(add->x())) + { + mul = add_lhs; + shift = dynamic_cast<luci::CircleConst *>(add->y()); + } + else if (auto add_rhs = dynamic_cast<luci::CircleMul *>(add->y())) + { + mul = add_rhs; + shift = dynamic_cast<luci::CircleConst *>(add->x()); + } + + // If CircleMul is not found or constant operand of CircleAdd is not found, + // this pass cannot be applied. + if (mul == nullptr || shift == nullptr) + return false; + + // If FusedActivationFunction of mul is not none, this pass cannot be applied. + if (mul->fusedActivationFunction() != luci::FusedActFunc::NONE) + return false; + + // To apply this pass, shape of shift should be [1, 1, 1, out_channel]. + if (shift->rank() != 4) + return false; + for (uint32_t i = 0; i < 3; ++i) + if (shift->dim(i).value() != 1) + return false; + + luci::CircleConv2D *conv = nullptr; + luci::CircleConst *scale = nullptr; + if (auto mul_lhs = dynamic_cast<luci::CircleConv2D *>(mul->x())) + { + conv = mul_lhs; + scale = dynamic_cast<luci::CircleConst *>(mul->y()); + } + else if (auto mul_rhs = dynamic_cast<luci::CircleConv2D *>(mul->y())) + { + conv = mul_rhs; + scale = dynamic_cast<luci::CircleConst *>(mul->x()); + } + + // If CircleConv2D is not found or constant operand of CircleMul is not found, + // this pass cannot be applied. + if (conv == nullptr || scale == nullptr) + return false; + + // To apply this pass, shape of scale should be [1, 1, 1, out_channel]. + if (scale->rank() != 4) + return false; + for (uint32_t i = 0; i < 3; ++i) + if (scale->dim(i).value() != 1) + return false; + + // If FusedActivationFunction of conv is not none, this pass cannot be applied. + if (conv->fusedActivationFunction() != luci::FusedActFunc::NONE) + return false; + + luci::CircleConst *filter = dynamic_cast<luci::CircleConst *>(conv->filter()); + luci::CircleConst *bias = dynamic_cast<luci::CircleConst *>(conv->bias()); + + // If filter or bias of conv is not const, this pass cannot be applied. + if (filter == nullptr || bias == nullptr) + return false; + + // If dtype of filter is different with scale and shift, multiplication may be impossible. + if (filter->dtype() != scale->dtype()) + return false; + if (filter->dtype() != shift->dtype()) + return false; + + // TODO Support more data type + if (filter->dtype() != loco::DataType::FLOAT32) + return false; + + // Output channel dimension should be same. If not, this pass cannot be applied. + if (filter->dim(0).value() != scale->dim(3).value()) + return false; + if (filter->dim(0).value() != shift->dim(3).value()) + return false; + + auto name = add->name(); + assert(name.length() > 0); + + luci::CircleConv2D *fused_conv = add->graph()->nodes()->create<luci::CircleConv2D>(); + luci::CircleConst *fused_filter = add->graph()->nodes()->create<luci::CircleConst>(); + luci::CircleConst *fused_bias = add->graph()->nodes()->create<luci::CircleConst>(); + + uint32_t filter_out_channel = filter->dim(0).value(); + uint32_t filter_height = filter->dim(1).value(); + uint32_t filter_width = filter->dim(2).value(); + uint32_t filter_in_channel = filter->dim(3).value(); + + // Copy filter + fused_filter->dtype(filter->dtype()); + fused_filter->size<loco::DataType::FLOAT32>(filter->size<loco::DataType::FLOAT32>()); + fused_filter->rank(4); + fused_filter->dim(0).set(filter_out_channel); + fused_filter->dim(1).set(filter_height); + fused_filter->dim(2).set(filter_width); + fused_filter->dim(3).set(filter_in_channel); + fused_filter->shape_status(luci::ShapeStatus::VALID); + fused_filter->name(name + "/Conv2D/filter"); + + // Fuse scale to new filter + for (uint32_t c = 0; c < filter_out_channel; c++) + { + for (uint32_t h = 0; h < filter_height; h++) + { + for (uint32_t w = 0; w < filter_width; w++) + { + for (uint32_t b = 0; b < filter_in_channel; b++) + { + uint32_t offset = c * filter_height * filter_width * filter_in_channel + + h * filter_width * filter_in_channel + w * filter_in_channel + b; + fused_filter->at<loco::DataType::FLOAT32>(offset) = + filter->at<loco::DataType::FLOAT32>(offset) * scale->at<loco::DataType::FLOAT32>(c); + } + } + } + } + + // Copy bias + assert(bias->rank() == 1); + assert(bias->dim(0).value() == filter_out_channel); + fused_bias->dtype(bias->dtype()); + fused_bias->size<loco::DataType::FLOAT32>(bias->size<loco::DataType::FLOAT32>()); + fused_bias->rank(1); + fused_bias->dim(0).set(filter_out_channel); + fused_bias->shape_status(luci::ShapeStatus::VALID); + fused_bias->name(name + "/Conv2D/bias"); + + // Fuse scale and shift to bias + for (uint32_t b = 0; b < filter_out_channel; ++b) + { + fused_bias->at<loco::DataType::FLOAT32>(b) = + bias->at<loco::DataType::FLOAT32>(b) * scale->at<loco::DataType::FLOAT32>(b) + + shift->at<loco::DataType::FLOAT32>(b); + } + + // Set attributes of fused_conv + fused_conv->input(conv->input()); + fused_conv->filter(fused_filter); + fused_conv->bias(fused_bias); + fused_conv->fusedActivationFunction(add->fusedActivationFunction()); + fused_conv->padding(conv->padding()); + fused_conv->stride()->h(conv->stride()->h()); + fused_conv->stride()->w(conv->stride()->w()); + fused_conv->dilation()->h(conv->dilation()->h()); + fused_conv->dilation()->w(conv->dilation()->w()); + fused_conv->name(name + "/Conv2D"); + luci::add_origin(fused_conv, luci::composite_origin({luci::get_origin(add), luci::get_origin(mul), + luci::get_origin(conv)})); + + replace(add).with(fused_conv); + + return true; +} + +} // namespace + +namespace luci +{ + +bool FuseBatchNormWithConvPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto add = dynamic_cast<luci::CircleAdd *>(node)) + { + if (fused_batch_norm_with_conv(add)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FuseBatchNormWithConvPass.test.cpp b/compiler/luci/pass/src/FuseBatchNormWithConvPass.test.cpp new file mode 100644 index 000000000..96bc2bd35 --- /dev/null +++ b/compiler/luci/pass/src/FuseBatchNormWithConvPass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/FuseBatchNormWithConvPass.h" + +#include <gtest/gtest.h> + +TEST(FuseBatchNormWithConvPassTest, name) +{ + luci::FuseBatchNormWithConvPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/FuseBatchNormWithDwConvPass.cpp b/compiler/luci/pass/src/FuseBatchNormWithDwConvPass.cpp new file mode 100644 index 000000000..8b2286f43 --- /dev/null +++ b/compiler/luci/pass/src/FuseBatchNormWithDwConvPass.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2021 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/Pass/FuseBatchNormWithDwConvPass.h" + +#include "helpers/NodeFiller.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> + +namespace +{ +/** + * Fuse Mul-Add to DepthwiseConv2D if possible. + * + * NOTE TF's BatchNormalization is converted to Mul and Add. + * + * BEFORE + * | [CircleConst] + * | / [CircleConst] + * | / / + * [CircleDepthwiseConv2D] [CircleConst] + * | / + * [CircleMul] [CircleConst] + * | / + * [CircleAdd] + * | + * + * AFTER + * | [CircleConst] + * +-------------------------------------+ / [CircleConst] + * | | / / + * | [CircleDepthwiseConv2D] [CircleConst] + * | [CircleConst] | / + * | / [CircleConst] [CircleMul] [CircleConst] + * | / / | / + * [CircleDepthwiseConv2D] [CircleAdd] + * | + * + */ + +/** + * @brief Check shape is [x] or [1, 1, 1, x] + */ +bool is_scale_shift_shape(luci::CircleConst *node) +{ + auto rank = node->rank(); + if (rank != 1 && rank != 4) + return false; + for (uint32_t r = 0; r < rank - 1; ++r) + { + if (node->dim(r).value() != 1) + return false; + } + return true; +} + +bool fused_batch_norm_with_dwconv(luci::CircleAdd *add) +{ + assert(add != nullptr); + + // Find the pattern of CircleDepthwiseConv2D - CircleMul - CircleAdd + luci::CircleConst *scale = nullptr; + luci::CircleConst *shift = nullptr; + luci::CircleDepthwiseConv2D *dwconv = nullptr; + luci::CircleMul *mul = nullptr; + if (not luci::fill(&shift, &mul).with_commutative_args_of(add)) + return false; + if (not luci::fill(&scale, &dwconv).with_commutative_args_of(mul)) + return false; + + // check scale and shift constant attributes + // scale and shift can be [x] or [1, 1, 1, x] + if (not is_scale_shift_shape(scale)) + return false; + if (not is_scale_shift_shape(shift)) + return false; + + // check mul, add attributes + if (mul->dtype() != loco::DataType::FLOAT32) + return false; + if (mul->fusedActivationFunction() != luci::FusedActFunc::NONE) + return false; + if (add->dtype() != loco::DataType::FLOAT32) + return false; + // TODO support more Activations + if (add->fusedActivationFunction() != luci::FusedActFunc::NONE && + add->fusedActivationFunction() != luci::FusedActFunc::RELU6) + return false; + + // get weight of dwconv + auto filter = dynamic_cast<luci::CircleConst *>(dwconv->filter()); + if (not filter) + return false; + if (filter->dtype() != loco::DataType::FLOAT32) + return false; + if (filter->rank() != 4) + return false; + + // check attributes of dwconv + if (dwconv->fusedActivationFunction() != luci::FusedActFunc::NONE) + return false; + if (dwconv->depthMultiplier() < 0) // can this happen? + return false; + + // get bias of dwconv + auto bias = dynamic_cast<luci::CircleConst *>(dwconv->bias()); + if (not bias) + return false; + if (bias->dtype() != loco::DataType::FLOAT32) + return false; + if (bias->rank() != 1) + return false; + + // filter represents as [1, H, W, C*M] where M is multiplier. + auto filter_out_chn = filter->dim(3).value(); + auto multiplier = static_cast<uint32_t>(dwconv->depthMultiplier()); + auto srank = scale->rank(); // as rank can be 1 or 4 + if (filter_out_chn != scale->dim(srank - 1).value() * multiplier) + return false; + srank = shift->rank(); + if (filter_out_chn != shift->dim(srank - 1).value() * multiplier) + return false; + auto channel = filter_out_chn / multiplier; + + auto name = add->name(); + assert(name.length() > 0); + + loco::Graph *graph = add->graph(); + luci::CircleDepthwiseConv2D *fused_dwconv = graph->nodes()->create<luci::CircleDepthwiseConv2D>(); + luci::CircleConst *fused_filter = graph->nodes()->create<luci::CircleConst>(); + luci::CircleConst *fused_bias = graph->nodes()->create<luci::CircleConst>(); + + auto filter_in_chn = filter->dim(0).value(); + auto filter_height = filter->dim(1).value(); + auto filter_width = filter->dim(2).value(); + assert(filter_in_chn == 1); + + // Copy filter shape + fused_filter->dtype(filter->dtype()); + fused_filter->size<loco::DataType::FLOAT32>(filter->size<loco::DataType::FLOAT32>()); + fused_filter->rank(4); + fused_filter->dim(0).set(filter_in_chn); + fused_filter->dim(1).set(filter_height); + fused_filter->dim(2).set(filter_width); + fused_filter->dim(3).set(filter_out_chn); + fused_filter->shape_status(luci::ShapeStatus::VALID); + fused_filter->name(name + "/DepthwiseConv2D/filter"); + + // fused filter weight = filter weight * mul(scale) + add(shift) + for (uint32_t b = 0; b < filter_in_chn; b++) + { + for (uint32_t h = 0; h < filter_height; h++) + { + for (uint32_t w = 0; w < filter_width; w++) + { + for (uint32_t c = 0; c < filter_out_chn; c++) + { + uint32_t offset = b * filter_height * filter_width * filter_out_chn + + h * filter_width * filter_out_chn + w * filter_out_chn + c; + uint32_t chn = c / multiplier; + fused_filter->at<loco::DataType::FLOAT32>(offset) = + filter->at<loco::DataType::FLOAT32>(offset) * scale->at<loco::DataType::FLOAT32>(chn); + } + } + } + } + + // Fuse bias with scale and shift + fused_bias->dtype(shift->dtype()); + fused_bias->size<loco::DataType::FLOAT32>(shift->size<loco::DataType::FLOAT32>()); + fused_bias->rank(1); + fused_bias->dim(0).set(channel); + fused_bias->shape_status(luci::ShapeStatus::VALID); + for (uint32_t c = 0; c < channel; ++c) + { + fused_bias->at<loco::DataType::FLOAT32>(c) = + bias->at<loco::DataType::FLOAT32>(c) * scale->at<loco::DataType::FLOAT32>(c) + + shift->at<loco::DataType::FLOAT32>(c); + } + fused_bias->name(name + "/DepthwiseConv2D/bias"); + + // set new tconv properties + fused_dwconv->input(dwconv->input()); + fused_dwconv->filter(fused_filter); + fused_dwconv->bias(fused_bias); + fused_dwconv->fusedActivationFunction(add->fusedActivationFunction()); + fused_dwconv->padding(dwconv->padding()); + fused_dwconv->stride()->h(dwconv->stride()->h()); + fused_dwconv->stride()->w(dwconv->stride()->w()); + fused_dwconv->depthMultiplier(dwconv->depthMultiplier()); + fused_dwconv->dilation()->h(dwconv->dilation()->h()); + fused_dwconv->dilation()->w(dwconv->dilation()->w()); + fused_dwconv->name(name + "/DepthwiseConv2D"); + luci::add_origin(fused_dwconv, + luci::composite_origin( + {luci::get_origin(add), luci::get_origin(mul), luci::get_origin(dwconv)})); + + replace(add).with(fused_dwconv); + + return true; +} + +} // namespace + +namespace luci +{ + +bool FuseBatchNormWithDwConvPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto add = dynamic_cast<luci::CircleAdd *>(node)) + { + if (fused_batch_norm_with_dwconv(add)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FuseBatchNormWithDwConvPass.test.cpp b/compiler/luci/pass/src/FuseBatchNormWithDwConvPass.test.cpp new file mode 100644 index 000000000..3030a7306 --- /dev/null +++ b/compiler/luci/pass/src/FuseBatchNormWithDwConvPass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/FuseBatchNormWithDwConvPass.h" + +#include <gtest/gtest.h> + +TEST(FuseBatchNormWithDwConvPassTest, name) +{ + luci::FuseBatchNormWithDwConvPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp b/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp deleted file mode 100644 index 95ccd8176..000000000 --- a/compiler/luci/pass/src/FuseBatchNormWithTConv.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2020 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/Pass/FuseBatchNormWithTConv.h" - -#include <luci/IR/CircleNodes.h> - -namespace -{ -/** - * NOTE TF's fusedBatchNorm is converted to mul and add of Circle. - * - * BEFORE - * - * [CircleTransposeConv] - * | - * [mul] - * | - * [add] - * AFTER - * - * [CircleTransposeConv] - */ -bool fused_batch_norm_with_tconv(luci::CircleTransposeConv *tconv) -{ - // check whether it has bias or not. This optimization works only if it doesn't. - auto bias = dynamic_cast<luci::CircleOutputExclude *>(tconv->bias()); - if (not bias) - return false; - - // get weight of tconv - auto filter = dynamic_cast<luci::CircleConst *>(tconv->filter()); - if (not filter) - return false; - if (filter->dtype() != loco::DataType::FLOAT32) - return false; - - // get mul node - auto tconv_output = loco::succs(tconv); - assert(tconv_output.size() == 1); - auto mul = dynamic_cast<luci::CircleMul *>(*tconv_output.begin()); - if (not mul) - return false; - if (mul->dtype() != loco::DataType::FLOAT32) - return false; - - // get add node - auto mul_output = loco::succs(mul); - assert(mul_output.size() == 1); - auto add = dynamic_cast<luci::CircleAdd *>(*mul_output.begin()); - if (not add) - return false; - if (add->dtype() != loco::DataType::FLOAT32) - return false; - if (add->fusedActivationFunction() != luci::FusedActFunc::NONE && - add->fusedActivationFunction() != luci::FusedActFunc::RELU6) - return false; - - // get scale of batchnorm - auto scale = dynamic_cast<luci::CircleConst *>(mul->y()); - if (not scale) - return false; - - // scale dim(0) == tconv filter channel dim - if (filter->rank() != 4) - return false; - auto filter_out_dim = filter->dim(0).value(); - if (scale->rank() != 1) - return false; - auto scale_dim = scale->dim(0).value(); - if (filter_out_dim != scale_dim) - return false; - - // get shift of batchnorm - auto shift = dynamic_cast<luci::CircleConst *>(add->y()); - if (not shift) - return false; - - // shift dim(0) == tconv filter channel dim - if (shift->rank() != 1) - return false; - auto shift_dim = shift->dim(0).value(); - if (filter_out_dim != shift_dim) - return false; - - // filter weight = filter weight * mul(scale) + add(shift) - uint32_t filter_height_dim = filter->dim(1).value(); - uint32_t filter_width_dim = filter->dim(2).value(); - uint32_t filter_in_dim = filter->dim(3).value(); - for (uint32_t c = 0; c < filter_out_dim; c++) - { - for (uint32_t h = 0; h < filter_height_dim; h++) - { - for (uint32_t w = 0; w < filter_width_dim; w++) - { - for (uint32_t b = 0; b < filter_in_dim; b++) - { - uint32_t offset = c * filter_height_dim * filter_width_dim * filter_in_dim + - h * filter_width_dim * filter_in_dim + w * filter_in_dim + b; - filter->at<loco::DataType::FLOAT32>(offset) *= scale->at<loco::DataType::FLOAT32>(c); - } - } - } - } - - // fuse shift with transposed conv - tconv->bias(shift); - - if (add->fusedActivationFunction() == luci::FusedActFunc::RELU6) - { - // separate relu op from add op - auto relu = add->graph()->nodes()->create<luci::CircleRelu6>(); - relu->features(tconv); - - // remove mul node - replace(add).with(relu); - } - else - { - replace(add).with(tconv); - } - - return true; -} - -} // namespace - -namespace luci -{ - -bool FuseBatchNormWithTConvPass::run(loco::Graph *g) -{ - bool changed = false; - for (auto node : loco::active_nodes(loco::output_nodes(g))) - { - auto tconv = dynamic_cast<luci::CircleTransposeConv *>(node); - if (not tconv) - continue; - - changed |= fused_batch_norm_with_tconv(tconv); - } - - return changed; -} - -} // namespace luci diff --git a/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp new file mode 100644 index 000000000..337954960 --- /dev/null +++ b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2020 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/Pass/FuseBatchNormWithTConvPass.h" + +#include "helpers/NodeFiller.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> + +namespace +{ +/** + * Fuse Mul-Add to TransposeConv if possible. + * + * NOTE TF's BatchNormalization is converted to Mul and Add. + * + * BEFORE + * | [CircleOutputExclude] + * | / [CircleConst] + * | / / + * [CircleTransposeConv] [CircleConst] + * | / + * [CircleMul] [CircleConst] + * | / + * [CircleAdd] + * | + * + * AFTER + * | [CircleOutputExclude] + * +-------------------------------------+ / [CircleConst] + * | | / / + * | [CircleTransposeConv] [CircleConst] + * | [CircleConst] | / + * | / [CircleConst] [CircleMul] [CircleConst] + * | / / | / + * [CircleTransposeConv] [CircleAdd] + * | + * ([CircleRelu6]) + * | + * + * Note: CircleRelu6 is inserted if Add activation is ReLU6 + */ +bool fused_batch_norm_with_tconv(luci::CircleAdd *add) +{ + assert(add != nullptr); + + // Find the pattern of CircleTransposeConv - CircleMul - CircleAdd + luci::CircleConst *scale = nullptr; + luci::CircleConst *shift = nullptr; + luci::CircleTransposeConv *tconv = nullptr; + luci::CircleMul *mul = nullptr; + if (not luci::fill(&shift, &mul).with_commutative_args_of(add)) + return false; + if (not luci::fill(&scale, &tconv).with_commutative_args_of(mul)) + return false; + + // check scale and shift constant attributes + if (scale->rank() != 1) + return false; + if (shift->rank() != 1) + return false; + // check mul, add attributes + if (mul->dtype() != loco::DataType::FLOAT32) + return false; + if (add->dtype() != loco::DataType::FLOAT32) + return false; + if (add->fusedActivationFunction() != luci::FusedActFunc::NONE && + add->fusedActivationFunction() != luci::FusedActFunc::RELU6) + return false; + + // tconv bias should be not set + if (not dynamic_cast<luci::CircleOutputExclude *>(tconv->bias())) + return false; + + // get weight of tconv + auto filter = dynamic_cast<luci::CircleConst *>(tconv->filter()); + if (not filter) + return false; + if (filter->dtype() != loco::DataType::FLOAT32) + return false; + if (filter->rank() != 4) + return false; + + auto filter_out_chn = filter->dim(0).value(); + if (filter_out_chn != scale->dim(0).value()) + return false; + if (filter_out_chn != shift->dim(0).value()) + return false; + + auto name = add->name(); + assert(name.length() > 0); + + loco::Graph *graph = add->graph(); + luci::CircleTransposeConv *fused_tconv = graph->nodes()->create<luci::CircleTransposeConv>(); + luci::CircleConst *fused_filter = graph->nodes()->create<luci::CircleConst>(); + luci::CircleConst *fused_bias = graph->nodes()->create<luci::CircleConst>(); + + auto filter_height = filter->dim(1).value(); + auto filter_width = filter->dim(2).value(); + auto filter_in_chn = filter->dim(3).value(); + + // Copy filter shape + fused_filter->dtype(filter->dtype()); + fused_filter->size<loco::DataType::FLOAT32>(filter->size<loco::DataType::FLOAT32>()); + fused_filter->rank(4); + fused_filter->dim(0).set(filter_out_chn); + fused_filter->dim(1).set(filter_height); + fused_filter->dim(2).set(filter_width); + fused_filter->dim(3).set(filter_in_chn); + fused_filter->shape_status(luci::ShapeStatus::VALID); + fused_filter->name(name + "/TransposeConv/filter"); + + // fused filter weight = filter weight * mul(scale) + add(shift) + for (uint32_t c = 0; c < filter_out_chn; c++) + { + for (uint32_t h = 0; h < filter_height; h++) + { + for (uint32_t w = 0; w < filter_width; w++) + { + for (uint32_t b = 0; b < filter_in_chn; b++) + { + uint32_t offset = c * filter_height * filter_width * filter_in_chn + + h * filter_width * filter_in_chn + w * filter_in_chn + b; + fused_filter->at<loco::DataType::FLOAT32>(offset) = + filter->at<loco::DataType::FLOAT32>(offset) * scale->at<loco::DataType::FLOAT32>(c); + } + } + } + } + + // Copy fused_bias from shift + fused_bias->dtype(shift->dtype()); + fused_bias->size<loco::DataType::FLOAT32>(shift->size<loco::DataType::FLOAT32>()); + fused_bias->rank(1); + fused_bias->dim(0).set(filter_out_chn); + fused_bias->shape_status(luci::ShapeStatus::VALID); + for (uint32_t c = 0; c < filter_out_chn; ++c) + { + fused_bias->at<loco::DataType::FLOAT32>(c) = shift->at<loco::DataType::FLOAT32>(c); + } + fused_bias->name(name + "/TransposeConv/bias"); + + // set new tconv properties + fused_tconv->inputSizes(tconv->inputSizes()); + fused_tconv->filter(fused_filter); + fused_tconv->outBackprop(tconv->outBackprop()); + fused_tconv->bias(fused_bias); + fused_tconv->padding(tconv->padding()); + fused_tconv->stride()->h(tconv->stride()->h()); + fused_tconv->stride()->w(tconv->stride()->w()); + fused_tconv->name(name + "/TransposeConv"); + luci::add_origin(fused_tconv, + luci::composite_origin( + {luci::get_origin(add), luci::get_origin(mul), luci::get_origin(tconv)})); + + if (add->fusedActivationFunction() == luci::FusedActFunc::RELU6) + { + // separate relu op from add op + auto relu = add->graph()->nodes()->create<luci::CircleRelu6>(); + relu->features(fused_tconv); + relu->name(name + "/Relu6"); + luci::add_origin(relu, luci::get_origin(add)); + + replace(add).with(relu); + } + else + { + replace(add).with(fused_tconv); + } + + return true; +} + +} // namespace + +namespace luci +{ + +bool FuseBatchNormWithTConvPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto add = dynamic_cast<luci::CircleAdd *>(node)) + { + if (fused_batch_norm_with_tconv(add)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/FuseBatchNormWithTConvPass.test.cpp b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.test.cpp new file mode 100644 index 000000000..051100dc9 --- /dev/null +++ b/compiler/luci/pass/src/FuseBatchNormWithTConvPass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/FuseBatchNormWithTConvPass.h" + +#include <gtest/gtest.h> + +TEST(FuseBatchNormWithTConvPassTest, name) +{ + luci::FuseBatchNormWithTConvPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/FuseInstanceNormPass.cpp b/compiler/luci/pass/src/FuseInstanceNormPass.cpp index 237152f98..ab7baa1fa 100644 --- a/compiler/luci/pass/src/FuseInstanceNormPass.cpp +++ b/compiler/luci/pass/src/FuseInstanceNormPass.cpp @@ -15,105 +15,16 @@ */ #include "luci/Pass/FuseInstanceNormPass.h" +#include "helpers/NodeFiller.h" #include "FuseInstanceNormPassInternal.h" #include <luci/IR/CircleNodes.h> -#include <loco/Service/ShapeInference.h> +#include <luci/Profile/CircleNodeOrigin.h> #include <cassert> #include <set> -// Helper to find commutative node's arguments -namespace -{ - -/** - * INTRODUCTION - * Binary operation f(x,y) is 'commutative' when - * f(x,y) == f(y,x) holds for all x, y. - * For examples, ADD, MUL and SQUARED_DIFFERENCE are commutative. - * These helpers make it easy to find commutative arguemnts of commtative node. - * - * HOW TO USE - * COMM_NODE *node; - * ARG_TYPE_1 *arg1; - * ARG_TYPE_2 *arg2; - * - * bool ok = fill(&arg1, &arg2).with_commutative_args_of(node); - * - * Result - * If 'node's commutative argument types are actually {ARG_TYPE_1, ARG_TYPE_2} - * (as a set), 'arg1' and 'arg2' set as actual 'node's arguemnts with matching - * type, and return value 'ok' is true. - * Otherwise, 'arg1' and 'arg2' not changed, 'ok' is false. - */ - -template <class ARG_TYPE_1, class ARG_TYPE_2> class NodeFiller final -{ -public: - NodeFiller(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) : _arg_1(arg_1), _arg_2(arg_2) - { - // DO NOTHING - } - - /** - * @return true When 'node's argument types are 'ARG_TYPE_1' and 'ARG_TYPE_2' - * In such case, it assign '_arg_1' and '_arg_2' to actual arguments - * - * @return false When 'node's argument types are NOT matched with 'ARG_TYPE_*' - * In such case, it does not amend '_arg_1' and '_arg_2' - * - * @require COMM_NODE has member x() and y() - */ - template <class COMM_NODE> bool with_commutative_args_of(const COMM_NODE *node); - -private: - ARG_TYPE_1 **_arg_1; - ARG_TYPE_2 **_arg_2; -}; - -template <class ARG_TYPE_1, class ARG_TYPE_2> -inline NodeFiller<ARG_TYPE_1, ARG_TYPE_2> fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) -{ - return NodeFiller<ARG_TYPE_1, ARG_TYPE_2>{arg_1, arg_2}; -} - -template <class ARG_TYPE_1, class ARG_TYPE_2> -template <class COMM_NODE> -bool NodeFiller<ARG_TYPE_1, ARG_TYPE_2>::with_commutative_args_of(const COMM_NODE *node) -{ - // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2 - { - auto x = dynamic_cast<ARG_TYPE_1 *>(node->x()); - auto y = dynamic_cast<ARG_TYPE_2 *>(node->y()); - - if (x && y) - { - *_arg_1 = x; - *_arg_2 = y; - return true; - } - } - - // Case 2) X == ARG_TYPE_2 / Y == ARG_TYPE_1 - { - auto x = dynamic_cast<ARG_TYPE_2 *>(node->x()); - auto y = dynamic_cast<ARG_TYPE_1 *>(node->y()); - - if (x && y) - { - *_arg_1 = y; - *_arg_2 = x; - return true; - } - } - - return false; -} - -} // namespace - // Helper to check detail /// @return true When node has shape of '1 x .. x 1 x depth' @@ -150,11 +61,10 @@ bool is_instance_mean_v0(luci::CircleMean *mean) // // CHECK 1) input is rank 4 // - auto input = mean->input(); - if (not loco::shape_known(input)) + auto input = loco::must_cast<luci::CircleNode *>(mean->input()); + if (input->shape_status() != luci::ShapeStatus::VALID) return false; - auto input_shape = loco::shape_get(input).as<loco::TensorShape>(); - if (input_shape.rank() != 4) + if (input->rank() != 4) return false; // @@ -195,11 +105,10 @@ bool is_instance_mean_v1(luci::CircleMean *mean) // // CHECK 1) input is rank 5 (NHWCX) // - auto input = mean->input(); - if (not loco::shape_known(input)) + auto input = loco::must_cast<luci::CircleNode *>(mean->input()); + if (input->shape_status() != luci::ShapeStatus::VALID) return false; - auto input_shape = loco::shape_get(input).as<loco::TensorShape>(); - if (input_shape.rank() != 5) + if (input->rank() != 5) return false; // @@ -445,8 +354,9 @@ bool InstanceNormPattern::matched() // So it is handled in the separate if statement if (_pv == PatternVersion::Version_2) { - CHECK_OR_FALSE(fill(&mul_gamma, &const_as_beta).with_commutative_args_of(add_as_terminal)); - CHECK_OR_FALSE(fill(&div, &const_as_gamma).with_commutative_args_of(mul_gamma)); + CHECK_OR_FALSE( + luci::fill(&mul_gamma, &const_as_beta).with_commutative_args_of(add_as_terminal)); + CHECK_OR_FALSE(luci::fill(&div, &const_as_gamma).with_commutative_args_of(mul_gamma)); sub = dynamic_cast<luci::CircleSub *>(div->x()); CHECK_OR_FALSE(sub); @@ -456,6 +366,7 @@ bool InstanceNormPattern::matched() luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm); CHECK_OR_FALSE(ifm_node->rank() == 4); + CHECK_OR_FALSE(ifm_node->dim(3).known()); uint32_t ifm_channel_depth = ifm_node->dim(3).value(); mean_of_ifm = dynamic_cast<luci::CircleMean *>(sub->y()); @@ -477,7 +388,7 @@ bool InstanceNormPattern::matched() CHECK_OR_FALSE(zero_point_five->at<loco::DataType::FLOAT32>(0) == 0.5); CHECK_OR_FALSE( - fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); + luci::fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); // TODO Support regarding broadcast CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1); @@ -489,7 +400,8 @@ bool InstanceNormPattern::matched() loco::Node *ifm_should_be = nullptr; luci::CircleMean *mean_of_ifm_should_be = nullptr; - CHECK_OR_FALSE(fill(&ifm_should_be, &mean_of_ifm_should_be).with_commutative_args_of(sqdiff)); + CHECK_OR_FALSE( + luci::fill(&ifm_should_be, &mean_of_ifm_should_be).with_commutative_args_of(sqdiff)); CHECK_OR_FALSE(ifm == ifm_should_be); CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be); @@ -503,25 +415,25 @@ bool InstanceNormPattern::matched() if (_pv == PatternVersion::Version_0) { - CHECK_OR_FALSE(fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal)); - CHECK_OR_FALSE(fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm)); + CHECK_OR_FALSE(luci::fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal)); + CHECK_OR_FALSE(luci::fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm)); } if (_pv == PatternVersion::Version_1) { - CHECK_OR_FALSE(fill(&mul_as_scaled_reshape, &sub).with_commutative_args_of(add_as_terminal)); CHECK_OR_FALSE( - fill(&reshape_of_ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_reshape)); + luci::fill(&mul_as_scaled_reshape, &sub).with_commutative_args_of(add_as_terminal)); + CHECK_OR_FALSE( + luci::fill(&reshape_of_ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_reshape)); ifm = reshape_of_ifm->tensor(); } - CHECK_OR_FALSE(loco::shape_known(ifm)); - auto ifm_shape = loco::shape_get(ifm); - CHECK_OR_FALSE(ifm_shape.domain() == loco::Domain::Tensor); - auto ifm_tensor_shape = ifm_shape.as<loco::TensorShape>(); - CHECK_OR_FALSE(ifm_tensor_shape.rank() == 4); - uint32_t ifm_channel_depth = ifm_tensor_shape.dim(3).value(); + auto ifm_circle = loco::must_cast<luci::CircleNode *>(ifm); + CHECK_OR_FALSE(ifm_circle->shape_status() == luci::ShapeStatus::VALID); + CHECK_OR_FALSE(ifm_circle->rank() == 4); + CHECK_OR_FALSE(ifm_circle->dim(3).known()); + uint32_t ifm_channel_depth = ifm_circle->dim(3).value(); - CHECK_OR_FALSE(fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma)); + CHECK_OR_FALSE(luci::fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma)); if (_pv == PatternVersion::Version_0) { @@ -536,7 +448,7 @@ bool InstanceNormPattern::matched() CHECK_OR_FALSE(add_as_variance); CHECK_OR_FALSE( - fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); + luci::fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance)); CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32); // TODO Support regarding broadcast @@ -557,7 +469,7 @@ bool InstanceNormPattern::matched() if (_pv == PatternVersion::Version_0) { loco::Node *ifm_should_be = nullptr; - CHECK_OR_FALSE(fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff)); + CHECK_OR_FALSE(luci::fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff)); CHECK_OR_FALSE(ifm == ifm_should_be); CHECK_OR_FALSE(is_instance_mean_v0(mean_of_ifm)); CHECK_OR_FALSE(ifm == mean_of_ifm->input()); @@ -565,7 +477,8 @@ bool InstanceNormPattern::matched() if (_pv == PatternVersion::Version_1) { loco::Node *reshape_should_be = nullptr; - CHECK_OR_FALSE(fill(&reshape_should_be, &mean_of_reshape).with_commutative_args_of(sqdiff)); + CHECK_OR_FALSE( + luci::fill(&reshape_should_be, &mean_of_reshape).with_commutative_args_of(sqdiff)); CHECK_OR_FALSE(reshape_of_ifm == reshape_should_be); CHECK_OR_FALSE(is_instance_mean_v1(mean_of_reshape)); CHECK_OR_FALSE(reshape_of_ifm == mean_of_reshape->input()); @@ -592,15 +505,15 @@ bool InstanceNormPattern::matched() if (_pv == PatternVersion::Version_0) { - CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_ifm_should_be) - .with_commutative_args_of(mul_as_scaled_mean)); + CHECK_OR_FALSE(luci::fill(&mul_gamma_should_be, &mean_of_ifm_should_be) + .with_commutative_args_of(mul_as_scaled_mean)); CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be); CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be); } if (_pv == PatternVersion::Version_1) { - CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_reshape_should_be) - .with_commutative_args_of(mul_as_scaled_mean)); + CHECK_OR_FALSE(luci::fill(&mul_gamma_should_be, &mean_of_reshape_should_be) + .with_commutative_args_of(mul_as_scaled_mean)); CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be); CHECK_OR_FALSE(mean_of_reshape == mean_of_reshape_should_be); } @@ -631,47 +544,59 @@ void fuse_instance_norm(const InstanceNormPattern &p) auto graph = p.add_as_terminal->graph(); - // Special case for version 2 (no need to reshape) - if (p.version() == InstanceNormPattern::Version_2) + // Version 0 and 1 need to reshape + if (p.version() != InstanceNormPattern::Version_2) { - // Make Instance Norm to replace - auto instance_norm = graph->nodes()->create<luci::CircleInstanceNorm>(); - instance_norm->input(p.ifm); - instance_norm->gamma(p.const_as_gamma); - instance_norm->beta(p.const_as_beta); - float epsilon = p.const_as_epsilon->at<loco::DataType::FLOAT32>(0); - instance_norm->epsilon(epsilon); - instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction()); - - replace(p.add_as_terminal).with(instance_norm); - - return; - } - - // Make reshape for gamma & beta - auto reshape_gamma = graph->nodes()->create<luci::CircleReshape>(); - auto reshape_beta = graph->nodes()->create<luci::CircleReshape>(); - { - auto ifm_shape = loco::shape_get(p.ifm).as<loco::TensorShape>(); - uint32_t ifm_channel_depth = ifm_shape.dim(3).value(); - - int32_t new_shape[1] = {static_cast<int32_t>(ifm_channel_depth)}; - - reshape_gamma->tensor(p.const_as_gamma); - reshape_beta->tensor(p.const_as_beta); + p.const_as_gamma->rank(1); + p.const_as_gamma->dim(0).set(p.const_as_gamma->size<loco::DataType::FLOAT32>()); + p.const_as_beta->rank(1); + p.const_as_beta->dim(0).set(p.const_as_beta->size<loco::DataType::FLOAT32>()); - luci::set_new_shape(reshape_gamma, new_shape, 1); - luci::set_new_shape(reshape_beta, new_shape, 1); + p.const_as_gamma->shape_status(luci::ShapeStatus::UNDEFINED); + p.const_as_beta->shape_status(luci::ShapeStatus::UNDEFINED); } // Make Instance Norm to replace auto instance_norm = graph->nodes()->create<luci::CircleInstanceNorm>(); instance_norm->input(p.ifm); - instance_norm->gamma(reshape_gamma); - instance_norm->beta(reshape_beta); + instance_norm->gamma(p.const_as_gamma); + instance_norm->beta(p.const_as_beta); float epsilon = p.const_as_epsilon->at<loco::DataType::FLOAT32>(0); instance_norm->epsilon(epsilon); instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction()); + // NOTE unique name should be assigned in export + instance_norm->name("InstanceNorm"); + + // set origin + std::vector<std::shared_ptr<luci::CircleNodeOrigin>> origin_vec{ + luci::get_origin(p.sqdiff), + luci::get_origin(p.mean_as_variance), + luci::get_origin(p.add_as_variance), + luci::get_origin(p.mul_gamma), + luci::get_origin(p.sub), + luci::get_origin(p.add_as_terminal)}; + if (p.version() == InstanceNormPattern::PatternVersion::Version_0) + { + origin_vec.push_back(luci::get_origin(p.mean_of_ifm)); + origin_vec.push_back(luci::get_origin(p.rsqrt)); + origin_vec.push_back(luci::get_origin(p.mul_as_scaled_ifm)); + origin_vec.push_back(luci::get_origin(p.mul_as_scaled_mean)); + } + if (p.version() == InstanceNormPattern::PatternVersion::Version_1) + { + origin_vec.push_back(luci::get_origin(p.reshape_of_ifm)); + origin_vec.push_back(luci::get_origin(p.mean_of_reshape)); + origin_vec.push_back(luci::get_origin(p.rsqrt)); + origin_vec.push_back(luci::get_origin(p.mul_as_scaled_mean)); + origin_vec.push_back(luci::get_origin(p.mul_as_scaled_reshape)); + } + if (p.version() == InstanceNormPattern::PatternVersion::Version_2) + { + origin_vec.push_back(luci::get_origin(p.mean_of_ifm)); + origin_vec.push_back(luci::get_origin(p.pow)); + origin_vec.push_back(luci::get_origin(p.div)); + } + luci::add_origin(instance_norm, luci::composite_origin(origin_vec)); replace(p.add_as_terminal).with(instance_norm); } diff --git a/compiler/luci/pass/src/FuseInstanceNormPass.test.cpp b/compiler/luci/pass/src/FuseInstanceNormPass.test.cpp index 3037f3def..b83ccca50 100644 --- a/compiler/luci/pass/src/FuseInstanceNormPass.test.cpp +++ b/compiler/luci/pass/src/FuseInstanceNormPass.test.cpp @@ -16,6 +16,8 @@ #include "FuseInstanceNormPassInternal.h" +#include "luci/Pass/FuseInstanceNormPass.h" + #include <vector> #include <gtest/gtest.h> @@ -34,6 +36,13 @@ void setShape(luci::CircleNode &node, const std::vector<int> &v) } // namespace +TEST(FuseInstanceNormPassTest, name) +{ + luci::FuseInstanceNormPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + TEST(FuseInstanceNormPass, is_quasi_1D_with_dummy_dim) { luci::CircleConst const_node; diff --git a/compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp b/compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp index bcde5fac4..469fcddbb 100644 --- a/compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp +++ b/compiler/luci/pass/src/FusePreActivationBatchNormPass.cpp @@ -16,9 +16,11 @@ #include "luci/Pass/FusePreActivationBatchNormPass.h" #include "FusePreActivationBatchNormPassInternal.h" +#include "BatchNormPatternFinder.h" #include <luci/IR/CircleNodes.h> #include <luci/Log.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { @@ -37,83 +39,6 @@ bool is_non_negative(const luci::CircleConst *node) return true; } -// Check if mul is batchnorm mul -bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleNode *&pred_node, - luci::CircleConst *&gamma) -{ - auto x = dynamic_cast<luci::CircleConst *>(mul->x()); - auto y = dynamic_cast<luci::CircleConst *>(mul->y()); - - luci::CircleNode *pred = nullptr; - luci::CircleConst *constant = nullptr; - - if (x != nullptr && y == nullptr) - { - pred = loco::must_cast<luci::CircleNode *>(mul->y()); - constant = x; - } - else if (x == nullptr && y != nullptr) - { - pred = loco::must_cast<luci::CircleNode *>(mul->x()); - constant = y; - } - else - { - return false; - } - - if (constant->rank() != 1) - return false; - - auto channel_dim = constant->dim(0); - if (!(channel_dim == mul->dim(mul->rank() - 1))) - return false; - - pred_node = pred; - gamma = constant; - return true; -} - -// Check if add is batchnorm add -bool is_batchnorm_add(const luci::CircleAdd *add, luci::CircleMul *&mul, luci::CircleConst *&beta) -{ - auto x = loco::must_cast<luci::CircleNode *>(add->x()); - auto y = loco::must_cast<luci::CircleNode *>(add->y()); - - luci::CircleMul *pred = nullptr; - luci::CircleConst *constant = nullptr; - - if (add->fusedActivationFunction() != luci::FusedActFunc::RELU) - return false; - - if (x->opcode() == luci::CircleOpcode::CIRCLECONST && y->opcode() == luci::CircleOpcode::MUL) - { - pred = loco::must_cast<luci::CircleMul *>(y); - constant = loco::must_cast<luci::CircleConst *>(x); - } - else if (x->opcode() == luci::CircleOpcode::MUL && y->opcode() == luci::CircleOpcode::CIRCLECONST) - { - pred = loco::must_cast<luci::CircleMul *>(x); - constant = loco::must_cast<luci::CircleConst *>(y); - } - else - { - return false; - } - - if (constant->rank() != 1) - return false; - - auto channel_dim = constant->dim(0); - // Assumption: Layout is channel-last - if (!(channel_dim == add->dim(add->rank() - 1))) - return false; - - mul = pred; - beta = constant; - return true; -} - const luci::CircleConv2D *get_forward_conv2d(const luci::CircleNode *node, uint32_t channel_size) { auto opcode = node->opcode(); @@ -249,6 +174,9 @@ bool update_conv_bias_with_beta(luci::CircleConv2D *conv, const luci::CircleCons auto size = beta->dim(0).value(); auto bias = dynamic_cast<luci::CircleConst *>(conv->bias()); + auto name = conv->name(); + assert(name.length() > 0); + if (bias == nullptr) { bias = conv->graph()->nodes()->create<luci::CircleConst>(); @@ -256,6 +184,7 @@ bool update_conv_bias_with_beta(luci::CircleConv2D *conv, const luci::CircleCons bias->rank(1); bias->dim(0).set(size); bias->size<loco::DataType::FLOAT32>(size); + bias->name(name + "/bias"); conv->bias(bias); } else @@ -282,14 +211,12 @@ bool update_conv_bias_with_beta(luci::CircleConv2D *conv, const luci::CircleCons luci::CircleSub *insert_sub(luci::CircleNode *pred, luci::CircleConst *beta) { + auto name = pred->name(); + assert(name.length() > 0); + auto sub = pred->graph()->nodes()->create<luci::CircleSub>(); - sub->dtype(loco::DataType::FLOAT32); - sub->rank(pred->rank()); - for (uint32_t i = 0; i < sub->rank(); i++) - { - sub->dim(i).set(pred->dim(i).value()); - } sub->fusedActivationFunction(luci::FusedActFunc::NONE); + sub->name(name + "/Sub"); loco::replace(pred).with(sub); @@ -366,6 +293,8 @@ bool fuse_sub_with_conv(luci::CircleSub *sub) if (!update_conv_bias_with_beta(conv, beta, false)) return false; + luci::add_origin(conv, luci::get_origin(sub)); + auto pred = sub->x(); loco::replace(sub).with(pred); @@ -442,6 +371,7 @@ bool fuse_add_with_conv(luci::CircleAdd *add, std::vector<luci::CircleSub *> &su if (!update_conv_bias_with_beta(conv, beta, true)) return false; + luci::add_origin(conv, luci::get_origin(add)); loco::replace(add).with(pred); add->drop(); @@ -462,6 +392,8 @@ bool fuse_add_with_conv(luci::CircleAdd *add, std::vector<luci::CircleSub *> &su if (!update_conv_bias_with_beta(conv, beta, true)) return false; + luci::add_origin(conv, luci::get_origin(add)); + auto relu = *loco::succs(add).begin(); auto relu_node = loco::must_cast<luci::CircleRelu *>(relu); assert(relu_node != nullptr); @@ -471,6 +403,7 @@ bool fuse_add_with_conv(luci::CircleAdd *add, std::vector<luci::CircleSub *> &su add->drop(); sub_list.push_back(insert_sub(pred, beta)); + luci::add_origin(sub_list.back(), luci::get_origin(add)); relu_node->features(pred); @@ -530,6 +463,11 @@ bool fuse_mul_with_conv(luci::CircleMul *mul) // Update CONV weights update_conv_weights_with_gamma(conv, gamma); + + // Update origin + // TODO need to remove const + luci::add_origin(const_cast<luci::CircleConv2D *>(conv), + luci::get_origin(loco::must_cast<luci::CircleNode *>(mul))); } loco::replace(mul).with(pred_node); @@ -568,6 +506,8 @@ bool swap_mul_add(luci::CircleAdd *add, std::vector<luci::CircleMul *> &mul_list if (!is_batchnorm_add(add, mul, beta)) return false; + if (add->fusedActivationFunction() != luci::FusedActFunc::RELU) + return false; if (loco::succs(mul).size() != 1) return false; @@ -582,8 +522,13 @@ bool swap_mul_add(luci::CircleAdd *add, std::vector<luci::CircleMul *> &mul_list return false; // Insert Relu at the bottom + auto name = add->name(); + assert(name.length() > 0); + auto relu = add->graph()->nodes()->create<luci::CircleRelu>(); relu->features(mul); + relu->name(name + "/Relu"); + luci::add_origin(relu, luci::get_origin(add)); loco::replace(add).with(relu); // Replace beta <- beta / gamma diff --git a/compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp b/compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp index a79b5bd5d..3d5791c9e 100644 --- a/compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp +++ b/compiler/luci/pass/src/FusePreActivationBatchNormPass.test.cpp @@ -16,6 +16,8 @@ #include "FusePreActivationBatchNormPassInternal.h" +#include "luci/Pass/FusePreActivationBatchNormPass.h" + #include <luci/IR/CircleNodes.h> #include <math.h> @@ -148,6 +150,22 @@ public: conv_filter->at<loco::DataType::FLOAT32>(i * out_size + j) = i * out_size + j; } } + + pred_conv->name("pred_conv"); + pred_conv_filter->name("pred_conv_filter"); + pred_conv_bias->name("pred_conv_bias"); + pred_conv2->name("pred_conv2"); + pred_conv2_filter->name("pred_conv2_filter"); + pred_conv2_bias->name("pred_conv2_bias"); + pred_add->name("pred_add"); + mul->name("mul"); + mul_gamma->name("mul_gamma"); + add->name("add"); + add_beta->name("add_beta"); + conv->name("conv"); + conv_filter->name("conv_filter"); + conv_bias->name("conv_bias"); + succ_add->name("succ_add"); } public: @@ -171,6 +189,13 @@ public: } // namespace +TEST(FusePreActivationBatchNormPassTest, name) +{ + luci::FusePreActivationBatchNormPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + TEST(FusePreActivationBatchNorm, swap_mul_add) { SimpleGraph g; diff --git a/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp b/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp index 281d1b081..96776dc92 100644 --- a/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp +++ b/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.cpp @@ -16,6 +16,8 @@ #include "luci/Pass/MakeBatchNormGammaPositivePass.h" +#include "BatchNormPatternFinder.h" + #include <luci/IR/CircleNodes.h> namespace @@ -39,71 +41,27 @@ bool negative_gamma_to_positive(luci::CircleConst *gamma) return changed; } -// Check if add is batchnorm add -bool is_batchnorm_add(const luci::CircleAdd *add) +bool make_positive_gamma(luci::CircleAdd *add) { - auto x = dynamic_cast<luci::CircleConst *>(add->x()); - auto y = dynamic_cast<luci::CircleConst *>(add->y()); - - luci::CircleConst *constant = nullptr; + luci::CircleMul *mul = nullptr; + luci::CircleConst *beta = nullptr; + luci::CircleConst *gamma = nullptr; + luci::CircleNode *pred = nullptr; - if (x != nullptr && y == nullptr) - constant = x; - else if (x == nullptr && y != nullptr) - constant = y; - else + if (!is_batchnorm_add(add, mul, beta)) return false; - if (constant->rank() != 1) + if (loco::succs(mul).size() != 1) return false; + if (!is_batchnorm_mul(mul, pred, gamma)) + return false; + assert(pred == add); // Only support Relu if (add->fusedActivationFunction() != luci::FusedActFunc::RELU) return false; - auto channel_dim = constant->dim(0); - if (!(channel_dim == add->dim(add->rank() - 1))) - return false; - - return true; -} - -// Check if mul is batchnorm mul -bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleConst *&gamma) -{ - auto x = dynamic_cast<luci::CircleConst *>(mul->x()); - auto y = dynamic_cast<luci::CircleConst *>(mul->y()); - - luci::CircleConst *constant = nullptr; - - if (x != nullptr && y == nullptr) - constant = x; - else if (x == nullptr && y != nullptr) - constant = y; - else - return false; - - if (constant->rank() != 1) - return false; - - auto channel_dim = constant->dim(0); - if (!(channel_dim == mul->dim(mul->rank() - 1))) - return false; - - // Check successor is batchnorm add - auto succs = loco::succs(mul); - if (succs.size() != 1) - return false; - - auto add = dynamic_cast<luci::CircleAdd *>(*succs.begin()); - if (add == nullptr) - return false; - - if (!is_batchnorm_add(add)) - return false; - - gamma = constant; - return true; + return negative_gamma_to_positive(gamma); } } // namespace @@ -111,18 +69,29 @@ bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleConst *&gamma) namespace luci { +/** + * Make negative gamma values of Mul-Add (as BatchNorm) to a small positive value (1e-10) + * + * PATTERN: + * | + * [CircleNode] [CircleConst](as gamma) + * | | + * [CircleMul] [CircleConst] + * | | + * [CircleAdd] + * | + */ bool MakeBatchNormGammaPositivePass::run(loco::Graph *g) { bool changed = false; for (auto node : loco::active_nodes(loco::output_nodes(g))) { - auto mul = dynamic_cast<luci::CircleMul *>(node); - if (mul == nullptr) + auto add = dynamic_cast<luci::CircleAdd *>(node); + if (add == nullptr) continue; - luci::CircleConst *gamma; - if (is_batchnorm_mul(mul, gamma)) - changed = negative_gamma_to_positive(gamma); + if (make_positive_gamma(add)) + changed = true; } return changed; } diff --git a/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.test.cpp b/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.test.cpp new file mode 100644 index 000000000..83093edc8 --- /dev/null +++ b/compiler/luci/pass/src/MakeBatchNormGammaPositivePass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/MakeBatchNormGammaPositivePass.h" + +#include <gtest/gtest.h> + +TEST(MakeBatchNormGammaPositivePassTest, name) +{ + luci::MakeBatchNormGammaPositivePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/MigrateLegacyShapeDtypePass.cpp b/compiler/luci/pass/src/MigrateLegacyShapeDtypePass.cpp deleted file mode 100644 index beb962a05..000000000 --- a/compiler/luci/pass/src/MigrateLegacyShapeDtypePass.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2020 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/Pass/MigrateLegacyShapeDtypePass.h" - -#include <loco/Service/ShapeInference.h> -#include <loco/Service/TypeInference.h> - -#include <luci/IR/CircleNodes.h> - -#include <loco.h> - -namespace -{ - -bool has_same_shape(luci::CircleNode *node, loco::TensorShape shape) -{ - if (node->rank() != shape.rank()) - return false; - - for (uint32_t i = 0; i < shape.rank(); ++i) - if (!(node->dim(i) == shape.dim(i))) - return false; - - return true; -} - -} // namespace - -namespace luci -{ - -bool MigrateLegacyShapeDtypePass::run(luci::Module *m) -{ - bool changed = false; - - for (size_t g = 0; g < m->size(); ++g) - { - if (run(m->graph(g))) - changed = true; - } - - return changed; -} - -bool MigrateLegacyShapeDtypePass::run(loco::Graph *g) -{ - bool changed = false; - - for (auto node : loco::all_nodes(g)) - { - auto circle_node = loco::must_cast<luci::CircleNode *>(node); - if (loco::shape_known(node)) - { - auto loco_shape = loco::shape_get(node).as<loco::TensorShape>(); - - assert(circle_node->shape_signature().rank() == 0 || - circle_node->shape_signature().rank() == loco_shape.rank()); - - // When shape of loco is copied to circle node, ShapeSignature should be applied. - loco::TensorShape new_shape; - new_shape.rank(loco_shape.rank()); - for (uint32_t i = 0; i < loco_shape.rank(); ++i) - { - if (circle_node->shape_signature().rank() > 0 && - circle_node->shape_signature().dim(i) == -1) - new_shape.dim(i) = 1; - else - new_shape.dim(i) = loco_shape.dim(i); - } - - if (circle_node->shape_status() == luci::ShapeStatus::UNDEFINED || - !has_same_shape(circle_node, new_shape)) - { - circle_node->rank(new_shape.rank()); - for (uint32_t i = 0; i < new_shape.rank(); ++i) - circle_node->dim(i) = new_shape.dim(i); - - if (circle_node->shape_status() == luci::ShapeStatus::UNDEFINED) - circle_node->shape_status(luci::ShapeStatus::VALID); - - changed = true; - } - } - - if (loco::dtype_known(node)) - { - if (loco::dtype_get(node) != circle_node->dtype()) - { - circle_node->dtype(loco::dtype_get(node)); - changed = true; - } - } - } - - return changed; -} - -} // namespace luci diff --git a/compiler/luci/pass/src/ModulePhase.test.cpp b/compiler/luci/pass/src/ModulePhase.test.cpp new file mode 100644 index 000000000..5d92c59f4 --- /dev/null +++ b/compiler/luci/pass/src/ModulePhase.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 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 "ModulePhase.h" + +#include "luci/Pass/CircleShapeInferencePass.h" + +#include <loco.h> + +#include <gtest/gtest.h> + +TEST(ModulePhaseTest, saturate) +{ + auto m = luci::make_module(); + auto g = loco::make_graph(); + m->add(std::move(g)); + + luci::Phase phase; + + // Any Pass will do for testing + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + + luci::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{m.get()}; + phase_runner.run(phase); + + SUCCEED(); +} + +TEST(ModulePhaseTest, restart) +{ + auto m = luci::make_module(); + auto g = loco::make_graph(); + m->add(std::move(g)); + + luci::Phase phase; + + // Any Pass will do for testing + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + + luci::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{m.get()}; + phase_runner.run(phase); + + SUCCEED(); +} diff --git a/compiler/luci/pass/src/PassTestGraphs.h b/compiler/luci/pass/src/PassTestGraphs.h new file mode 100644 index 000000000..f5ae24f0b --- /dev/null +++ b/compiler/luci/pass/src/PassTestGraphs.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 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 __LUCI_PASS_TEST_GRAPHS_H__ +#define __LUCI_PASS_TEST_GRAPHS_H__ + +#include <loco.h> +#include <luci/IR/CircleNodes.h> + +namespace luci +{ + +/** + * ConstantFoldingTestGraph is a base class for testing + * constant folding passes. It creates Input and Output + * in the below graph. Child classes must implement Connector + * and Folded pattern. + * + * [Input] [Folded pattern] (Implemented by child class) + * \ / + * [Connector] (Implemented by child class) + * | + * [Output] + * + * Connector should satisfy the below conditions + * - Input type == Output type == Folded pattern type + * - Input shape == Output shape == Folded pattern shape + * + * For example, Add, Mul, Sub, .. can be a Connector + */ +class ConstantFoldingTestGraph +{ +public: + ConstantFoldingTestGraph(std::vector<uint32_t> input_shape, loco::DataType input_dtype) + { + _input = _g.nodes()->create<luci::CircleInput>(); + _output = _g.nodes()->create<luci::CircleOutput>(); + + auto graph_input = _g.inputs()->create(); + _input->index(graph_input->index()); + auto graph_output = _g.outputs()->create(); + _output->index(graph_output->index()); + + graph_input->dtype(input_dtype); + graph_output->dtype(input_dtype); + _input->dtype(input_dtype); + _output->dtype(input_dtype); + + auto input_tensor_shape = std::make_unique<loco::TensorShape>(); + input_tensor_shape->rank(input_shape.size()); + for (int i = 0; i < input_shape.size(); i++) + input_tensor_shape->dim(i).set(input_shape[i]); + graph_input->shape(std::move(input_tensor_shape)); + + auto output_tensor_shape = std::make_unique<loco::TensorShape>(); + output_tensor_shape->rank(input_shape.size()); + for (int i = 0; i < input_shape.size(); i++) + output_tensor_shape->dim(i).set(input_shape[i]); + graph_output->shape(std::move(output_tensor_shape)); + + _input->rank(input_shape.size()); + for (int i = 0; i < input_shape.size(); i++) + _input->dim(i).set(input_shape[i]); + + _output->rank(input_shape.size()); + for (int i = 0; i < input_shape.size(); i++) + _output->dim(i).set(input_shape[i]); + + _input->name("input"); + _output->name("output"); + } + + virtual void init() = 0; + + virtual ~ConstantFoldingTestGraph() = default; + + virtual loco::Node *createFoldedPattern() = 0; + + virtual luci::CircleConst *getFoldedPattern() = 0; + + loco::Graph *graph() { return &_g; } + + // NOTE: we're not adding _ prefix as these class members are public +protected: + loco::Graph _g; + luci::CircleInput *_input = nullptr; + luci::CircleOutput *_output = nullptr; +}; + +/** + * ConstantFoldingTestAddGraph is ConstantFoldingTestGraph + * whose Connector is Add. + */ +class ConstantFoldingAddTestGraph : public ConstantFoldingTestGraph +{ +protected: + ConstantFoldingAddTestGraph(std::vector<uint32_t> input_shape, loco::DataType input_dtype) + : ConstantFoldingTestGraph(input_shape, input_dtype) + { + _add = _g.nodes()->create<luci::CircleAdd>(); + _add->dtype(input_dtype); + + _add->rank(input_shape.size()); + for (int i = 0; i < input_shape.size(); i++) + _add->dim(i).set(input_shape[i]); + + _add->x(_input); + + _output->from(_add); + + _add->name("add"); + } + +protected: + void init() override { _add->y(createFoldedPattern()); } + +protected: + luci::CircleConst *getFoldedPattern() override + { + return dynamic_cast<luci::CircleConst *>(_add->y()); + } + +protected: + luci::CircleAdd *_add = nullptr; +}; + +} // namespace luci + +#endif // __LUCI_PASS_TEST_GRAPHS_H__ diff --git a/compiler/luci/pass/src/ProgressReporter.h b/compiler/luci/pass/src/ProgressReporter.h index cf30da735..8c6c95e65 100644 --- a/compiler/luci/pass/src/ProgressReporter.h +++ b/compiler/luci/pass/src/ProgressReporter.h @@ -30,7 +30,7 @@ class ProgressReporter : public logo::PhaseEventListener { public: ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) - : _graph{graph}, _strategy{strategy} + : _graph{graph}, _strategy{strategy} { // DO NOTHING } @@ -54,7 +54,7 @@ class ModuleProgressReporter : public logo::PhaseEventListener { public: ModuleProgressReporter(luci::Module *module, logo::PhaseStrategy strategy) - : _module{module}, _strategy{strategy} + : _module{module}, _strategy{strategy} { // DO NOTHING } diff --git a/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp b/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp index 0f8d562e9..de973a431 100644 --- a/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp +++ b/compiler/luci/pass/src/PropagateConcatenationQparam.test.cpp @@ -136,30 +136,34 @@ class ConstInputConcatGraph public: ConstInputConcatGraph(loco::DataType quant_type) { - concat_node.dtype(quant_type); - concat_node.fusedActivationFunction(luci::FusedActFunc::NONE); - input_1.dtype(loco::DataType::FLOAT32); - input_1.size<loco::DataType::FLOAT32>(5); + concat_node = g.nodes()->create<luci::CircleConcatenation>(2); + input_1 = g.nodes()->create<luci::CircleConst>(); + input_2 = g.nodes()->create<luci::CircleConv2D>(); + + concat_node->dtype(quant_type); + concat_node->fusedActivationFunction(luci::FusedActFunc::NONE); + input_1->dtype(loco::DataType::FLOAT32); + input_1->size<loco::DataType::FLOAT32>(5); for (int i = 0; i < 5; i++) { // Set data {-2, -1, 0, 1, 2} - input_1.at<loco::DataType::FLOAT32>(i) = i - 2.0; + input_1->at<loco::DataType::FLOAT32>(i) = i - 2.0; } - input_2.dtype(quant_type); + input_2->dtype(quant_type); - concat_node.values(0, &input_1); - concat_node.values(1, &input_2); + concat_node->values(0, input_1); + concat_node->values(1, input_2); if (quant_type == loco::DataType::U8) { - addQuantParam(concat_node, {0.1}, {10}); - addQuantParam(input_2, {2.0}, {2}); + addQuantParam(*concat_node, {0.1}, {10}); + addQuantParam(*input_2, {2.0}, {2}); } else if (quant_type == loco::DataType::S16) { - addQuantParam(concat_node, {0.1}, {0}); - addQuantParam(input_2, {2.0}, {0}); + addQuantParam(*concat_node, {0.1}, {0}); + addQuantParam(*input_2, {2.0}, {0}); } else { @@ -167,16 +171,11 @@ public: } } - ~ConstInputConcatGraph() - { - concat_node.values(0, nullptr); - concat_node.values(1, nullptr); - } - public: - luci::CircleConcatenation concat_node{2}; - luci::CircleConst input_1; - luci::CircleConv2D input_2; + loco::Graph g; + luci::CircleConcatenation *concat_node = nullptr; + luci::CircleConst *input_1 = nullptr; + luci::CircleConv2D *input_2 = nullptr; }; } // namespace @@ -223,19 +222,20 @@ TEST(PropagateConcatenationQparam, propagate_concat_quantparam_u8) // input_1 is const. const values are quantized with the qparam of concat ConstInputConcatGraph cg(loco::DataType::U8); - luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::U8); - EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]); - EXPECT_EQ(10, cg.concat_node.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(0.1, cg.input_1.quantparam()->scale[0]); - EXPECT_EQ(10, cg.input_1.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(0.1, cg.input_2.quantparam()->scale[0]); - EXPECT_EQ(10, cg.input_2.quantparam()->zerop[0]); - EXPECT_EQ(loco::DataType::U8, cg.input_1.dtype()); - EXPECT_EQ(0, cg.input_1.at<loco::DataType::U8>(0)); - EXPECT_EQ(0, cg.input_1.at<loco::DataType::U8>(1)); - EXPECT_EQ(10, cg.input_1.at<loco::DataType::U8>(2)); - EXPECT_EQ(20, cg.input_1.at<loco::DataType::U8>(3)); - EXPECT_EQ(30, cg.input_1.at<loco::DataType::U8>(4)); + luci::propagate_concat_quantparam(cg.concat_node, loco::DataType::U8); + EXPECT_FLOAT_EQ(0.1, cg.concat_node->quantparam()->scale[0]); + EXPECT_EQ(10, cg.concat_node->quantparam()->zerop[0]); + const auto cg_input_1 = loco::must_cast<luci::CircleConst *>(cg.concat_node->values(0)); + EXPECT_FLOAT_EQ(0.1, cg_input_1->quantparam()->scale[0]); + EXPECT_EQ(10, cg_input_1->quantparam()->zerop[0]); + EXPECT_FLOAT_EQ(0.1, cg.input_2->quantparam()->scale[0]); + EXPECT_EQ(10, cg.input_2->quantparam()->zerop[0]); + EXPECT_EQ(loco::DataType::U8, cg_input_1->dtype()); + EXPECT_EQ(0, cg_input_1->at<loco::DataType::U8>(0)); + EXPECT_EQ(0, cg_input_1->at<loco::DataType::U8>(1)); + EXPECT_EQ(10, cg_input_1->at<loco::DataType::U8>(2)); + EXPECT_EQ(20, cg_input_1->at<loco::DataType::U8>(3)); + EXPECT_EQ(30, cg_input_1->at<loco::DataType::U8>(4)); } TEST(PropagateConcatenationQparam, propagate_concat_quantparam_u8_NEG) @@ -260,20 +260,21 @@ TEST(PropagateConcatenationQparam, propagate_concat_quantparam_u8_NEG) // concat has fused activation function and input_1 is const. // const values are quantized using its min/max ConstInputConcatGraph cg(loco::DataType::U8); - cg.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU); - luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::U8); - EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]); - EXPECT_EQ(10, cg.concat_node.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(0.015686275, cg.input_1.quantparam()->scale[0]); - EXPECT_EQ(128, cg.input_1.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(2.0, cg.input_2.quantparam()->scale[0]); - EXPECT_EQ(2, cg.input_2.quantparam()->zerop[0]); - EXPECT_EQ(loco::DataType::U8, cg.input_1.dtype()); - EXPECT_EQ(quantize(-2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(0)); - EXPECT_EQ(quantize(-1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(1)); - EXPECT_EQ(quantize(0, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(2)); - EXPECT_EQ(quantize(1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(3)); - EXPECT_EQ(quantize(2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::U8>(4)); + cg.concat_node->fusedActivationFunction(luci::FusedActFunc::RELU); + luci::propagate_concat_quantparam(cg.concat_node, loco::DataType::U8); + EXPECT_FLOAT_EQ(0.1, cg.concat_node->quantparam()->scale[0]); + EXPECT_EQ(10, cg.concat_node->quantparam()->zerop[0]); + const auto cg_input_1 = loco::must_cast<luci::CircleConst *>(cg.concat_node->values(0)); + EXPECT_FLOAT_EQ(0.015686275, cg_input_1->quantparam()->scale[0]); + EXPECT_EQ(128, cg_input_1->quantparam()->zerop[0]); + EXPECT_FLOAT_EQ(2.0, cg.input_2->quantparam()->scale[0]); + EXPECT_EQ(2, cg.input_2->quantparam()->zerop[0]); + EXPECT_EQ(loco::DataType::U8, cg_input_1->dtype()); + EXPECT_EQ(quantize(-2, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::U8>(0)); + EXPECT_EQ(quantize(-1, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::U8>(1)); + EXPECT_EQ(quantize(0, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::U8>(2)); + EXPECT_EQ(quantize(1, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::U8>(3)); + EXPECT_EQ(quantize(2, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::U8>(4)); } TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16) @@ -318,19 +319,20 @@ TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16) // input_1 is const. const values are quantized with the qparam of concat ConstInputConcatGraph cg(loco::DataType::S16); - luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::S16); - EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]); - EXPECT_EQ(0, cg.concat_node.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(0.1, cg.input_1.quantparam()->scale[0]); - EXPECT_EQ(0, cg.input_1.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(0.1, cg.input_2.quantparam()->scale[0]); - EXPECT_EQ(0, cg.input_2.quantparam()->zerop[0]); - EXPECT_EQ(loco::DataType::S16, cg.input_1.dtype()); - EXPECT_EQ(-20, cg.input_1.at<loco::DataType::S16>(0)); - EXPECT_EQ(-10, cg.input_1.at<loco::DataType::S16>(1)); - EXPECT_EQ(0, cg.input_1.at<loco::DataType::S16>(2)); - EXPECT_EQ(10, cg.input_1.at<loco::DataType::S16>(3)); - EXPECT_EQ(20, cg.input_1.at<loco::DataType::S16>(4)); + luci::propagate_concat_quantparam(cg.concat_node, loco::DataType::S16); + EXPECT_FLOAT_EQ(0.1, cg.concat_node->quantparam()->scale[0]); + EXPECT_EQ(0, cg.concat_node->quantparam()->zerop[0]); + const auto cg_input_1 = loco::must_cast<luci::CircleConst *>(cg.concat_node->values(0)); + EXPECT_FLOAT_EQ(0.1, cg_input_1->quantparam()->scale[0]); + EXPECT_EQ(0, cg_input_1->quantparam()->zerop[0]); + EXPECT_FLOAT_EQ(0.1, cg.input_2->quantparam()->scale[0]); + EXPECT_EQ(0, cg.input_2->quantparam()->zerop[0]); + EXPECT_EQ(loco::DataType::S16, cg_input_1->dtype()); + EXPECT_EQ(-20, cg_input_1->at<loco::DataType::S16>(0)); + EXPECT_EQ(-10, cg_input_1->at<loco::DataType::S16>(1)); + EXPECT_EQ(0, cg_input_1->at<loco::DataType::S16>(2)); + EXPECT_EQ(10, cg_input_1->at<loco::DataType::S16>(3)); + EXPECT_EQ(20, cg_input_1->at<loco::DataType::S16>(4)); } TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16_NEG) @@ -355,18 +357,19 @@ TEST(PropagateConcatenationQparam, propagate_concat_quantparam_i16_NEG) // concat has fused activation function and input_1 is const. // const values are quantized using its min/max ConstInputConcatGraph cg(loco::DataType::S16); - cg.concat_node.fusedActivationFunction(luci::FusedActFunc::RELU); - luci::propagate_concat_quantparam(&cg.concat_node, loco::DataType::S16); - EXPECT_FLOAT_EQ(0.1, cg.concat_node.quantparam()->scale[0]); - EXPECT_EQ(0, cg.concat_node.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(0.000061037, cg.input_1.quantparam()->scale[0]); - EXPECT_EQ(0, cg.input_1.quantparam()->zerop[0]); - EXPECT_FLOAT_EQ(2.0, cg.input_2.quantparam()->scale[0]); - EXPECT_EQ(0, cg.input_2.quantparam()->zerop[0]); - EXPECT_EQ(loco::DataType::S16, cg.input_1.dtype()); - EXPECT_EQ(quantize(-2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(0)); - EXPECT_EQ(quantize(-1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(1)); - EXPECT_EQ(quantize(0, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(2)); - EXPECT_EQ(quantize(1, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(3)); - EXPECT_EQ(quantize(2, cg.input_1.quantparam()), cg.input_1.at<loco::DataType::S16>(4)); + cg.concat_node->fusedActivationFunction(luci::FusedActFunc::RELU); + luci::propagate_concat_quantparam(cg.concat_node, loco::DataType::S16); + EXPECT_FLOAT_EQ(0.1, cg.concat_node->quantparam()->scale[0]); + EXPECT_EQ(0, cg.concat_node->quantparam()->zerop[0]); + const auto cg_input_1 = loco::must_cast<luci::CircleConst *>(cg.concat_node->values(0)); + EXPECT_FLOAT_EQ(0.000061037, cg_input_1->quantparam()->scale[0]); + EXPECT_EQ(0, cg_input_1->quantparam()->zerop[0]); + EXPECT_FLOAT_EQ(2.0, cg.input_2->quantparam()->scale[0]); + EXPECT_EQ(0, cg.input_2->quantparam()->zerop[0]); + EXPECT_EQ(loco::DataType::S16, cg_input_1->dtype()); + EXPECT_EQ(quantize(-2, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::S16>(0)); + EXPECT_EQ(quantize(-1, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::S16>(1)); + EXPECT_EQ(quantize(0, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::S16>(2)); + EXPECT_EQ(quantize(1, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::S16>(3)); + EXPECT_EQ(quantize(2, cg_input_1->quantparam()), cg_input_1->at<loco::DataType::S16>(4)); } diff --git a/compiler/luci/pass/src/PropagateQuantParamPass.cpp b/compiler/luci/pass/src/PropagateQuantParamPass.cpp index af83cd83b..26282086b 100644 --- a/compiler/luci/pass/src/PropagateQuantParamPass.cpp +++ b/compiler/luci/pass/src/PropagateQuantParamPass.cpp @@ -91,9 +91,8 @@ bool PropagateQuantParamPass::run(loco::Graph *g) INFO(l) << "PropagateQuantParamPass visit node: " << circle_node->name() << std::endl; PropagateQuantParam pqp; - changed = circle_node->accept(&pqp); - if (changed) - break; + if (circle_node->accept(&pqp)) + changed = true; } return changed; diff --git a/compiler/luci/pass/src/PropagateQuantParamPass.test.cpp b/compiler/luci/pass/src/PropagateQuantParamPass.test.cpp index 15adbfc01..ed1f96828 100644 --- a/compiler/luci/pass/src/PropagateQuantParamPass.test.cpp +++ b/compiler/luci/pass/src/PropagateQuantParamPass.test.cpp @@ -83,6 +83,13 @@ public: } // namespace +TEST(PropagateQuantParamPassTest, name) +{ + luci::PropagateQuantParamPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + TEST(PropagateQuantParam, simple) { SimpleGraph g; diff --git a/compiler/luci/pass/src/QuantizationUtils.cpp b/compiler/luci/pass/src/QuantizationUtils.cpp index fa0141114..85d600e47 100644 --- a/compiler/luci/pass/src/QuantizationUtils.cpp +++ b/compiler/luci/pass/src/QuantizationUtils.cpp @@ -96,7 +96,7 @@ void asymmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float data = data < nudged_min ? nudged_min : data; data = data > nudged_max ? nudged_max : data; quantized_values[i] = - static_cast<int32_t>(std::round((data - nudged_min) * scaling_factor_inv)); + static_cast<int32_t>(std::round((data - nudged_min) * scaling_factor_inv)); } node->dtype(loco::DataType::U8); // change the type of tensor @@ -133,14 +133,14 @@ void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float for (uint32_t i = 0; i < size; ++i) { node->at<loco::DataType::S16>(i) = - std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); + std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); } } void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t &zp, float &nudged_min, float &nudged_max) { - assert(min != max); + assert(min <= max); const int32_t kMaxScale = std::numeric_limits<int16_t>::max(); const int32_t kMinScale = -kMaxScale; @@ -158,8 +158,8 @@ void compute_sym_scale_zp(float min, float max, float &scaling_factor, int64_t & scale_factor_from_max_side = rmax / qmax_double; scaling_factor = scale_factor_from_min_side > scale_factor_from_max_side - ? scale_factor_from_min_side - : scale_factor_from_max_side; + ? scale_factor_from_min_side + : scale_factor_from_max_side; zp = 0; nudged_min = static_cast<float>(qmin_double * scaling_factor); nudged_max = static_cast<float>(qmax_double * scaling_factor); @@ -226,7 +226,8 @@ void compute_asym_scale_zp(float min, float max, float &scaling_factor, int64_t zp = nudged_zero_point; } -bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, int &channel_dim_index) +bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, + int32_t &channel_dim_index) { auto succs = loco::succs(node); @@ -304,7 +305,7 @@ bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, int uint32_t cal_offset(loco::TensorShape &dimension, uint32_t *indices) { return indices[0] * dimension.dim(1).value() * dimension.dim(2).value() * - dimension.dim(3).value() + + dimension.dim(3).value() + indices[1] * dimension.dim(2).value() * dimension.dim(3).value() + indices[2] * dimension.dim(3).value() + indices[3]; } diff --git a/compiler/luci/pass/src/QuantizationUtils.h b/compiler/luci/pass/src/QuantizationUtils.h index 22a5cf1ee..c8c558d3c 100644 --- a/compiler/luci/pass/src/QuantizationUtils.h +++ b/compiler/luci/pass/src/QuantizationUtils.h @@ -37,7 +37,8 @@ void symmetric_wquant_with_minmax_per_layer(CircleConst *node, float min, float float &scaling_factor, int64_t &zp, float &nudged_min, float &nudged_max); -bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, int &channel_dim_index); +bool get_channel_dim_index(CircleConst *node, loco::TensorShape &dimension, + int32_t &channel_dim_index); uint32_t cal_offset(loco::TensorShape &dimension, uint32_t *indices); diff --git a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp index e10c4bb4d..e99c7b389 100644 --- a/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp +++ b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.cpp @@ -24,33 +24,29 @@ #include <iostream> #include <cmath> - -namespace luci -{ +#include <functional> namespace { -void cal_minmax_per_channel(CircleConst *node, std::vector<float> &min, std::vector<float> &max) +using namespace luci; +using IterFunc = std::function<void(uint32_t *, loco::TensorShape &, int32_t)>; + +void iterate_per_channel(CircleConst *node, IterFunc func) { loco::TensorShape dimension; dimension.rank(4); uint32_t indices[4] = { - 0, + 0, }; - int channel_dim_index{0}; - int size{0}; + int32_t channel_dim_index{0}; if (!get_channel_dim_index(node, dimension, channel_dim_index)) { assert(false); return; } - size = dimension.dim(channel_dim_index).value(); - std::vector<bool> has_min_max_value(size, false); - min.resize(size); - max.resize(size); for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) { for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) @@ -59,25 +55,57 @@ void cal_minmax_per_channel(CircleConst *node, std::vector<float> &min, std::vec { for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) { - int channel_idx = indices[channel_dim_index]; - auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); - if (has_min_max_value[channel_idx]) - { - min[channel_idx] = data < min[channel_idx] ? data : min[channel_idx]; - max[channel_idx] = data > max[channel_idx] ? data : max[channel_idx]; - } - else - { - min[channel_idx] = data; - max[channel_idx] = data; - has_min_max_value[channel_idx] = true; - } + func(indices, dimension, channel_dim_index); } } } } } +} // namespace + +namespace luci +{ + +namespace +{ + +void cal_minmax_per_channel(CircleConst *node, std::vector<float> &min, std::vector<float> &max) +{ + loco::TensorShape dimension; + dimension.rank(4); + int32_t channel_dim_index{0}; + + if (!get_channel_dim_index(node, dimension, channel_dim_index)) + { + assert(false); + return; + } + auto size = dimension.dim(channel_dim_index).value(); + + std::vector<bool> has_min_max_value(size, false); + min.resize(size); + max.resize(size); + + auto cal_minmax = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); + if (has_min_max_value[channel_idx]) + { + min[channel_idx] = data < min[channel_idx] ? data : min[channel_idx]; + max[channel_idx] = data > max[channel_idx] ? data : max[channel_idx]; + } + else + { + min[channel_idx] = data; + max[channel_idx] = data; + has_min_max_value[channel_idx] = true; + } + }; + + iterate_per_channel(node, cal_minmax); +} + void sym_wquant_per_channel(CircleConst *node, std::vector<float> &min, std::vector<float> &max, std::vector<float> &scaling_factor, std::vector<int64_t> &zp, std::vector<float> &nudged_min, std::vector<float> &nudged_max) @@ -94,45 +122,24 @@ void sym_wquant_per_channel(CircleConst *node, std::vector<float> &min, std::vec compute_sym_scale_zp(min[i], max[i], scaling_factor[i], zp[i], nudged_min[i], nudged_max[i]); } - loco::TensorShape dimension; - dimension.rank(4); - uint32_t indices[4] = { - 0, + auto quantize = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; + auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); + data = data < nudged_min[channel_idx] ? nudged_min[channel_idx] : data; + data = data > nudged_max[channel_idx] ? nudged_max[channel_idx] : data; + quantized_values[cal_offset(dimension, indices)] = + static_cast<int32_t>(std::round(data * scaling_factor_inv)); }; - int channel_dim_index{0}; - - if (!get_channel_dim_index(node, dimension, channel_dim_index)) - { - assert(false); - return; - } - for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) - { - for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) - { - for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) - { - for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) - { - int channel_idx = indices[channel_dim_index]; - const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; - auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); - data = data < nudged_min[channel_idx] ? nudged_min[channel_idx] : data; - data = data > nudged_max[channel_idx] ? nudged_max[channel_idx] : data; - quantized_values[cal_offset(dimension, indices)] = - static_cast<int32_t>(std::round(data * scaling_factor_inv)); - } - } - } - } + iterate_per_channel(node, quantize); node->dtype(loco::DataType::S16); // change the type of tensor node->size<loco::DataType::S16>(size); // resize tensor for (uint32_t i = 0; i < size; ++i) { node->at<loco::DataType::S16>(i) = - std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); + std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); } } @@ -142,35 +149,14 @@ void sym_wdequant_per_channel(CircleConst *node, std::vector<float> &scaling_fac uint32_t size = node->size<loco::DataType::S16>(); std::vector<float> dequantized_values(size); - loco::TensorShape dimension; - dimension.rank(4); - uint32_t indices[4] = { - 0, + auto dequantize = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + auto data = node->at<loco::DataType::S16>(cal_offset(dimension, indices)); + dequantized_values[cal_offset(dimension, indices)] = + static_cast<float>(data) * scaling_factor[channel_idx]; }; - int channel_dim_index{0}; - - if (!get_channel_dim_index(node, dimension, channel_dim_index)) - { - assert(false); - return; - } - for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) - { - for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) - { - for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) - { - for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) - { - int channel_idx = indices[channel_dim_index]; - auto data = node->at<loco::DataType::S16>(cal_offset(dimension, indices)); - dequantized_values[cal_offset(dimension, indices)] = - static_cast<float>(data) * scaling_factor[channel_idx]; - } - } - } - } + iterate_per_channel(node, dequantize); node->dtype(loco::DataType::FLOAT32); // change the type of tensor node->size<loco::DataType::FLOAT32>(size); // resize tensor @@ -198,38 +184,17 @@ void asymmetric_wquant_per_channel(CircleConst *node, std::vector<float> &min, compute_asym_scale_zp(min[i], max[i], scaling_factor[i], zp[i], nudged_min[i], nudged_max[i]); } - loco::TensorShape dimension; - dimension.rank(4); - uint32_t indices[4] = { - 0, + auto quantize = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; + auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); + data = data < nudged_min[channel_idx] ? nudged_min[channel_idx] : data; + data = data > nudged_max[channel_idx] ? nudged_max[channel_idx] : data; + quantized_values[cal_offset(dimension, indices)] = + static_cast<int32_t>(std::round((data - nudged_min[channel_idx]) * scaling_factor_inv)); }; - int channel_dim_index{0}; - - if (!get_channel_dim_index(node, dimension, channel_dim_index)) - { - assert(false); - return; - } - for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) - { - for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) - { - for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) - { - for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) - { - int channel_idx = indices[channel_dim_index]; - const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; - auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); - data = data < nudged_min[channel_idx] ? nudged_min[channel_idx] : data; - data = data > nudged_max[channel_idx] ? nudged_max[channel_idx] : data; - quantized_values[cal_offset(dimension, indices)] = static_cast<int32_t>( - std::round((data - nudged_min[channel_idx]) * scaling_factor_inv)); - } - } - } - } + iterate_per_channel(node, quantize); node->dtype(loco::DataType::U8); // change the type of tensor node->size<loco::DataType::U8>(size); // resize tensor @@ -246,35 +211,14 @@ void asymmetric_wdequant_per_channel(CircleConst *node, std::vector<float> &scal uint32_t size = node->size<loco::DataType::U8>(); std::vector<float> dequantized_values(size); - loco::TensorShape dimension; - dimension.rank(4); - uint32_t indices[4] = { - 0, + auto dequantize = [&](uint32_t *indices, loco::TensorShape &dimension, int channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + auto data = node->at<loco::DataType::U8>(cal_offset(dimension, indices)); + dequantized_values[cal_offset(dimension, indices)] = + static_cast<float>(data) * scaling_factor[channel_idx] + nudged_min[channel_idx]; }; - int channel_dim_index{0}; - - if (!get_channel_dim_index(node, dimension, channel_dim_index)) - { - assert(false); - return; - } - for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) - { - for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) - { - for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) - { - for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) - { - int channel_idx = indices[channel_dim_index]; - auto data = node->at<loco::DataType::U8>(cal_offset(dimension, indices)); - dequantized_values[cal_offset(dimension, indices)] = - static_cast<float>(data) * scaling_factor[channel_idx] + nudged_min[channel_idx]; - } - } - } - } + iterate_per_channel(node, dequantize); node->dtype(loco::DataType::FLOAT32); // change the type of tensor node->size<loco::DataType::FLOAT32>(size); // resize tensor @@ -311,7 +255,7 @@ struct QuantizeDequantizeWeights final : public luci::CircleNodeMutableVisitor<b { QuantizeDequantizeWeights(loco::DataType input, loco::DataType output, QuantizationGranularity granularity) - : input_type(input), output_type(output), granularity(granularity) + : input_type(input), output_type(output), granularity(granularity) { } diff --git a/compiler/stdex/include/stdex/Queue.h b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.test.cpp index c72297bc8..f226253c2 100644 --- a/compiler/stdex/include/stdex/Queue.h +++ b/compiler/luci/pass/src/QuantizeDequantizeWeightsPass.test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,25 +14,14 @@ * limitations under the License. */ -#ifndef __STDEX_QUEUE_H__ -#define __STDEX_QUEUE_H__ +#include "luci/Pass/QuantizeDequantizeWeightsPass.h" -#include <queue> +#include <gtest/gtest.h> -namespace stdex +TEST(QuantizeDequantizeWeightsPassTest, name) { - -/** - * @brief Take the front (= first) element from the queue - * @note The queue SHOULD have at least one element - */ -template <typename T> T take(std::queue<T> &q) -{ - auto res = q.front(); - q.pop(); - return res; + luci::QuantizeDequantizeWeightsPass pass(loco::DataType::FLOAT32, loco::DataType::U8, + luci::QuantizationGranularity::LayerWise); + auto const name = pass.name(); + ASSERT_NE(nullptr, name); } - -} // namespace stdex - -#endif // __STDEX_QUEUE_H__ diff --git a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp index f6eebe3b9..4707ad0e9 100644 --- a/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp +++ b/compiler/luci/pass/src/QuantizeWithMinMaxPass.cpp @@ -19,12 +19,51 @@ #include <luci/IR/CircleNodes.h> #include <luci/IR/CircleNodeVisitor.h> +#include <luci/Service/Nodes/CircleConst.h> #include <luci/Log.h> #include <oops/UserExn.h> #include <iostream> #include <cmath> +#include <functional> + +namespace +{ + +using namespace luci; +using IterFunc = std::function<void(uint32_t *, loco::TensorShape &, int32_t)>; + +void iterate_per_channel(CircleConst *node, int32_t &channel_dim_index, IterFunc func) +{ + loco::TensorShape dimension; + dimension.rank(4); + uint32_t indices[4] = { + 0, + }; + + if (!get_channel_dim_index(node, dimension, channel_dim_index)) + { + assert(false); + return; + } + + for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) + { + for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) + { + for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) + { + for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) + { + func(indices, dimension, channel_dim_index); + } + } + } + } +} + +} // namespace namespace luci { @@ -32,6 +71,30 @@ namespace luci namespace { +// Create a new const node from an existing node. +// The new node has the following characteristics +// type: T +// shape: same with 'node' (given as an argument) +// buffer size: 'size' (given as an argument) +// Note that contents are not filled in this function. +template <loco::DataType T> +luci::CircleConst *create_empty_const_from(luci::CircleConst *node, uint32_t size) +{ + auto new_node = node->graph()->nodes()->create<CircleConst>(); + // TODO: We don't have any naming convention for quantized nodes yet. + // Fix this when we have one. + new_node->name(node->name()); + new_node->dtype(T); + new_node->rank(node->rank()); + for (uint32_t i = 0; i < node->rank(); i++) + new_node->dim(i).set(node->dim(i).value()); + + new_node->size<T>(size); + new_node->shape_status(luci::ShapeStatus::VALID); + + return new_node; +} + void overwrite_quantparam(luci::CircleConcatenation *concat, luci::CircleNode *target) { auto concat_qparam = concat->quantparam(); @@ -44,6 +107,9 @@ void overwrite_quantparam(luci::CircleConcatenation *concat, luci::CircleNode *t auto quantparam = std::make_unique<CircleQuantParam>(); target->quantparam(std::move(quantparam)); target_qparam = target->quantparam(); + + if (target_qparam == nullptr) + throw std::runtime_error("Creating new quant param failed"); } target_qparam->min = concat_qparam->min; target_qparam->max = concat_qparam->max; @@ -79,7 +145,7 @@ void quant_const_values(luci::CircleConst *const_node, float scaling_factor, flo const_node->size<loco::DataType::S16>(size); // resize tensor for (uint32_t i = 0; i < size; ++i) const_node->at<loco::DataType::S16>(i) = - std::min(32767, std::max(-32767, quantized_values[i])); + std::min(32767, std::max(-32767, quantized_values[i])); break; default: throw std::runtime_error("Unsupported data type"); @@ -219,17 +285,16 @@ void quant_const(CircleConst *node, loco::DataType quant_type) } // Check if the node is the bias of Conv2D, DepthwiseConv2D, FullyConnected, or TransposeConv layer -// If true, return <input, weight> pair of the successor node (used to quantize bias) -// If flase, return <nullptr, nullptr> -std::pair<loco::Node *, loco::Node *> get_input_weight_of_bias(CircleNode *node) +// Returns a list of <input, weights, output> vectors for the above operators. +// Note that it returns a 'list' because bias can be used by multiple operators. +std::vector<std::vector<loco::Node *>> get_input_weight_output_of_bias(CircleNode *node) { + std::vector<std::vector<loco::Node *>> result; auto circle_const = dynamic_cast<CircleConst *>(node); if (circle_const == nullptr) - return std::make_pair(nullptr, nullptr); + return result; auto succs = loco::succs(node); - if (succs.size() != 1) // assume bias is used by only one node - return std::make_pair(nullptr, nullptr); for (auto out : succs) { @@ -238,35 +303,39 @@ std::pair<loco::Node *, loco::Node *> get_input_weight_of_bias(CircleNode *node) { assert(conv->input() != nullptr); assert(conv->filter() != nullptr); - return std::make_pair(conv->input(), conv->filter()); + result.push_back({conv->input(), conv->filter(), conv}); + continue; } auto dw_conv = dynamic_cast<CircleDepthwiseConv2D *>(out); if (dw_conv != nullptr && dw_conv->bias() == circle_const) { assert(dw_conv->input() != nullptr); assert(dw_conv->filter() != nullptr); - return std::make_pair(dw_conv->input(), dw_conv->filter()); + result.push_back({dw_conv->input(), dw_conv->filter(), dw_conv}); + continue; } auto fc = dynamic_cast<CircleFullyConnected *>(out); if (fc != nullptr && fc->bias() == circle_const) { assert(fc->input() != nullptr); assert(fc->weights() != nullptr); - return std::make_pair(fc->input(), fc->weights()); + result.push_back({fc->input(), fc->weights(), fc}); + continue; } auto tconv = dynamic_cast<CircleTransposeConv *>(out); if (tconv != nullptr && tconv->bias() == circle_const) { assert(tconv->outBackprop() != nullptr); assert(tconv->filter() != nullptr); - return std::make_pair(tconv->outBackprop(), tconv->filter()); + result.push_back({tconv->outBackprop(), tconv->filter(), tconv}); + continue; } } - return std::make_pair(nullptr, nullptr); + return result; } -void asym_quant_bias_per_layer(CircleConst *node, float input_scale, float weight_scale, - float *scaling_factor, int64_t *zp) +CircleConst *asym_quant_bias_per_layer(CircleConst *node, float input_scale, float weight_scale, + float *scaling_factor, int64_t *zp) { float scale = input_scale * weight_scale; const float scaling_factor_inv = (scale == 0) ? 0 : 1.0 / scale; @@ -276,24 +345,27 @@ void asym_quant_bias_per_layer(CircleConst *node, float input_scale, float weigh for (uint32_t i = 0; i < size; ++i) { quantized_values[i] = - static_cast<int32_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv)); + static_cast<int32_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv)); } - node->dtype(loco::DataType::S32); // change the type of tensor - node->size<loco::DataType::S32>(size); // resize tensor + auto new_bias = create_empty_const_from<loco::DataType::S32>(node, size); + const int32_t kMinScale = std::numeric_limits<int32_t>::lowest(); const int32_t kMaxScale = std::numeric_limits<int32_t>::max(); for (uint32_t i = 0; i < size; ++i) { - node->at<loco::DataType::S32>(i) = - std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); + new_bias->at<loco::DataType::S32>(i) = + std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); } *scaling_factor = scale; *zp = 0; + + return new_bias; } -void quant_bias_per_channel(CircleConst *node, float input_scale, std::vector<float> &weight_scale, - std::vector<float> &scaling_factor, std::vector<int64_t> &zp) +CircleConst *quant_bias_per_channel(CircleConst *node, float input_scale, + std::vector<float> &weight_scale, + std::vector<float> &scaling_factor, std::vector<int64_t> &zp) { float scaling_factor_inv{0}; @@ -305,24 +377,27 @@ void quant_bias_per_channel(CircleConst *node, float input_scale, std::vector<fl scaling_factor[i] = input_scale * weight_scale[i]; scaling_factor_inv = (scaling_factor[i] == 0) ? 0 : 1.0 / scaling_factor[i]; quantized_values[i] = - static_cast<int32_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv)); + static_cast<int32_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv)); zp[i] = 0; } - node->dtype(loco::DataType::S32); // change the type of tensor - node->size<loco::DataType::S32>(size); // resize tensor + auto new_bias = create_empty_const_from<loco::DataType::S32>(node, size); + const int32_t kMinScale = std::numeric_limits<int32_t>::lowest(); const int32_t kMaxScale = std::numeric_limits<int32_t>::max(); for (uint32_t i = 0; i < size; ++i) { - node->at<loco::DataType::S32>(i) = - std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); + new_bias->at<loco::DataType::S32>(i) = + std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); } + + return new_bias; } -void int16_quant_bias_per_channel(CircleConst *node, float input_scale, - std::vector<float> &weight_scale, - std::vector<float> &scaling_factor, std::vector<int64_t> &zp) +CircleConst *int16_quant_bias_per_channel(CircleConst *node, float input_scale, + std::vector<float> &weight_scale, + std::vector<float> &scaling_factor, + std::vector<int64_t> &zp) { float scaling_factor_inv{0}; @@ -334,16 +409,18 @@ void int16_quant_bias_per_channel(CircleConst *node, float input_scale, scaling_factor[i] = input_scale * weight_scale[i]; scaling_factor_inv = (scaling_factor[i] == 0) ? 0 : 1.0 / scaling_factor[i]; quantized_values[i] = - static_cast<int64_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv)); + static_cast<int64_t>(std::round(node->at<loco::DataType::FLOAT32>(i) * scaling_factor_inv)); zp[i] = 0; } - node->dtype(loco::DataType::S64); // change the type of tensor - node->size<loco::DataType::S64>(size); // resize tensor + auto new_bias = create_empty_const_from<loco::DataType::S64>(node, size); + for (uint32_t i = 0; i < size; ++i) { - node->at<loco::DataType::S64>(i) = quantized_values[i]; + new_bias->at<loco::DataType::S64>(i) = quantized_values[i]; } + + return new_bias; } bool has_min_max(const CircleNode *node) @@ -362,42 +439,22 @@ void sym_wquant_per_channel(CircleConst *node, std::vector<float> &scaling_facto uint32_t size = node->size<loco::DataType::FLOAT32>(); std::vector<int32_t> quantized_values(size); - loco::TensorShape dimension; - dimension.rank(4); - uint32_t indices[4] = { - 0, + auto quantize = [&](uint32_t *indices, loco::TensorShape &dimension, int32_t channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; + auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); + quantized_values[cal_offset(dimension, indices)] = + static_cast<int32_t>(std::round(data * scaling_factor_inv)); }; - if (!get_channel_dim_index(node, dimension, channel_dim_index)) - { - assert(false); - return; - } - - for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) - { - for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) - { - for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) - { - for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) - { - int channel_idx = indices[channel_dim_index]; - const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; - auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); - quantized_values[cal_offset(dimension, indices)] = - static_cast<int32_t>(std::round(data * scaling_factor_inv)); - } - } - } - } + iterate_per_channel(node, channel_dim_index, quantize); node->dtype(loco::DataType::S16); // change the type of tensor node->size<loco::DataType::S16>(size); // resize tensor for (uint32_t i = 0; i < size; ++i) { node->at<loco::DataType::S16>(i) = - std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); + std::min(kMaxScale, std::max(kMinScale, quantized_values[i])); } } @@ -412,35 +469,15 @@ void asym_wquant_per_channel(CircleConst *node, std::vector<float> &min, uint32_t size = node->size<loco::DataType::FLOAT32>(); std::vector<int32_t> quantized_values(size); - loco::TensorShape dimension; - dimension.rank(4); - uint32_t indices[4] = { - 0, + auto quantize = [&](uint32_t *indices, loco::TensorShape &dimension, int32_t channel_dim_index) { + int channel_idx = indices[channel_dim_index]; + const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; + auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); + quantized_values[cal_offset(dimension, indices)] = + static_cast<int32_t>(std::round((data - min[channel_idx]) * scaling_factor_inv)); }; - if (!get_channel_dim_index(node, dimension, channel_dim_index)) - { - assert(false); - return; - } - - for (indices[0] = 0; indices[0] < dimension.dim(0).value(); indices[0]++) - { - for (indices[1] = 0; indices[1] < dimension.dim(1).value(); indices[1]++) - { - for (indices[2] = 0; indices[2] < dimension.dim(2).value(); indices[2]++) - { - for (indices[3] = 0; indices[3] < dimension.dim(3).value(); indices[3]++) - { - int channel_idx = indices[channel_dim_index]; - const float scaling_factor_inv = 1.0 / scaling_factor[channel_idx]; - auto data = node->at<loco::DataType::FLOAT32>(cal_offset(dimension, indices)); - quantized_values[cal_offset(dimension, indices)] = - static_cast<int32_t>(std::round((data - min[channel_idx]) * scaling_factor_inv)); - } - } - } - } + iterate_per_channel(node, channel_dim_index, quantize); node->dtype(loco::DataType::U8); // change the type of tensor node->size<loco::DataType::U8>(size); // resize tensor @@ -473,6 +510,21 @@ void asym_wquant_per_layer(CircleConst *node, float min, float scaling_factor) } } +void set_bias(luci::CircleNode *node, luci::CircleConst *bias) +{ + if (auto conv = dynamic_cast<CircleConv2D *>(node)) + conv->bias(bias); + else if (auto dconv = dynamic_cast<CircleDepthwiseConv2D *>(node)) + dconv->bias(bias); + else if (auto tconv = dynamic_cast<CircleTransposeConv *>(node)) + tconv->bias(bias); + else if (auto fc = dynamic_cast<CircleFullyConnected *>(node)) + fc->bias(bias); + else + throw std::runtime_error("Only convolution, depthwise convolution, transposed convolution, and " + "fully-connected layer have bias"); +} + /** * @brief QuantizeActivation quantizes tensors for activations * @details Quantize using recorded min/max values @@ -480,7 +532,7 @@ void asym_wquant_per_layer(CircleConst *node, float min, float scaling_factor) struct QuantizeActivation final : public luci::CircleNodeMutableVisitor<bool> { QuantizeActivation(loco::DataType input, loco::DataType output) - : input_type(input), output_type(output) + : input_type(input), output_type(output) { } @@ -503,8 +555,12 @@ struct QuantizeActivation final : public luci::CircleNodeMutableVisitor<bool> continue; // Check if this is bias (bias is quantized later) - auto iw = get_input_weight_of_bias(circle_node); - if (iw.first != nullptr && iw.second != nullptr) + auto iwo = get_input_weight_output_of_bias(circle_node); + if (iwo.size() > 0) + continue; + + // Check if this is bool type (bool type is not quantized) + if (circle_node->dtype() == loco::DataType::BOOL) continue; // Check if this is activation @@ -547,7 +603,7 @@ struct QuantizeActivation final : public luci::CircleNodeMutableVisitor<bool> struct QuantizeBias final : public luci::CircleNodeMutableVisitor<bool> { QuantizeBias(loco::DataType input, loco::DataType output, QuantizationGranularity gr) - : input_type(input), output_type(output), granularity(gr) + : input_type(input), output_type(output), granularity(gr) { } @@ -562,65 +618,77 @@ struct QuantizeBias final : public luci::CircleNodeMutableVisitor<bool> if (is_quantized(node)) return false; - // Check if this is bias - auto iw = get_input_weight_of_bias(node); - if (iw.first == nullptr || iw.second == nullptr) - return false; - - auto input = loco::must_cast<luci::CircleNode *>(iw.first); - auto weight = loco::must_cast<luci::CircleNode *>(iw.second); + auto iwo_list = get_input_weight_output_of_bias(node); - if (granularity == QuantizationGranularity::ChannelWise) + for (auto iwo : iwo_list) { - assert(input->quantparam()->scale.size() == 1); // input scale's layer-wise - auto input_scale = input->quantparam()->scale[0]; + assert(iwo.size() == 3); - assert(weight->quantparam() != nullptr); // weight scale's channel-wise - auto weight_scale = weight->quantparam()->scale; + auto input = loco::must_cast<luci::CircleNode *>(iwo[0]); + auto weight = loco::must_cast<luci::CircleNode *>(iwo[1]); + auto output = loco::must_cast<luci::CircleNode *>(iwo[2]); - auto circle_const = loco::must_cast<luci::CircleConst *>(node); + auto const_bias = loco::must_cast<luci::CircleConst *>(node); + assert(const_bias->dtype() == loco::DataType::FLOAT32); - uint32_t size = circle_const->size<loco::DataType::FLOAT32>(); - assert(size == weight_scale.size()); - std::vector<float> scaling_factor(size); - std::vector<int64_t> zp(size); + CircleConst *new_bias = nullptr; - if (output_type == loco::DataType::U8) - { - quant_bias_per_channel(circle_const, input_scale, weight_scale, scaling_factor, zp); - } - else if (output_type == loco::DataType::S16) + if (granularity == QuantizationGranularity::ChannelWise) { - int16_quant_bias_per_channel(circle_const, input_scale, weight_scale, scaling_factor, zp); + assert(input->quantparam()->scale.size() == 1); // input scale's layer-wise + auto input_scale = input->quantparam()->scale[0]; + + assert(weight->quantparam() != nullptr); // weight scale's channel-wise + auto weight_scale = weight->quantparam()->scale; + + uint32_t size = const_bias->size<loco::DataType::FLOAT32>(); + assert(size == weight_scale.size()); + std::vector<float> scaling_factor(size); + std::vector<int64_t> zp(size); + + if (output_type == loco::DataType::U8) + { + new_bias = + quant_bias_per_channel(const_bias, input_scale, weight_scale, scaling_factor, zp); + } + else if (output_type == loco::DataType::S16) + { + new_bias = + int16_quant_bias_per_channel(const_bias, input_scale, weight_scale, scaling_factor, zp); + } + else + { + throw std::runtime_error("Unsupported quantization type."); + } + + auto quantparam = std::make_unique<CircleQuantParam>(); + quantparam->scale = scaling_factor; + quantparam->zerop = zp; + assert(new_bias->quantparam() == nullptr); // bias should not be quantized before + new_bias->quantparam(std::move(quantparam)); + + set_bias(output, new_bias); } else { - throw std::runtime_error("Unsupported quantization type."); - } + assert(input->quantparam()->scale.size() == 1); // Only support per-layer quant + auto input_scale = input->quantparam()->scale[0]; - auto quantparam = std::make_unique<CircleQuantParam>(); - quantparam->scale = scaling_factor; - quantparam->zerop = zp; - assert(circle_const->quantparam() == nullptr); // bias should not be quantized before - circle_const->quantparam(std::move(quantparam)); - } - else - { - assert(input->quantparam()->scale.size() == 1); // Only support per-layer quant - auto input_scale = input->quantparam()->scale[0]; - - assert(weight->quantparam()->scale.size() == 1); // Only support per-layer quant - auto weight_scale = weight->quantparam()->scale[0]; - - auto circle_const = loco::must_cast<luci::CircleConst *>(node); - float scaling_factor{0}; - int64_t zp{0}; - asym_quant_bias_per_layer(circle_const, input_scale, weight_scale, &scaling_factor, &zp); - auto quantparam = std::make_unique<CircleQuantParam>(); - quantparam->scale.push_back(scaling_factor); - quantparam->zerop.push_back(zp); - assert(circle_const->quantparam() == nullptr); // bias should not be quantized before - circle_const->quantparam(std::move(quantparam)); + assert(weight->quantparam()->scale.size() == 1); // Only support per-layer quant + auto weight_scale = weight->quantparam()->scale[0]; + + float scaling_factor{0}; + int64_t zp{0}; + new_bias = + asym_quant_bias_per_layer(const_bias, input_scale, weight_scale, &scaling_factor, &zp); + auto quantparam = std::make_unique<CircleQuantParam>(); + quantparam->scale.push_back(scaling_factor); + quantparam->zerop.push_back(zp); + assert(new_bias->quantparam() == nullptr); // bias should not be quantized before + new_bias->quantparam(std::move(quantparam)); + + set_bias(output, new_bias); + } } return false; } @@ -633,7 +701,7 @@ struct QuantizeBias final : public luci::CircleNodeMutableVisitor<bool> struct QuantizeWeights final : public luci::CircleNodeMutableVisitor<bool> { QuantizeWeights(loco::DataType input, loco::DataType output, QuantizationGranularity gr) - : input_type(input), output_type(output), granularity(gr) + : input_type(input), output_type(output), granularity(gr) { } @@ -641,116 +709,179 @@ struct QuantizeWeights final : public luci::CircleNodeMutableVisitor<bool> loco::DataType output_type; QuantizationGranularity granularity; - // Quantize input tensors of each node - bool visit(luci::CircleNode *node) +private: + void quantize_weights(luci::CircleConst *weights) { - LOGGER(l); - INFO(l) << "QuantizeWeights visit node: " << node->name() << std::endl; - auto arity = node->arity(); - for (uint32_t i = 0; i < arity; i++) + // Find min/max per channel-wise + if (granularity == QuantizationGranularity::ChannelWise) { - auto input_node = node->arg(i); - auto circle_node = loco::must_cast<luci::CircleNode *>(input_node); + auto quantparam = weights->quantparam(); + if (quantparam == nullptr) + { + assert(false && "quantparam is nullptr"); + return; + } - // Check if this is already quantized - if (is_quantized(circle_node)) - continue; + auto min = quantparam->min; + auto scaling_factor = quantparam->scale; + int32_t channel_dim_index = 0; - if (is_weights(circle_node)) + if (output_type == loco::DataType::U8) { - auto circle_const = loco::must_cast<luci::CircleConst *>(circle_node); - - // Find min/max per channel-wise - if (granularity == QuantizationGranularity::ChannelWise) - { - auto quantparam = circle_node->quantparam(); - if (quantparam == nullptr) - { - assert(false && "quantparam is nullptr"); - return false; - } - - auto min = quantparam->min; - auto scaling_factor = quantparam->scale; - int32_t channel_dim_index = 0; - - if (output_type == loco::DataType::U8) - { - asym_wquant_per_channel(circle_const, min, scaling_factor, channel_dim_index); - } - else - { - sym_wquant_per_channel(circle_const, scaling_factor, channel_dim_index); - } - quantparam->min.clear(); - quantparam->max.clear(); - quantparam->quantized_dimension = channel_dim_index; - } - // Find min/max per layer-wise - else - { - // Quantize using recorded quantparam - auto quantparam = circle_node->quantparam(); - assert(quantparam != nullptr); - assert(quantparam->min.size() == 1); // only support layer-wise quant - assert(quantparam->scale.size() == 1); // only support layer-wise quant - auto min = quantparam->min[0]; - auto scaling_factor = quantparam->scale[0]; - asym_wquant_per_layer(circle_const, min, scaling_factor); - quantparam->min.clear(); - quantparam->max.clear(); - } + asym_wquant_per_channel(weights, min, scaling_factor, channel_dim_index); + } + else + { + sym_wquant_per_channel(weights, scaling_factor, channel_dim_index); } + quantparam->min.clear(); + quantparam->max.clear(); + quantparam->quantized_dimension = channel_dim_index; + } + // Find min/max per layer-wise + else + { + // Quantize using recorded quantparam + auto quantparam = weights->quantparam(); + assert(quantparam != nullptr); + assert(quantparam->min.size() == 1); // only support layer-wise quant + assert(quantparam->scale.size() == 1); // only support layer-wise quant + auto min = quantparam->min[0]; + auto scaling_factor = quantparam->scale[0]; + asym_wquant_per_layer(weights, min, scaling_factor); + quantparam->min.clear(); + quantparam->max.clear(); } - return false; } -}; -void quant_instnorm(luci::CircleInstanceNorm *node, loco::DataType output_type, - QuantizationGranularity granularity) -{ - auto gamma = loco::must_cast<luci::CircleConst *>(node->gamma()); - auto beta = loco::must_cast<luci::CircleConst *>(node->beta()); - assert(gamma->dtype() == loco::DataType::FLOAT32); - assert(beta->dtype() == loco::DataType::FLOAT32); + bool visit(luci::CircleConv2D *node) + { + LOGGER(l); + INFO(l) << "QuantizeWeights visit node: " << node->name() << std::endl; - if (granularity == QuantizationGranularity::LayerWise) + auto weights = loco::must_cast<luci::CircleConst *>(node->filter()); + if (!is_quantized(weights)) + { + auto new_weights = luci::clone(weights); + node->filter(new_weights); + quantize_weights(new_weights); + return true; + } + return false; + } + + bool visit(luci::CircleDepthwiseConv2D *node) { - quant_const(gamma, output_type); - quant_const(beta, output_type); + LOGGER(l); + INFO(l) << "QuantizeWeights visit node: " << node->name() << std::endl; + + auto weights = loco::must_cast<luci::CircleConst *>(node->filter()); + if (!is_quantized(weights)) + { + auto new_weights = luci::clone(weights); + node->filter(new_weights); + quantize_weights(new_weights); + return true; + } + return false; } - else if (granularity == QuantizationGranularity::ChannelWise) + + bool visit(luci::CircleInstanceNorm *node) { - quant_const_per_channel(gamma, output_type); - quant_const_per_channel(beta, output_type); + LOGGER(l); + INFO(l) << "QuantizeWeights visit node: " << node->name() << std::endl; + + auto gamma = loco::must_cast<luci::CircleConst *>(node->gamma()); + auto beta = loco::must_cast<luci::CircleConst *>(node->beta()); + + bool changed = false; + if (!is_quantized(gamma)) + { + assert(gamma->dtype() == loco::DataType::FLOAT32); + auto new_gamma = luci::clone(gamma); + if (granularity == QuantizationGranularity::LayerWise) + quant_const(new_gamma, output_type); + else if (granularity == QuantizationGranularity::ChannelWise) + quant_const_per_channel(new_gamma, output_type); + node->gamma(new_gamma); + changed = true; + } + if (!is_quantized(beta)) + { + assert(beta->dtype() == loco::DataType::FLOAT32); + auto new_beta = luci::clone(beta); + if (granularity == QuantizationGranularity::LayerWise) + quant_const(new_beta, output_type); + else if (granularity == QuantizationGranularity::ChannelWise) + quant_const_per_channel(new_beta, output_type); + node->beta(new_beta); + changed = true; + } + + return changed; } - else - throw std::runtime_error("Quantization granularity must be either 'layer' or 'channel'"); -} -void quant_prelu(luci::CirclePRelu *node, loco::DataType output_type, - QuantizationGranularity granularity) -{ - auto alpha = loco::must_cast<luci::CircleConst *>(node->alpha()); - assert(alpha->dtype() == loco::DataType::FLOAT32); + bool visit(luci::CirclePRelu *node) + { + LOGGER(l); + INFO(l) << "QuantizeWeights visit node: " << node->name() << std::endl; + + auto alpha = loco::must_cast<luci::CircleConst *>(node->alpha()); + + if (!is_quantized(alpha)) + { + assert(alpha->dtype() == loco::DataType::FLOAT32); + auto new_alpha = luci::clone(alpha); + if (granularity == QuantizationGranularity::LayerWise) + quant_const(new_alpha, output_type); + else if (granularity == QuantizationGranularity::ChannelWise) + quant_const_per_channel(new_alpha, output_type); + node->alpha(new_alpha); + return true; + } - if (granularity == QuantizationGranularity::LayerWise) + return false; + } + + bool visit(luci::CircleTransposeConv *node) { - quant_const(alpha, output_type); + LOGGER(l); + INFO(l) << "QuantizeWeights visit node: " << node->name() << std::endl; + + auto weights = loco::must_cast<luci::CircleConst *>(node->filter()); + if (!is_quantized(weights)) + { + auto new_weights = luci::clone(weights); + node->filter(new_weights); + quantize_weights(new_weights); + return true; + } + return false; } - else if (granularity == QuantizationGranularity::ChannelWise) + + bool visit(luci::CircleFullyConnected *node) { - quant_const_per_channel(alpha, output_type); + LOGGER(l); + INFO(l) << "QuantizeWeights visit node: " << node->name() << std::endl; + + auto weights = loco::must_cast<luci::CircleConst *>(node->weights()); + if (!is_quantized(weights)) + { + auto new_weights = luci::clone(weights); + node->weights(new_weights); + quantize_weights(new_weights); + return true; + } + return false; } - else - throw std::runtime_error("Quantization granularity must be either 'layer' or 'channel'"); -} + + bool visit(luci::CircleNode *) { return false; } +}; /** * @brief Quantize const input tensors using min/max of const values */ -void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type, - QuantizationGranularity granularity) +void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type) { auto opcode = node->opcode(); auto arity = node->arity(); @@ -763,6 +894,8 @@ void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type, case luci::CircleOpcode::CONV_2D: case luci::CircleOpcode::DEPTHWISE_CONV_2D: case luci::CircleOpcode::FULLY_CONNECTED: + case luci::CircleOpcode::INSTANCE_NORM: + case luci::CircleOpcode::PRELU: case luci::CircleOpcode::TRANSPOSE_CONV: // Handled in QuantizeWeights and QuantizeBias break; @@ -771,8 +904,13 @@ void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type, // Handled in propagate_concat_quantparam break; + case luci::CircleOpcode::LOGICAL_OR: + // Inputs of logical Ops are bool, thus not quantized + break; + case luci::CircleOpcode::ARG_MAX: case luci::CircleOpcode::ARG_MIN: + case luci::CircleOpcode::BATCH_TO_SPACE_ND: case luci::CircleOpcode::MEAN: case luci::CircleOpcode::PAD: case luci::CircleOpcode::REDUCE_ANY: @@ -783,6 +921,9 @@ void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type, case luci::CircleOpcode::RESIZE_BILINEAR: case luci::CircleOpcode::RESIZE_NEAREST_NEIGHBOR: case luci::CircleOpcode::REVERSE_SEQUENCE: + case luci::CircleOpcode::SLICE: + case luci::CircleOpcode::SPACE_TO_BATCH_ND: + case luci::CircleOpcode::STRIDED_SLICE: case luci::CircleOpcode::SUM: case luci::CircleOpcode::TILE: case luci::CircleOpcode::TOPK_V2: @@ -791,41 +932,53 @@ void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type, // Ex: axis, paddings input_node = node->arg(0); const_node = dynamic_cast<luci::CircleConst *>(input_node); - if (const_node != nullptr) + if (const_node != nullptr && !is_quantized(const_node)) quant_const(const_node, output_type); break; - case luci::CircleOpcode::INSTANCE_NORM: - quant_instnorm(loco::must_cast<luci::CircleInstanceNorm *>(node), output_type, granularity); - break; - - case luci::CircleOpcode::PRELU: - quant_prelu(loco::must_cast<luci::CirclePRelu *>(node), output_type, granularity); - break; - case luci::CircleOpcode::ADD: case luci::CircleOpcode::ADD_N: + case luci::CircleOpcode::DEPTH_TO_SPACE: case luci::CircleOpcode::DIV: + case luci::CircleOpcode::ELU: case luci::CircleOpcode::EQUAL: + case luci::CircleOpcode::FLOOR: + case luci::CircleOpcode::FLOOR_DIV: case luci::CircleOpcode::GREATER: case luci::CircleOpcode::GREATER_EQUAL: case luci::CircleOpcode::LESS: case luci::CircleOpcode::LESS_EQUAL: + case luci::CircleOpcode::LOGISTIC: case luci::CircleOpcode::MAXIMUM: case luci::CircleOpcode::MINIMUM: case luci::CircleOpcode::MUL: case luci::CircleOpcode::NOT_EQUAL: + case luci::CircleOpcode::POW: + case luci::CircleOpcode::RSQRT: + case luci::CircleOpcode::SOFTMAX: + case luci::CircleOpcode::SPACE_TO_DEPTH: + case luci::CircleOpcode::SQRT: case luci::CircleOpcode::SUB: + case luci::CircleOpcode::TANH: // Quantize all const inputs using their values for (uint32_t i = 0; i < arity; i++) { input_node = node->arg(i); const_node = dynamic_cast<luci::CircleConst *>(input_node); - if (const_node != nullptr) + if (const_node != nullptr && !is_quantized(const_node)) quant_const(const_node, output_type); } break; + case luci::CircleOpcode::SPLIT: + // Only the second input is quantized + // First input should not be quantized (e.g., split_dim) + input_node = node->arg(1); + const_node = dynamic_cast<luci::CircleConst *>(input_node); + if (const_node != nullptr && !is_quantized(const_node)) + quant_const(const_node, output_type); + break; + default: for (uint32_t i = 0; i < arity; i++) { @@ -850,8 +1003,8 @@ void quantize_const_inputs(luci::CircleNode *node, loco::DataType output_type, * (U8 qparam2) * * AFTER - * [CircleNode] [CircleConst] - * (U8 qparam2) (U8 qparam2) + * [CircleNode] [CircleConst] [CircleConst] <- Dead node + * (U8 qparam2) (U8 qparam2) (FP32) * \ / * \ / * [CircleConcatenation] @@ -871,7 +1024,11 @@ void propagate_concat_quantparam(luci::CircleConcatenation *concat, loco::DataTy auto node = concat->arg(i); auto const_node = dynamic_cast<luci::CircleConst *>(node); if (const_node != nullptr) - quant_const(const_node, quant_type); + { + auto new_const = luci::clone(const_node); + quant_const(new_const, quant_type); + concat->values(i, new_const); + } } return; } @@ -884,20 +1041,6 @@ void propagate_concat_quantparam(luci::CircleConcatenation *concat, loco::DataTy if (node->opcode() == luci::CircleOpcode::CONCATENATION) continue; - // Skip if this input is used by other Ops - auto succs = loco::succs(node); - if (succs.size() != 1) - { - if (node->opcode() == luci::CircleOpcode::CIRCLECONST) - { - luci::CircleConst *const_node = loco::must_cast<luci::CircleConst *>(node); - quant_const(const_node, quant_type); - } - continue; - } - - assert(succs.find(concat) != succs.end()); - // Quantize constant values if (node->opcode() == luci::CircleOpcode::CIRCLECONST) { @@ -913,15 +1056,21 @@ void propagate_concat_quantparam(luci::CircleConcatenation *concat, loco::DataTy const auto scaling_factor = concat_qparam->scale[0]; const auto zerop = concat_qparam->zerop[0]; - quant_const_values(const_node, scaling_factor, zerop, quant_type); + auto new_const = luci::clone(const_node); + quant_const_values(new_const, scaling_factor, zerop, quant_type); + concat->values(i, new_const); + overwrite_quantparam(concat, new_const); } else { + const auto succs = loco::succs(node); + if (succs.size() > 1) + continue; + // Non-const input must have been quantized assert(node->quantparam() != nullptr); + overwrite_quantparam(concat, node); } - - overwrite_quantparam(concat, node); } } @@ -954,13 +1103,6 @@ bool QuantizeWithMinMaxPass::run(loco::Graph *g) circle_node->accept(&qb); } - // Quantize const inputs other than weights and bias - for (auto node : loco::active_nodes(loco::output_nodes(g))) - { - auto circle_node = loco::must_cast<luci::CircleNode *>(node); - quantize_const_inputs(circle_node, _output_dtype, _granularity); - } - // Propagate quantization parameters of concat Op for (auto node : loco::active_nodes(loco::output_nodes(g))) { @@ -976,6 +1118,13 @@ bool QuantizeWithMinMaxPass::run(loco::Graph *g) propagate_concat_quantparam(concat, _output_dtype); } + // Quantize const inputs other than weights and bias + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + quantize_const_inputs(circle_node, _output_dtype); + } + // Update output dtype auto graph_outputs = g->outputs(); for (auto node : loco::output_nodes(g)) diff --git a/compiler/luci/pass/src/QuantizeWithMinMaxPass.test.cpp b/compiler/luci/pass/src/QuantizeWithMinMaxPass.test.cpp new file mode 100644 index 000000000..75ec0cfd8 --- /dev/null +++ b/compiler/luci/pass/src/QuantizeWithMinMaxPass.test.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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/Pass/QuantizeWithMinMaxPass.h" + +#include <gtest/gtest.h> + +TEST(QuantizeWithMinMaxPassTest, name) +{ + luci::QuantizeWithMinMaxPass pass(loco::DataType::FLOAT32, loco::DataType::U8, + luci::QuantizationGranularity::LayerWise); + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/QuantizedModelVerifier.cpp b/compiler/luci/pass/src/QuantizedModelVerifier.cpp new file mode 100644 index 000000000..5ea803cc9 --- /dev/null +++ b/compiler/luci/pass/src/QuantizedModelVerifier.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 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 "QuantizedModelVerifier.h" + +#include "VerifyQuantizedNodeLayerWiseGranularity.h" +#include "VerifyQuantizedNodeChannelWiseGranularity.h" +#include "VerifyQuantizedNodeU8Type.h" +#include "VerifyQuantizedNodeS16Type.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> + +namespace luci +{ + +void QuantizedModelVerifier::verify(loco::Graph *g) +{ + if (_quantized_dtype != Type::U8 && _quantized_dtype != Type::S16) + throw std::runtime_error("Unsupported quantized dtype"); + + if (_granularity != Granularity::ChannelWise && _granularity != Granularity::LayerWise) + throw std::runtime_error("Unsupported granularity"); + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + + // Verify Type + if (_quantized_dtype == Type::U8) + { + VerifyQuantizedNodeU8Type vt; + if (!circle_node->accept(&vt)) + throw std::runtime_error("Wrong data type"); + } + else if (_quantized_dtype == Type::S16) + { + VerifyQuantizedNodeS16Type vt; + if (!circle_node->accept(&vt)) + throw std::runtime_error("Wrong data type"); + } + + // Verify Granularity + if (_granularity == Granularity::LayerWise) + { + VerifyQuantizedNodeLayerWiseGranularity vg; + if (!circle_node->accept(&vg)) + throw std::runtime_error("Wrong granularity"); + } + else if (_granularity == Granularity::ChannelWise) + { + VerifyQuantizedNodeChannelWiseGranularity vg; + if (!circle_node->accept(&vg)) + throw std::runtime_error("Wrong granularity"); + } + } +} + +} // namespace luci diff --git a/compiler/luci/pass/src/QuantizedModelVerifier.h b/compiler/luci/pass/src/QuantizedModelVerifier.h new file mode 100644 index 000000000..d5fbb8e74 --- /dev/null +++ b/compiler/luci/pass/src/QuantizedModelVerifier.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 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 __LUCI_QUANTIZED_MODEL_VERIFIER_H__ +#define __LUCI_QUANTIZED_MODEL_VERIFIER_H__ + +#include "luci/Pass/QuantizationParameters.h" + +#include <loco.h> + +namespace luci +{ + +/** + * @brief Class to verify quantized model + * + * TODO Move this to luci/service + */ +struct QuantizedModelVerifier +{ + +public: + QuantizedModelVerifier(loco::DataType quantized_dtype, QuantizationGranularity granularity) + : _quantized_dtype(quantized_dtype), _granularity(granularity) + { + } + + void verify(loco::Graph *g); + +private: + loco::DataType _quantized_dtype; + QuantizationGranularity _granularity; +}; + +} // namespace luci + +#endif // __LUCI_QUANTIZED_MODEL_VERIFIER_H__ diff --git a/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp b/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp new file mode 100644 index 000000000..eae1b0c1f --- /dev/null +++ b/compiler/luci/pass/src/QuantizedModelVerifier.test.cpp @@ -0,0 +1,1668 @@ +/* + * Copyright (c) 2021 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 "QuantizedModelVerifier.h" + +#include "luci/Pass/QuantizeWithMinMaxPass.h" + +#include <luci/test/TestIOGraph.h> + +#include <gtest/gtest.h> + +using Type = loco::DataType; +using Granularity = luci::QuantizationGranularity; + +namespace +{ + +/** + * @brief A helper function to create dummy const node + */ +template <Type T> luci::CircleConst *create_dummy_const(loco::Graph *g, luci::test::ShapeU32 shape) +{ + auto node = g->nodes()->create<luci::CircleConst>(); + { + node->dtype(T); + node->shape(shape); + node->size<T>(luci::test::num_elements(shape)); + + for (int32_t i = 0; i < luci::test::num_elements(shape); i++) + { + // DESIGN NOTE + // + // Filling with any random numbers are fine + // Q. Should it include minus numbers? + switch (T) + { + case Type::FLOAT32: + // Fill with index + node->at<T>(i) = static_cast<float>(i); + break; + case Type::BOOL: + // Fill by flip + node->at<T>(i) = (i % 2) ? true : false; + break; + case Type::U8: + // Fill with index + node->at<T>(i) = static_cast<uint8_t>(i); + break; + case Type::S16: + // Fill with index + node->at<T>(i) = static_cast<int16_t>(i); + break; + } + } + } + + return node; +} + +/** + * @brief A helper function to create const node with value + */ +template <Type DT, typename T> +luci::CircleConst *create_const(loco::Graph *g, luci::test::ShapeU32 shape, + std::initializer_list<T> values) +{ + auto node = g->nodes()->create<luci::CircleConst>(); + { + node->dtype(DT); + node->shape(shape); + node->size<DT>(luci::test::num_elements(shape)); + + assert(values.size() == node->size<DT>()); + + uint32_t index = 0; + for (auto val : values) + { + node->at<DT>(index++) = static_cast<T>(val); + } + } + + return node; +} + +void insert_scale_zp(luci::CircleNode *node, float scale, int64_t zp) +{ + auto qparam = node->quantparam(); + assert(qparam != nullptr); // FIX_CALLER_UNLESS + qparam->scale.push_back(scale); + qparam->zerop.push_back(zp); +} + +void quantize_and_verify(loco::Graph *g, Type quantized_dtype, Granularity granularity) +{ + luci::QuantizeWithMinMaxPass pass(Type::FLOAT32, quantized_dtype, granularity); + pass.run(g); + + luci::QuantizedModelVerifier verifier(quantized_dtype, granularity); + verifier.verify(g); +} + +// Helper function to reduce duplicate test codes +// Assumption: g->output()->from() is the target node +void quantize_and_verify_with_wrong_type(luci::test::TestIOGraph *g, Type quantized_dtype, + Granularity granularity, Type wrong_dtype) +{ + luci::QuantizeWithMinMaxPass pass(Type::FLOAT32, quantized_dtype, granularity); + pass.run(g->g()); + + auto node = loco::must_cast<luci::CircleNode *>(g->output()->from()); + node->dtype(wrong_dtype); + + luci::QuantizedModelVerifier verifier(quantized_dtype, granularity); + verifier.verify(g->g()); +} + +// Helper function to reduce duplicate test codes +// Assumption: g->output()->from() is the target node +void quantize_and_verify_with_wrong_granularity(luci::test::TestIOGraph *g, Type quantized_dtype, + Granularity granularity) +{ + luci::QuantizeWithMinMaxPass pass(Type::FLOAT32, quantized_dtype, granularity); + pass.run(g->g()); + + auto node = loco::must_cast<luci::CircleNode *>(g->output()->from()); + insert_scale_zp(node, 1.0, 1); + + luci::QuantizedModelVerifier verifier(quantized_dtype, granularity); + verifier.verify(g->g()); +} + +// Helper function to reduce duplicate test codes +void quantize_and_verify_with_wrong_granularity(luci::test::TestIOGraph *g, Type quantized_dtype, + Granularity granularity, luci::CircleNode *target) +{ + luci::QuantizeWithMinMaxPass pass(Type::FLOAT32, quantized_dtype, granularity); + pass.run(g->g()); + + insert_scale_zp(target, 1.0, 1); + + luci::QuantizedModelVerifier verifier(quantized_dtype, granularity); + verifier.verify(g->g()); +} + +// Set min/max for all non-const nodes in the graph +void set_minmax_to_non_const(loco::Graph *g, float min, float max) +{ + for (auto node : loco::all_nodes(g)) + { + auto const_node = dynamic_cast<luci::CircleConst *>(node); + if (const_node != nullptr) + continue; + + // Min/Max is not recorded for ArgMax + // See MinMaxObserver.cpp in record_minmax module + auto argmax_node = dynamic_cast<luci::CircleArgMax *>(node); + if (argmax_node != nullptr) + continue; + + // Min/Max is not recorded for Split + // See MinMaxObserver.cpp in record_minmax module + auto split_node = dynamic_cast<luci::CircleSplit *>(node); + if (split_node != nullptr) + continue; + + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + auto qparam = std::make_unique<luci::CircleQuantParam>(); + { + qparam->min.emplace_back(min); + qparam->max.emplace_back(max); + } + circle_node->quantparam(std::move(qparam)); + } +} + +/** + * @brief Simple Test Graph + * @note + * The simple test graph's nodes are initialized with + * simple shapes and values. + */ +class SimpleTestGraph : public luci::test::TestIOGraph +{ +public: + virtual void init(void) = 0; +}; + +class InstanceNormTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _gamma = create_dummy_const<Type::FLOAT32>(g(), {32}); + _beta = create_dummy_const<Type::FLOAT32>(g(), {32}); + _instnorm = g()->nodes()->create<luci::CircleInstanceNorm>(); + { + _instnorm->input(input()); + _instnorm->gamma(_gamma); + _instnorm->beta(_beta); + } + output()->from(_instnorm); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + loco::Node *gamma(void) const { return _instnorm->gamma(); } + loco::Node *beta(void) const { return _instnorm->beta(); } + +public: + luci::CircleInstanceNorm *_instnorm = nullptr; + luci::CircleConst *_input = nullptr; + luci::CircleConst *_gamma = nullptr; + luci::CircleConst *_beta = nullptr; +}; + +class LogisticTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _logistic = g()->nodes()->create<luci::CircleLogistic>(); + { + _logistic->x(input()); + } + output()->from(_logistic); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleLogistic *_logistic = nullptr; +}; + +class SoftmaxTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _softmax = g()->nodes()->create<luci::CircleSoftmax>(); + { + _softmax->logits(input()); + _softmax->beta(0.1); + } + output()->from(_softmax); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleSoftmax *_softmax = nullptr; +}; + +class SpaceToBatchNDTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({1, 2, 2, 1}, {4, 1, 1, 1}); + _block_shape = create_dummy_const<Type::S32>(g(), {2}); + for (uint32_t i = 0; i < 2; i++) + _block_shape->at<Type::S32>(i) = 2; + + _paddings = create_dummy_const<Type::S32>(g(), {2, 2}); + for (uint32_t i = 0; i < 4; i++) + _paddings->at<Type::S32>(i) = 0; + + _stob = g()->nodes()->create<luci::CircleSpaceToBatchND>(); + { + _stob->input(input()); + _stob->block_shape(_block_shape); + _stob->paddings(_paddings); + } + output()->from(_stob); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleSpaceToBatchND *_stob = nullptr; + luci::CircleConst *_block_shape = nullptr; + luci::CircleConst *_paddings = nullptr; +}; + +class SpaceToDepthTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({1, 2, 2, 1}, {1, 1, 1, 4}); + _stod = g()->nodes()->create<luci::CircleSpaceToDepth>(); + { + _stod->input(input()); + _stod->block_size(2); + } + output()->from(_stod); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleSpaceToDepth *_stod = nullptr; +}; + +template <Type indexT> class SliceTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _begin = g()->nodes()->create<luci::CircleConst>(); + { + _begin->dtype(indexT); + } + _size = g()->nodes()->create<luci::CircleConst>(); + { + _size->dtype(indexT); + } + _slice = g()->nodes()->create<luci::CircleSlice>(); + { + _slice->input(input()); + _slice->begin(_begin); + _slice->size(_size); + } + output()->from(_slice); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleSlice *_slice = nullptr; + luci::CircleConst *_begin = nullptr; + luci::CircleConst *_size = nullptr; +}; + +class SplitTestGraph final : public luci::test::TestIOGraph +{ +public: + void init(void) + { + TestIOGraph::init({1, 32}, {32}); + _split_dim = create_dummy_const<Type::S32>(g(), {1}); + _split = g()->nodes()->create<luci::CircleSplit>(); + { + _split->input(input()); + _split->split_dim(_split_dim); + } + _split_o1 = g()->nodes()->create<luci::CircleSplitOut>(); + { + _split_o1->input(_split); + _split_o1->index(0); + } + + output()->from(_split_o1); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleSplit *_split = nullptr; + luci::CircleSplitOut *_split_o1 = nullptr; + luci::CircleConst *_split_dim = nullptr; +}; + +class StridedSliceTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _begin = g()->nodes()->create<luci::CircleConst>(); + { + _begin->dtype(Type::S32); + } + _end = g()->nodes()->create<luci::CircleConst>(); + { + _end->dtype(Type::S32); + } + _strides = g()->nodes()->create<luci::CircleConst>(); + { + _strides->dtype(Type::S32); + } + _slice = g()->nodes()->create<luci::CircleStridedSlice>(); + { + _slice->input(input()); + _slice->begin(_begin); + _slice->end(_end); + _slice->strides(_strides); + } + output()->from(_slice); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleStridedSlice *_slice = nullptr; + luci::CircleConst *_begin = nullptr; + luci::CircleConst *_end = nullptr; + luci::CircleConst *_strides = nullptr; +}; + +class ReshapeTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _shape = g()->nodes()->create<luci::CircleConst>(); + { + _shape->dtype(Type::S32); + } + _reshape = g()->nodes()->create<luci::CircleReshape>(); + { + _reshape->tensor(input()); + _reshape->shape(_shape); + } + output()->from(_reshape); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleReshape *_reshape = nullptr; + luci::CircleConst *_shape = nullptr; +}; + +class TanhTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _tanh = g()->nodes()->create<luci::CircleTanh>(); + { + _tanh->x(input()); + } + output()->from(_tanh); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleTanh *_tanh = nullptr; +}; + +class FloorTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _floor = g()->nodes()->create<luci::CircleFloor>(); + { + _floor->x(input()); + } + output()->from(_floor); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleFloor *_floor = nullptr; +}; + +template <Type indexT> class ArgMaxTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {1}); + // output dtype is float by default, but ArgMax should have indexType (s32/s64) + output()->dtype(indexT); + _dimension = g()->nodes()->create<luci::CircleConst>(); + { + _dimension->dtype(indexT); + } + _argmax = g()->nodes()->create<luci::CircleArgMax>(); + { + _argmax->input(input()); + _argmax->dimension(_dimension); + _argmax->output_type(indexT); + _argmax->dtype(indexT); + } + output()->from(_argmax); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleArgMax *_argmax = nullptr; + luci::CircleConst *_dimension = nullptr; +}; + +class BatchToSpaceNDTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _block_shape = g()->nodes()->create<luci::CircleConst>(); + { + _block_shape->dtype(Type::S32); + } + _crops = g()->nodes()->create<luci::CircleConst>(); + { + _crops->dtype(Type::S32); + } + _btos = g()->nodes()->create<luci::CircleBatchToSpaceND>(); + { + _btos->input(input()); + _btos->block_shape(_block_shape); + _btos->crops(_crops); + } + output()->from(_btos); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleBatchToSpaceND *_btos = nullptr; + luci::CircleConst *_block_shape = nullptr; + luci::CircleConst *_crops = nullptr; +}; + +class DepthToSpaceTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({1, 1, 1, 4}, {1, 2, 2, 1}); + _dtos = g()->nodes()->create<luci::CircleDepthToSpace>(); + { + _dtos->input(input()); + _dtos->block_size(2); + } + output()->from(_dtos); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleDepthToSpace *_dtos = nullptr; +}; + +class PadTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _paddings = g()->nodes()->create<luci::CircleConst>(); + { + _paddings->dtype(Type::S32); + } + _pad = g()->nodes()->create<luci::CirclePad>(); + { + _pad->input(input()); + _pad->paddings(_paddings); + } + output()->from(_pad); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CirclePad *_pad = nullptr; + luci::CircleConst *_paddings = nullptr; +}; + +class TransposeTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _perm = g()->nodes()->create<luci::CircleConst>(); + { + _perm->dtype(Type::S32); + } + _transpose = g()->nodes()->create<luci::CircleTranspose>(); + { + _transpose->a(input()); + _transpose->perm(_perm); + } + output()->from(_transpose); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleTranspose *_transpose = nullptr; + luci::CircleConst *_perm = nullptr; +}; + +class ConcatenationTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({16}, {32}); + _param = create_dummy_const<Type::FLOAT32>(g(), {16}); + _concat = g()->nodes()->create<luci::CircleConcatenation>(2); + { + _concat->values(0, input()); + _concat->values(1, _param); + _concat->axis(0); + } + output()->from(_concat); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleConcatenation *_concat = nullptr; + luci::CircleConst *_param = nullptr; +}; + +// Test graph for comparison Ops +// GREATER, GREATER_EQUAL, LESS, LESS_EQUAL, EQUAL, NOT_EQUAL +template <class Op> class ComparisonOpTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + output()->dtype(loco::DataType::BOOL); + _y = create_dummy_const<Type::FLOAT32>(g(), {32}); + _op = g()->nodes()->create<Op>(); + { + _op->x(input()); + _op->y(_y); + _op->dtype(loco::DataType::BOOL); + } + output()->from(_op); + + set_minmax_to_non_const(g(), -1, 1); + } + + loco::Node *x(void) const { return _op->x(); } + loco::Node *y(void) const { return _op->y(); } + +public: + Op *_op = nullptr; + luci::CircleConst *_y = nullptr; +}; + +// Test graph for binary logical Ops +// LOGICAL_OR, LOGICAL_AND +template <class Op> class BinaryLogicalOpTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + input()->dtype(loco::DataType::BOOL); + output()->dtype(loco::DataType::BOOL); + _y = create_dummy_const<Type::BOOL>(g(), {32}); + _op = g()->nodes()->create<Op>(); + { + _op->x(input()); + _op->y(_y); + _op->dtype(loco::DataType::BOOL); + } + output()->from(_op); + + set_minmax_to_non_const(g(), -1, 1); + } + + loco::Node *x(void) const { return _op->x(); } + loco::Node *y(void) const { return _op->y(); } + +public: + Op *_op = nullptr; + luci::CircleConst *_y = nullptr; +}; + +class DivTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + + _const = create_dummy_const<Type::FLOAT32>(g(), {32}); + _div = g()->nodes()->create<luci::CircleDiv>(); + { + _div->x(input()); + _div->y(_const); + } + output()->from(_div); + + set_minmax_to_non_const(g(), -1, 1); + } + + loco::Node *x() { return _div->x(); } + + loco::Node *y() { return _div->y(); } + +private: + luci::CircleDiv *_div = nullptr; + luci::CircleConst *_const = nullptr; +}; + +class FloorDivTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + + _const = create_dummy_const<Type::FLOAT32>(g(), {32}); + _floor_div = g()->nodes()->create<luci::CircleFloorDiv>(); + { + _floor_div->x(input()); + _floor_div->y(_const); + } + output()->from(_floor_div); + + set_minmax_to_non_const(g(), -1, 1); + } + + loco::Node *x() { return _floor_div->x(); } + + loco::Node *y() { return _floor_div->y(); } + +private: + luci::CircleFloorDiv *_floor_div = nullptr; + luci::CircleConst *_const = nullptr; +}; + +class RsqrtTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _rsqrt = g()->nodes()->create<luci::CircleRsqrt>(); + { + _rsqrt->x(input()); + } + output()->from(_rsqrt); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleRsqrt *_rsqrt = nullptr; +}; + +class SqrtTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _sqrt = g()->nodes()->create<luci::CircleSqrt>(); + { + _sqrt->x(input()); + } + output()->from(_sqrt); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class EluTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + _elu = g()->nodes()->create<luci::CircleElu>(); + { + _elu->features(input()); + } + output()->from(_elu); + + set_minmax_to_non_const(g(), -1, 1); + } + +public: + luci::CircleElu *_elu = nullptr; +}; + +class PowTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({32}, {32}); + + _const = create_dummy_const<Type::FLOAT32>(g(), {32}); + _pow = g()->nodes()->create<luci::CirclePow>(); + { + _pow->x(input()); + _pow->y(_const); + } + output()->from(_pow); + + set_minmax_to_non_const(g(), -1, 1); + } + + loco::Node *x() { return _pow->x(); } + + loco::Node *y() { return _pow->y(); } + +private: + luci::CirclePow *_pow = nullptr; + luci::CircleConst *_const = nullptr; +}; + +class ResizeBilinearTestGraph final : public SimpleTestGraph +{ +public: + void init(void) override + { + TestIOGraph::init({1, 4, 4, 1}, {1, 8, 8, 1}); + + _size = create_const<Type::S32, int32_t>(g(), {2}, {8, 8}); + _resize_bilinear = g()->nodes()->create<luci::CircleResizeBilinear>(); + { + _resize_bilinear->input(input()); + _resize_bilinear->size(_size); + } + output()->from(_resize_bilinear); + + set_minmax_to_non_const(g(), -1, 1); + } + +private: + luci::CircleResizeBilinear *_resize_bilinear = nullptr; + luci::CircleConst *_size = nullptr; +}; + +} // namespace + +// Quantize and verify with given configurations +#define TEST_WITH_GRAPH(graph, type, granularity) \ + do \ + { \ + graph g; \ + g.init(); \ + EXPECT_NO_THROW(quantize_and_verify(g.g(), type, granularity)); \ + } while (0) + +// Quantize and verify with wrong type +#define TEST_WITH_WRONG_TYPE(graph, type, granularity, wrong_dtype) \ + do \ + { \ + graph g; \ + g.init(); \ + EXPECT_ANY_THROW(quantize_and_verify_with_wrong_type(&g, type, granularity, wrong_dtype)); \ + } while (0) + +// Quantize and verify with wrong granularity +#define TEST_WITH_WRONG_GRANULARITY(graph, type, granularity) \ + do \ + { \ + graph g; \ + g.init(); \ + EXPECT_ANY_THROW(quantize_and_verify_with_wrong_granularity(&g, type, granularity)); \ + } while (0) + +// Quantize and verify with wrong granularity +// Users can specify the test target +#define TEST_WITH_WRONG_GRANULARITY_TARGET(graph, type, granularity, target) \ + do \ + { \ + graph g; \ + g.init(); \ + auto node = loco::must_cast<luci::CircleNode *>(target); \ + EXPECT_ANY_THROW(quantize_and_verify_with_wrong_granularity(&g, type, granularity, node)); \ + } while (0) + +// Test a local helper function +TEST(QuantizedModelVerifierTest, LocalCreateDummyConst) +{ + loco::Graph g; + + EXPECT_NO_THROW(create_dummy_const<Type::FLOAT32>(&g, {32, 32})); +} + +TEST(QuantizedModelVerifierTest, LocalCreateConst) +{ + loco::Graph g; + std::initializer_list<float> values = {0.1, 0, -5, 100}; + luci::CircleConst *node = create_const<Type::FLOAT32, float>(&g, {2, 2}, values); + + uint32_t index = 0; + for (auto val : values) + { + EXPECT_EQ(node->at<Type::FLOAT32>(index++), val); + } +} + +TEST(QuantizedModelVerifierTest, InstanceNorm) +{ + TEST_WITH_GRAPH(InstanceNormTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(InstanceNormTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(InstanceNormTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, InstanceNorm_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(InstanceNormTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(InstanceNormTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(InstanceNormTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, InstanceNorm_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(InstanceNormTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(InstanceNormTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(InstanceNormTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Logistic) +{ + TEST_WITH_GRAPH(LogisticTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(LogisticTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(LogisticTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Logistic_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(LogisticTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(LogisticTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(LogisticTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Logistic_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(LogisticTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(LogisticTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(LogisticTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Softmax) +{ + TEST_WITH_GRAPH(SoftmaxTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SoftmaxTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SoftmaxTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Softmax_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(SoftmaxTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SoftmaxTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SoftmaxTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Softmax_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(SoftmaxTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SoftmaxTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SoftmaxTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, SpaceToBatchND) +{ + TEST_WITH_GRAPH(SpaceToBatchNDTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SpaceToBatchNDTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SpaceToBatchNDTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, SpaceToBatchND_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(SpaceToBatchNDTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SpaceToBatchNDTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SpaceToBatchNDTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, SpaceToBatchND_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(SpaceToBatchNDTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SpaceToBatchNDTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SpaceToBatchNDTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, SpaceToDepth) +{ + TEST_WITH_GRAPH(SpaceToDepthTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SpaceToDepthTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SpaceToDepthTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, SpaceToDepth_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(SpaceToDepthTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SpaceToDepthTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SpaceToDepthTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, SpaceToDepth_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(SpaceToDepthTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SpaceToDepthTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SpaceToDepthTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Slice) +{ + TEST_WITH_GRAPH(SliceTestGraph<Type::S32>, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SliceTestGraph<Type::S32>, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SliceTestGraph<Type::S32>, Type::S16, Granularity::ChannelWise); + + TEST_WITH_GRAPH(SliceTestGraph<Type::S64>, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SliceTestGraph<Type::S64>, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SliceTestGraph<Type::S64>, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Slice_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(SliceTestGraph<Type::S32>, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SliceTestGraph<Type::S32>, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SliceTestGraph<Type::S32>, Type::S16, Granularity::ChannelWise, Type::U8); + + TEST_WITH_WRONG_TYPE(SliceTestGraph<Type::S64>, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SliceTestGraph<Type::S64>, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SliceTestGraph<Type::S64>, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Slice_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(SliceTestGraph<Type::S32>, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SliceTestGraph<Type::S32>, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SliceTestGraph<Type::S32>, Type::S16, Granularity::ChannelWise); + + TEST_WITH_WRONG_GRANULARITY(SliceTestGraph<Type::S64>, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SliceTestGraph<Type::S64>, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SliceTestGraph<Type::S64>, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Split) +{ + TEST_WITH_GRAPH(SplitTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SplitTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SplitTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Split_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(SplitTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SplitTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SplitTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Split_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(SplitTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SplitTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SplitTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, StridedSlice) +{ + TEST_WITH_GRAPH(StridedSliceTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(StridedSliceTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(StridedSliceTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, StridedSlice_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(StridedSliceTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(StridedSliceTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(StridedSliceTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, StridedSlice_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(StridedSliceTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(StridedSliceTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(StridedSliceTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, ArgMax) +{ + TEST_WITH_GRAPH(ArgMaxTestGraph<Type::S32>, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ArgMaxTestGraph<Type::S32>, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ArgMaxTestGraph<Type::S32>, Type::S16, Granularity::ChannelWise); + + TEST_WITH_GRAPH(ArgMaxTestGraph<Type::S64>, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ArgMaxTestGraph<Type::S64>, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ArgMaxTestGraph<Type::S64>, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, ArgMax_wrong_dimension_type_NEG) +{ + ArgMaxTestGraph<Type::S32> g; + g.init(); + luci::QuantizeWithMinMaxPass pass(Type::FLOAT32, Type::U8, Granularity::LayerWise); + pass.run(g.g()); + + g._dimension->dtype(Type::U8); + + luci::QuantizedModelVerifier verifier(Type::U8, Granularity::LayerWise); + EXPECT_ANY_THROW(verifier.verify(g.g())); +} + +TEST(QuantizedModelVerifierTest, ArgMax_wrong_input_granularity_NEG) +{ + ArgMaxTestGraph<Type::S32> g; + g.init(); + + luci::QuantizeWithMinMaxPass pass(Type::FLOAT32, Type::U8, Granularity::LayerWise); + pass.run(g.g()); + + insert_scale_zp(loco::must_cast<luci::CircleNode *>(g._argmax->input()), 1.0, 1); + + luci::QuantizedModelVerifier verifier(Type::U8, Granularity::LayerWise); + EXPECT_ANY_THROW(verifier.verify(g.g())); +} + +TEST(QuantizedModelVerifierTest, BatchToSpaceND) +{ + TEST_WITH_GRAPH(BatchToSpaceNDTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(BatchToSpaceNDTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(BatchToSpaceNDTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, BatchToSpaceND_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(BatchToSpaceNDTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(BatchToSpaceNDTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(BatchToSpaceNDTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, BatchToSpaceND_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(BatchToSpaceNDTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(BatchToSpaceNDTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(BatchToSpaceNDTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, DepthToSpace) +{ + TEST_WITH_GRAPH(DepthToSpaceTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(DepthToSpaceTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(DepthToSpaceTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, DepthToSpace_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(DepthToSpaceTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(DepthToSpaceTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(DepthToSpaceTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, DepthToSpace_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(DepthToSpaceTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(DepthToSpaceTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(DepthToSpaceTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Concatenation) +{ + TEST_WITH_GRAPH(ConcatenationTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ConcatenationTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ConcatenationTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Concatenation_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(ConcatenationTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(ConcatenationTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(ConcatenationTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Concatenation_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(ConcatenationTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(ConcatenationTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(ConcatenationTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, LogicalOr) +{ + TEST_WITH_GRAPH(BinaryLogicalOpTestGraph<luci::CircleLogicalOr>, Type::U8, + Granularity::LayerWise); + TEST_WITH_GRAPH(BinaryLogicalOpTestGraph<luci::CircleLogicalOr>, Type::U8, + Granularity::ChannelWise); + TEST_WITH_GRAPH(BinaryLogicalOpTestGraph<luci::CircleLogicalOr>, Type::S16, + Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, LogicalOr_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(BinaryLogicalOpTestGraph<luci::CircleLogicalOr>, Type::U8, + Granularity::LayerWise, Type::U8); + TEST_WITH_WRONG_TYPE(BinaryLogicalOpTestGraph<luci::CircleLogicalOr>, Type::U8, + Granularity::ChannelWise, Type::U8); + TEST_WITH_WRONG_TYPE(BinaryLogicalOpTestGraph<luci::CircleLogicalOr>, Type::S16, + Granularity::ChannelWise, Type::S16); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Reshape) +{ + TEST_WITH_GRAPH(ReshapeTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ReshapeTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ReshapeTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Reshape_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(ReshapeTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(ReshapeTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(ReshapeTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Reshape_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(ReshapeTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(ReshapeTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(ReshapeTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Tanh) +{ + TEST_WITH_GRAPH(TanhTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(TanhTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(TanhTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Tanh_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(TanhTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(TanhTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(TanhTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Tanh_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(TanhTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(TanhTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(TanhTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Pad) +{ + TEST_WITH_GRAPH(PadTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(PadTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(PadTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Pad_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(PadTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(PadTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(PadTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Pad_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(PadTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(PadTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(PadTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Transpose) +{ + TEST_WITH_GRAPH(TransposeTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(TransposeTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(TransposeTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Transpose_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(TransposeTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(TransposeTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(TransposeTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Transpose_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(TransposeTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(TransposeTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(TransposeTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Floor) +{ + TEST_WITH_GRAPH(FloorTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(FloorTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(FloorTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Floor_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(FloorTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(FloorTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(FloorTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Floor_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(FloorTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(FloorTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(FloorTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, GreaterEqual) +{ + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::LayerWise); + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::ChannelWise); + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::S16, + Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, GreaterEqual_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::LayerWise, Type::U8); + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::ChannelWise, Type::U8); + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::S16, + Granularity::ChannelWise, Type::S16); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, GreaterEqual_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::LayerWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::ChannelWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::S16, + Granularity::ChannelWise, g.x()); + + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::LayerWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::U8, + Granularity::ChannelWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreaterEqual>, Type::S16, + Granularity::ChannelWise, g.y()); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Greater) +{ + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleGreater>, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Greater_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, Granularity::LayerWise, + Type::U8); + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, + Granularity::ChannelWise, Type::U8); + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleGreater>, Type::S16, + Granularity::ChannelWise, Type::S16); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Greater_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, + Granularity::LayerWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, + Granularity::ChannelWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreater>, Type::S16, + Granularity::ChannelWise, g.x()); + + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, + Granularity::LayerWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreater>, Type::U8, + Granularity::ChannelWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleGreater>, Type::S16, + Granularity::ChannelWise, g.y()); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, NotEqual) +{ + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, NotEqual_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, + Granularity::LayerWise, Type::U8); + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, + Granularity::ChannelWise, Type::U8); + TEST_WITH_WRONG_TYPE(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::S16, + Granularity::ChannelWise, Type::S16); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, NotEqual_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, + Granularity::LayerWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, + Granularity::ChannelWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::S16, + Granularity::ChannelWise, g.x()); + + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, + Granularity::LayerWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::U8, + Granularity::ChannelWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(ComparisonOpTestGraph<luci::CircleNotEqual>, Type::S16, + Granularity::ChannelWise, g.y()); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Div) +{ + TEST_WITH_GRAPH(DivTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(DivTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(DivTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Div_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(DivTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(DivTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(DivTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Div_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY_TARGET(DivTestGraph, Type::U8, Granularity::LayerWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(DivTestGraph, Type::U8, Granularity::ChannelWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(DivTestGraph, Type::S16, Granularity::ChannelWise, g.x()); + + TEST_WITH_WRONG_GRANULARITY_TARGET(DivTestGraph, Type::U8, Granularity::LayerWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(DivTestGraph, Type::U8, Granularity::ChannelWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(DivTestGraph, Type::S16, Granularity::ChannelWise, g.y()); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, FloorDiv) +{ + TEST_WITH_GRAPH(FloorDivTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(FloorDivTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(FloorDivTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, FloorDiv_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(FloorDivTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(FloorDivTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(FloorDivTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, FloorDiv_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY_TARGET(FloorDivTestGraph, Type::U8, Granularity::LayerWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(FloorDivTestGraph, Type::U8, Granularity::ChannelWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(FloorDivTestGraph, Type::S16, Granularity::ChannelWise, g.x()); + + TEST_WITH_WRONG_GRANULARITY_TARGET(FloorDivTestGraph, Type::U8, Granularity::LayerWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(FloorDivTestGraph, Type::U8, Granularity::ChannelWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(FloorDivTestGraph, Type::S16, Granularity::ChannelWise, g.y()); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Rsqrt) +{ + TEST_WITH_GRAPH(RsqrtTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(RsqrtTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(RsqrtTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Rsqrt_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(RsqrtTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(RsqrtTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(RsqrtTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Rsqrt_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(RsqrtTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(RsqrtTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(RsqrtTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Sqrt) +{ + TEST_WITH_GRAPH(SqrtTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(SqrtTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(SqrtTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Sqrt_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(SqrtTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(SqrtTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(SqrtTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Sqrt_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(SqrtTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(SqrtTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(SqrtTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Elu) +{ + TEST_WITH_GRAPH(EluTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(EluTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(EluTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Elu_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(EluTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(EluTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(EluTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Elu_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(EluTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(EluTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(EluTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Pow) +{ + TEST_WITH_GRAPH(PowTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(PowTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(PowTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Pow_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(PowTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(PowTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(PowTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, Pow_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY_TARGET(PowTestGraph, Type::U8, Granularity::LayerWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(PowTestGraph, Type::U8, Granularity::ChannelWise, g.x()); + TEST_WITH_WRONG_GRANULARITY_TARGET(PowTestGraph, Type::S16, Granularity::ChannelWise, g.x()); + + TEST_WITH_WRONG_GRANULARITY_TARGET(PowTestGraph, Type::U8, Granularity::LayerWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(PowTestGraph, Type::U8, Granularity::ChannelWise, g.y()); + TEST_WITH_WRONG_GRANULARITY_TARGET(PowTestGraph, Type::S16, Granularity::ChannelWise, g.y()); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, ResizeBilinear) +{ + TEST_WITH_GRAPH(ResizeBilinearTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_GRAPH(ResizeBilinearTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_GRAPH(ResizeBilinearTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, ResizeBilinear_wrong_type_NEG) +{ + TEST_WITH_WRONG_TYPE(ResizeBilinearTestGraph, Type::U8, Granularity::LayerWise, Type::S16); + TEST_WITH_WRONG_TYPE(ResizeBilinearTestGraph, Type::U8, Granularity::ChannelWise, Type::S16); + TEST_WITH_WRONG_TYPE(ResizeBilinearTestGraph, Type::S16, Granularity::ChannelWise, Type::U8); + SUCCEED(); +} + +TEST(QuantizedModelVerifierTest, ResizeBilinear_wrong_granularity_NEG) +{ + TEST_WITH_WRONG_GRANULARITY(ResizeBilinearTestGraph, Type::U8, Granularity::LayerWise); + TEST_WITH_WRONG_GRANULARITY(ResizeBilinearTestGraph, Type::U8, Granularity::ChannelWise); + TEST_WITH_WRONG_GRANULARITY(ResizeBilinearTestGraph, Type::S16, Granularity::ChannelWise); + SUCCEED(); +} + +#undef TEST_WITH_GRAPH +#undef TEST_WITH_WRONG_TYPE +#undef TEST_WITH_WRONG_GRANULARITY diff --git a/compiler/luci/pass/src/RemoveRedundantReshape.cpp b/compiler/luci/pass/src/RemoveRedundantReshape.cpp new file mode 100644 index 000000000..2f0b22ae6 --- /dev/null +++ b/compiler/luci/pass/src/RemoveRedundantReshape.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 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/Pass/RemoveRedundantReshapePass.h" + +#include <luci/IR/CircleNodes.h> + +namespace +{ + +bool remove_redundant_reshape(luci::CircleReshape *node) +{ + auto pred_node = dynamic_cast<luci::CircleReshape *>(node->tensor()); + if (pred_node == nullptr) + return false; + + node->tensor(pred_node->tensor()); + return true; +} + +} // namespace + +namespace luci +{ + +/** + * BEFORE + * + * [CircleNode] + * | + * [CircleReshape_1] + * | + * [CircleReshape_2] + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode] + * / \ + * [CircleReshape_1] [CircleReshape_2] + * | + * [CircleNode] + **/ +bool RemoveRedundantReshapePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto reshape_node = dynamic_cast<luci::CircleReshape *>(node)) + { + if (remove_redundant_reshape(reshape_node)) + changed = true; + } + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/RemoveRedundantReshape.test.cpp b/compiler/luci/pass/src/RemoveRedundantReshape.test.cpp new file mode 100644 index 000000000..617840f3a --- /dev/null +++ b/compiler/luci/pass/src/RemoveRedundantReshape.test.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021 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/Pass/RemoveRedundantReshapePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +class RemoveRedundantReshape : public ::testing::Test +{ +public: + RemoveRedundantReshape() {} + + void createReshapeConst(luci::CircleReshape *target, const std::vector<int32_t> shape) + { + auto shape_const = g.nodes()->create<luci::CircleConst>(); + shape_const->dtype(loco::DataType::S32); + shape_const->size<loco::DataType::S32>(shape.size()); + shape_const->shape_status(luci::ShapeStatus::VALID); + shape_const->rank(1); + shape_const->dim(0).set(shape.size()); + for (int32_t i = 0; i < shape.size(); i++) + { + shape_const->at<loco::DataType::S32>(i) = shape.at(i); + } + shape_const->name("shape_const"); + target->shape(shape_const); + } + + void buildGraph(const std::initializer_list<uint32_t> base_shape, + const std::vector<int32_t> first_shape, const std::vector<int32_t> second_shape) + { + // Input Create. + input = g.nodes()->create<luci::CircleInput>(); + auto graph_input = g.inputs()->create(); + input->index(graph_input->index()); + input->shape_status(luci::ShapeStatus::VALID); + input->rank(base_shape.size()); + input->shape(base_shape); + input->name("input"); + + // Create first reshape. + first_reshape = g.nodes()->create<luci::CircleReshape>(); + first_reshape->tensor(input); + first_reshape->name("Reshape"); + createReshapeConst(first_reshape, first_shape); + + // Create second reshape. + second_reshape = g.nodes()->create<luci::CircleReshape>(); + second_reshape->tensor(first_reshape); + second_reshape->name("second_reshape"); + createReshapeConst(second_reshape, second_shape); + + // Output Connect. + output = g.nodes()->create<luci::CircleOutput>(); + output->from(second_reshape); + output->name("output"); + auto graph_output = g.outputs()->create(); + output->index(graph_output->index()); + } + +public: + loco::Graph g; + luci::CircleInput *input = nullptr; + luci::CircleReshape *first_reshape = nullptr; + luci::CircleReshape *second_reshape = nullptr; + luci::CircleOutput *output = nullptr; +}; + +} // namespace + +TEST(RemoveRedundantReshapePassTest, name) +{ + luci::RemoveRedundantReshapePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(RemoveRedundantReshape, simple_case) +{ + buildGraph({4, 6}, {-1, 4, 6}, {1, -1, 2, 3}); + luci::RemoveRedundantReshapePass pass; + while (pass.run(&g)) + ; + int count = 0; + for (auto node : loco::active_nodes(loco::output_nodes(&g))) + { + if (auto reshape = dynamic_cast<luci::CircleReshape *>(node)) + { + count++; + } + } + ASSERT_EQ(1, count); +} diff --git a/compiler/luci/pass/src/RemoveRedundantTranspose.test.cpp b/compiler/luci/pass/src/RemoveRedundantTranspose.test.cpp deleted file mode 100644 index db608b674..000000000 --- a/compiler/luci/pass/src/RemoveRedundantTranspose.test.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2020 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/Pass/RemoveRedundantTransposePass.h" - -#include <luci/IR/CircleNodes.h> - -#include <vector> - -#include <gtest/gtest.h> - -namespace -{ - -void setValue(luci::CircleConst *node, const std::vector<int> &v) -{ - node->dtype(loco::DataType::S32); - node->size<loco::DataType::S32>(v.size()); - node->rank(1); - node->dim(0).set(v.size()); - for (int i = 0; i < v.size(); ++i) - { - node->at<loco::DataType::S32>(i) = v[i]; - } -} - -/** - * Type1 - * BEFORE - * | - * [CircleNode] [CircleConst] - * \ / - * [CircleTranspose] [CircleConst] - * \ / - * [CircleTranspose] - * | - * - * AFTER - * | - * [CircleNode] - * | Remove Both - * - * -------------------------------------------- - * - * Type2 - * BEFORE - * | - * [CircleNode] [CircleConst] - * \ / - * [CircleTranspose] [CircleConst] - * \ / - * [CircleTranspose] - * | - * - * AFTER - * | | - * [CircleNode] [CircleConst] - * \ / - * [CircleTranspose] - * | - * - */ -void create_redundunt_transpose(loco::Graph *g, const std::vector<int32_t> &perm1, - const std::vector<int32_t> &perm2) -{ - assert(g); - - auto input = g->nodes()->create<luci::CircleInput>(); - auto graph_input = g->inputs()->create(); - input->index(graph_input->index()); - - // Create perm1 - auto perm1_node = g->nodes()->create<luci::CircleConst>(); - setValue(perm1_node, perm1); - - auto transpose1 = g->nodes()->create<luci::CircleTranspose>(); - transpose1->dtype(loco::DataType::FLOAT32); - transpose1->a(input); - transpose1->perm(perm1_node); - - // Create perm2 - auto perm2_node = g->nodes()->create<luci::CircleConst>(); - setValue(perm2_node, perm2); - - auto transpose2 = g->nodes()->create<luci::CircleTranspose>(); - transpose2->dtype(loco::DataType::FLOAT32); - transpose2->a(transpose1); - transpose2->perm(perm2_node); - - // Output - auto output = g->nodes()->create<luci::CircleOutput>(); - output->from(transpose2); - auto graph_output = g->outputs()->create(); - output->index(graph_output->index()); -} - -} // namespace - -TEST(RemoveRedundantTransposePass, remove_consecutive_transpose_function_type1) -{ - auto graph = loco::make_graph(); - create_redundunt_transpose(graph.get(), {1, 0, 2, 3}, {1, 0, 2, 3}); - - luci::RemoveRedundantTransposePass pass; - while (pass.run(graph.get())) - ; - luci::CircleTranspose *transpose_node = nullptr; - for (auto node : loco::active_nodes(loco::output_nodes(graph.get()))) - { - auto trans = dynamic_cast<luci::CircleTranspose *>(node); - if (not trans) - continue; - transpose_node = trans; - break; - } - // No transpose node is in graph. - ASSERT_EQ(nullptr, transpose_node); -} - -TEST(RemoveRedundantTransposePass, remove_consecutive_transpose_function_type2) -{ - auto graph = loco::make_graph(); - create_redundunt_transpose(graph.get(), {0, 1, 3, 2}, {1, 0, 2, 3}); - - luci::RemoveRedundantTransposePass pass; - while (pass.run(graph.get())) - ; - luci::CircleTranspose *transpose_node = nullptr; - for (auto node : loco::active_nodes(loco::output_nodes(graph.get()))) - { - auto trans = dynamic_cast<luci::CircleTranspose *>(node); - if (not trans) - continue; - transpose_node = trans; - break; - } - // Just one transpose node, with updated perm constant. - ASSERT_NE(nullptr, transpose_node); - auto perm = loco::must_cast<luci::CircleConst *>(transpose_node->perm()); - ASSERT_EQ(1, perm->at<loco::DataType::S32>(0)); - ASSERT_EQ(0, perm->at<loco::DataType::S32>(1)); - ASSERT_EQ(3, perm->at<loco::DataType::S32>(2)); - ASSERT_EQ(2, perm->at<loco::DataType::S32>(3)); -} diff --git a/compiler/luci/pass/src/RemoveRedundantTranspose.cpp b/compiler/luci/pass/src/RemoveRedundantTransposePass.cpp index 33cb76520..71c51ecda 100644 --- a/compiler/luci/pass/src/RemoveRedundantTranspose.cpp +++ b/compiler/luci/pass/src/RemoveRedundantTransposePass.cpp @@ -17,6 +17,7 @@ #include "luci/Pass/RemoveRedundantTransposePass.h" #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { @@ -35,47 +36,54 @@ bool check_perm(const luci::CircleConst *first_perm, const luci::CircleConst *se return true; } -bool remove_consecutive_transpose_function(luci::CircleNode *node) +bool remove_consecutive_transpose_function(luci::CircleTranspose *target_node) { - auto target_node = dynamic_cast<luci::CircleTranspose *>(node); - if (target_node == nullptr) - return false; auto pred_node = dynamic_cast<luci::CircleTranspose *>(target_node->a()); if (pred_node == nullptr) return false; - if (loco::succs(pred_node).size() != 1) - return false; - auto pred_perm = dynamic_cast<luci::CircleConst *>(target_node->perm()); - if (pred_perm == nullptr) + auto target_perm = dynamic_cast<luci::CircleConst *>(target_node->perm()); + if (target_perm == nullptr) return false; - auto main_perm = dynamic_cast<luci::CircleConst *>(pred_node->perm()); - if (main_perm == nullptr) + auto pred_perm = dynamic_cast<luci::CircleConst *>(pred_node->perm()); + if (pred_perm == nullptr) return false; auto main_node = loco::must_cast<luci::CircleNode *>(pred_node->a()); - if (check_perm(pred_perm, main_perm)) + if (check_perm(target_perm, pred_perm)) { - replace(node).with(main_node); + replace(target_node).with(main_node); } else { - auto g = main_perm->graph(); + auto name = target_node->name(); + assert(name.length() > 0); + + auto g = pred_perm->graph(); auto new_const_node = g->nodes()->create<luci::CircleConst>(); new_const_node->dtype(loco::DataType::S32); new_const_node->rank(1); - new_const_node->dim(0) = main_perm->dim(0); - new_const_node->size<loco::DataType::S32>(main_perm->dim(0).value()); + new_const_node->dim(0) = pred_perm->dim(0); + new_const_node->size<loco::DataType::S32>(pred_perm->dim(0).value()); new_const_node->shape_status(luci::ShapeStatus::VALID); - for (uint32_t i = 0; i < main_perm->size<loco::DataType::S32>(); i++) + for (uint32_t i = 0; i < pred_perm->size<loco::DataType::S32>(); i++) { new_const_node->at<loco::DataType::S32>(i) = - pred_perm->at<loco::DataType::S32>(main_perm->at<loco::DataType::S32>(i)); + target_perm->at<loco::DataType::S32>(pred_perm->at<loco::DataType::S32>(i)); } - pred_node->perm(new_const_node); - replace(node).with(pred_node); + new_const_node->name(name + "/Transpose/perm"); + + // Create New Transpose Node + auto new_transpose_node = g->nodes()->create<luci::CircleTranspose>(); + new_transpose_node->dtype(target_node->dtype()); + new_transpose_node->a(main_node); + new_transpose_node->perm(new_const_node); + new_transpose_node->name(name + "/Transpose"); + luci::add_origin(new_transpose_node, luci::get_origin(target_node)); + + replace(target_node).with(new_transpose_node); } return true; } @@ -84,41 +92,36 @@ bool remove_consecutive_transpose_function(luci::CircleNode *node) namespace luci { + /** * BEFORE * | * [CircleNode] [CircleConst] - * (main_node) (main_perm) - * \ / + * | (pred_perm) + * \ / * [CircleTranspose] [CircleConst] - * (pred_node) (pred_perm) + * (pred_node) (target_perm) * \ / * [CircleTranspose] * (target_node) * | * * AFTER - * <Optional Case> - * - * | | | - * [CircleNode] [CircleConst] | - * (main_node) (new_const_node) | - * \ / or [CircleNode] - * [CircleTranspose] (main_node) - * (pred_node) | + * | | + * [CircleNode] [CircleConst](new) | + * \ / or [CircleNode] + * [CircleTranspose](new) | * | | - * */ bool RemoveRedundantTransposePass::run(loco::Graph *g) { bool changed = false; for (auto node : loco::active_nodes(loco::output_nodes(g))) { - auto circle_node = loco::must_cast<luci::CircleNode *>(node); - if (remove_consecutive_transpose_function(circle_node)) + if (auto transpose = dynamic_cast<luci::CircleTranspose *>(node)) { - changed = true; - break; + if (remove_consecutive_transpose_function(transpose)) + changed = true; } } return changed; diff --git a/compiler/luci/pass/src/RemoveRedundantTransposePass.test.cpp b/compiler/luci/pass/src/RemoveRedundantTransposePass.test.cpp new file mode 100644 index 000000000..e80623499 --- /dev/null +++ b/compiler/luci/pass/src/RemoveRedundantTransposePass.test.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2020 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/Pass/RemoveRedundantTransposePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <vector> + +#include <gtest/gtest.h> + +namespace +{ + +void setValue(luci::CircleConst *node, const std::vector<int> &v) +{ + node->dtype(loco::DataType::S32); + node->size<loco::DataType::S32>(v.size()); + node->rank(1); + node->dim(0).set(v.size()); + for (int i = 0; i < v.size(); ++i) + { + node->at<loco::DataType::S32>(i) = v[i]; + } +} + +/** + * Remove for consecutive Transpose + * + * Type1: Remove both Transpose + * BEFORE + * | + * [CircleNode] [CircleConst] + * \ / + * [CircleTranspose] [CircleConst] + * \ / + * [CircleTranspose] + * | + * + * AFTER + * | + * [CircleNode] + * | + * + * -------------------------------------------- + * + * Type2: Merge to one Transpose + * BEFORE + * | + * [CircleNode] [CircleConst] + * \ / + * [CircleTranspose] [CircleConst] + * \ / + * [CircleTranspose] + * | + * + * AFTER + * | + * [CircleNode] [CircleConst] + * \ / + * [CircleTranspose] + * | + * + */ +void create_redundunt_transpose(loco::Graph *g, const std::vector<int32_t> &perm1, + const std::vector<int32_t> &perm2) +{ + assert(g); + + auto input = g->nodes()->create<luci::CircleInput>(); + auto graph_input = g->inputs()->create(); + input->index(graph_input->index()); + input->name("input"); + + // Create perm1 + auto perm1_node = g->nodes()->create<luci::CircleConst>(); + setValue(perm1_node, perm1); + perm1_node->name("perm1_node"); + + auto transpose1 = g->nodes()->create<luci::CircleTranspose>(); + transpose1->dtype(loco::DataType::FLOAT32); + transpose1->a(input); + transpose1->perm(perm1_node); + transpose1->name("transpose1"); + + // Create perm2 + auto perm2_node = g->nodes()->create<luci::CircleConst>(); + setValue(perm2_node, perm2); + perm2_node->name("perm2_node"); + + auto transpose2 = g->nodes()->create<luci::CircleTranspose>(); + transpose2->dtype(loco::DataType::FLOAT32); + transpose2->a(transpose1); + transpose2->perm(perm2_node); + transpose2->name("transpose2"); + + // Output + auto output = g->nodes()->create<luci::CircleOutput>(); + output->from(transpose2); + auto graph_output = g->outputs()->create(); + output->index(graph_output->index()); + output->name("output"); +} + +/** + * Remove for consecutive Transposes with branching + * + * BEFORE + * | + * [CircleNode] [CircleConst] + * \ / + * [CircleConst] [CircleTranspose] [CircleConst] + * \ / \ / + * [CircleTranspose] [CircleTranspose] + * | | + * [CircleNode] [CircleNode] + * | | + * + * AFTER + * Type 1: Remove all Transpose + * | + * [CircleNode] + * / \ + * [CircleNode] [CircleNode] + * | | + * + * Type 2: Remove both for one side and create new for another side + * | + * [CircleNode] [CircleConst](new) + * / \ / + * / [CircleTranspose](new) + * | | + * [CircleNode] [CircleNode] + * | | + */ +void create_redundunt_transpose_with_branch(loco::Graph *g, const std::vector<int32_t> &perm1, + const std::vector<int32_t> &perm2, + const std::vector<int32_t> &perm3) +{ + assert(g); + + auto input = g->nodes()->create<luci::CircleInput>(); + auto graph_input = g->inputs()->create(); + input->dtype(loco::DataType::FLOAT32); + input->index(graph_input->index()); + input->name("input"); + graph_input->dtype(loco::DataType::FLOAT32); + + graph_input->shape({4, 4, 4, 4}); + input->shape({4, 4, 4, 4}); + + // Create perm1 + auto perm1_node = g->nodes()->create<luci::CircleConst>(); + setValue(perm1_node, perm1); + perm1_node->name("perm1_node"); + + auto transpose1 = g->nodes()->create<luci::CircleTranspose>(); + transpose1->dtype(loco::DataType::FLOAT32); + transpose1->a(input); + transpose1->perm(perm1_node); + transpose1->name("transpose1"); + + // Create perm2 + auto perm2_node = g->nodes()->create<luci::CircleConst>(); + setValue(perm2_node, perm2); + perm2_node->name("perm2_node"); + + auto transpose2 = g->nodes()->create<luci::CircleTranspose>(); + transpose2->dtype(loco::DataType::FLOAT32); + transpose2->a(transpose1); + transpose2->perm(perm2_node); + transpose2->name("transpose2"); + + // create perm3 + auto perm3_node = g->nodes()->create<luci::CircleConst>(); + setValue(perm3_node, perm3); + perm3_node->name("perm3_node"); + + auto transpose3 = g->nodes()->create<luci::CircleTranspose>(); + transpose3->dtype(loco::DataType::FLOAT32); + transpose3->a(transpose1); + transpose3->perm(perm3_node); + transpose3->name("transpose3"); + + // Output + auto output1 = g->nodes()->create<luci::CircleOutput>(); + output1->from(transpose2); + output1->name("output1"); + auto output2 = g->nodes()->create<luci::CircleOutput>(); + output2->from(transpose3); + output2->name("output2"); + auto graph_output1 = g->outputs()->create(); + output1->index(graph_output1->index()); + auto graph_output2 = g->outputs()->create(); + output2->index(graph_output2->index()); + output1->dtype(loco::DataType::FLOAT32); + output2->dtype(loco::DataType::FLOAT32); + graph_output1->dtype(loco::DataType::FLOAT32); + graph_output2->dtype(loco::DataType::FLOAT32); + output1->shape({4, 4, 4, 4}); + output2->shape({4, 4, 4, 4}); + graph_output1->shape({4, 4, 4, 4}); + graph_output2->shape({4, 4, 4, 4}); +} + +} // namespace + +TEST(RemoveRedundantTransposePassTest, name) +{ + luci::RemoveRedundantTransposePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(RemoveRedundantTransposePass, remove_consecutive_transpose_function_type1) +{ + auto graph = loco::make_graph(); + create_redundunt_transpose(graph.get(), {1, 0, 2, 3}, {1, 0, 2, 3}); + + luci::RemoveRedundantTransposePass pass; + while (pass.run(graph.get())) + ; + luci::CircleTranspose *transpose_node = nullptr; + for (auto node : loco::active_nodes(loco::output_nodes(graph.get()))) + { + auto trans = dynamic_cast<luci::CircleTranspose *>(node); + if (not trans) + continue; + transpose_node = trans; + break; + } + // No transpose node is in graph. + ASSERT_EQ(nullptr, transpose_node); +} + +TEST(RemoveRedundantTransposePass, remove_consecutive_transpose_function_type2) +{ + auto graph = loco::make_graph(); + create_redundunt_transpose(graph.get(), {0, 1, 3, 2}, {1, 0, 2, 3}); + + luci::RemoveRedundantTransposePass pass; + while (pass.run(graph.get())) + ; + luci::CircleTranspose *transpose_node = nullptr; + for (auto node : loco::active_nodes(loco::output_nodes(graph.get()))) + { + auto trans = dynamic_cast<luci::CircleTranspose *>(node); + if (not trans) + continue; + transpose_node = trans; + break; + } + // Just one transpose node, with updated perm constant. + ASSERT_NE(nullptr, transpose_node); + auto perm = loco::must_cast<luci::CircleConst *>(transpose_node->perm()); + ASSERT_EQ(1, perm->at<loco::DataType::S32>(0)); + ASSERT_EQ(0, perm->at<loco::DataType::S32>(1)); + ASSERT_EQ(3, perm->at<loco::DataType::S32>(2)); + ASSERT_EQ(2, perm->at<loco::DataType::S32>(3)); +} + +/** + * @brief Test case that first transpose output become input of operations more than one. + */ +TEST(RemoveRedundantTransposePass, remove_consecutive_transpose_function_with_branch_remove_case) +{ + auto graph = loco::make_graph(); + create_redundunt_transpose_with_branch(graph.get(), {1, 0, 2, 3}, {1, 0, 2, 3}, {1, 0, 2, 3}); + + luci::RemoveRedundantTransposePass pass; + while (pass.run(graph.get())) + ; + luci::CircleTranspose *transpose_node = nullptr; + for (auto node : loco::active_nodes(loco::output_nodes(graph.get()))) + { + auto trans = dynamic_cast<luci::CircleTranspose *>(node); + if (not trans) + continue; + transpose_node = trans; + break; + } + // No transpose node is in graph. + ASSERT_EQ(nullptr, transpose_node); +} + +TEST(RemoveRedundantTransposePass, remove_consecutive_transpose_function_with_branch_leave_one) +{ + auto graph = loco::make_graph(); + create_redundunt_transpose_with_branch(graph.get(), {1, 0, 2, 3}, {1, 0, 2, 3}, {0, 1, 3, 2}); + + luci::RemoveRedundantTransposePass pass; + while (pass.run(graph.get())) + ; + luci::CircleTranspose *transpose_node = nullptr; + for (auto node : loco::active_nodes(loco::output_nodes(graph.get()))) + { + auto trans = dynamic_cast<luci::CircleTranspose *>(node); + if (not trans) + continue; + transpose_node = trans; + break; + } + ASSERT_NE(nullptr, transpose_node); + auto perm = loco::must_cast<luci::CircleConst *>(transpose_node->perm()); + ASSERT_EQ(1, perm->at<loco::DataType::S32>(0)); + ASSERT_EQ(0, perm->at<loco::DataType::S32>(1)); + ASSERT_EQ(3, perm->at<loco::DataType::S32>(2)); + ASSERT_EQ(2, perm->at<loco::DataType::S32>(3)); +} diff --git a/compiler/luci/pass/src/RemoveUnnecessaryReshapePass.cpp b/compiler/luci/pass/src/RemoveUnnecessaryReshapePass.cpp new file mode 100644 index 000000000..3f0c4ee82 --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessaryReshapePass.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 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/Pass/RemoveUnnecessaryReshapePass.h" + +#include <luci/IR/CircleNodes.h> + +namespace +{ + +bool remove_no_effect_reshape(luci::CircleNode *node) +{ + auto target_node = dynamic_cast<luci::CircleReshape *>(node); + if (target_node == nullptr) + return false; + + auto new_shape = dynamic_cast<luci::CircleConst *>(target_node->shape()); + if (new_shape == nullptr) + return false; + + // Compare updated shape and input shape. + auto input_node = loco::must_cast<luci::CircleNode *>(target_node->tensor()); + if (input_node->rank() != new_shape->dim(0).value()) + return false; + for (uint32_t i = 0; i < input_node->rank(); i++) + { + // If update_shape is -1, don't care + // TODO check updated shape has value -1 at most one. + if (new_shape->at<loco::DataType::S32>(i) == -1) + continue; + // If input_shape dynamic, can't remove this. + if (!input_node->dim(i).known()) + return false; + // If input_shape and updated shape differ, also can't remove. + if (input_node->dim(i).value() != static_cast<uint32_t>(new_shape->at<loco::DataType::S32>(i))) + return false; + } + + replace(target_node).with(input_node); + return true; +} + +} // namespace + +namespace luci +{ + +bool RemoveUnnecessaryReshapePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + if (remove_no_effect_reshape(circle_node)) + { + changed = true; + } + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/RemoveUnnecessaryReshapePass.test.cpp b/compiler/luci/pass/src/RemoveUnnecessaryReshapePass.test.cpp new file mode 100644 index 000000000..9d2e758b4 --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessaryReshapePass.test.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021 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/Pass/RemoveUnnecessaryReshapePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <luci/test/TestIOGraph.h> +#include "test/TestFirstNode.h" + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class ReshapeGraphlet +{ +public: + ReshapeGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape, bool remove) + { + std::vector<uint32_t> shape_vector{input_shape}; + + auto dim0_val = remove ? shape_vector.size() : 1; + _reshape_shape = g->nodes()->create<luci::CircleConst>(); + _reshape_shape->rank(1); + _reshape_shape->dim(0).set(dim0_val); + _reshape_shape->shape_status(luci::ShapeStatus::VALID); + _reshape_shape->dtype(loco::DataType::S32); + + _reshape_shape->size<loco::DataType::S32>(dim0_val); + for (uint32_t i = 0; i < dim0_val; i++) + { + if (remove) + _reshape_shape->at<loco::DataType::S32>(i) = static_cast<int32_t>(shape_vector.at(i)); + else + _reshape_shape->at<loco::DataType::S32>(i) = -1; + } + _reshape_shape->name("reshape_shape"); + + // Reshape create + auto newshape_rank = remove ? shape_vector.size() : 1; + _reshape = g->nodes()->create<luci::CircleReshape>(); + _reshape->newShape()->rank(newshape_rank); + for (uint32_t i = 0; i < newshape_rank; i++) + { + if (remove) + _reshape->newShape()->dim(i) = static_cast<int32_t>(shape_vector.at(i)); + else + _reshape->newShape()->dim(i) = -1; + } + _reshape->name("reshape"); + } + +protected: + luci::CircleReshape *_reshape = nullptr; + luci::CircleConst *_reshape_shape = nullptr; +}; + +class ReshapeGraph : public TestIOGraph, public ReshapeGraphlet +{ +public: + ReshapeGraph() = default; + +public: + void init(const ShapeU32 shape, bool remove) + { + TestIOGraph::init(shape, shape); + ReshapeGraphlet::init(g(), shape, remove); + + // connect graph + _reshape->tensor(input()); + _reshape->shape(_reshape_shape); + + output()->from(_reshape); + } +}; + +// TODO use ::testing::Test + +} // namespace + +TEST(RemoveUnnecessaryReshapePassTest, name) +{ + luci::RemoveUnnecessaryReshapePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(RemoveUnnecessaryReshapePass, removed) +{ + ReshapeGraph g; + + g.init({1, 2, 3, 4}, true); + + // confirm graph has Reshape + auto reshape_node = luci::test::first_node<luci::CircleReshape>(g.g()); + ASSERT_NE(nullptr, reshape_node); + luci::RemoveUnnecessaryReshapePass pass; + while (pass.run(g.g())) + ; + + // check Reshape is removed + reshape_node = luci::test::first_node<luci::CircleReshape>(g.g()); + ASSERT_EQ(nullptr, reshape_node); +} + +TEST(RemoveUnnecessaryReshapePass, not_removed_NEG) +{ + ReshapeGraph g; + + g.init({1, 2, 3, 4}, false); + + // confirm graph has Reshape + auto reshape_node = luci::test::first_node<luci::CircleReshape>(g.g()); + ASSERT_NE(nullptr, reshape_node); + luci::RemoveUnnecessaryReshapePass pass; + while (pass.run(g.g())) + ; + + // check Reshape is NOT removed + reshape_node = luci::test::first_node<luci::CircleReshape>(g.g()); + ASSERT_NE(nullptr, reshape_node); +} diff --git a/compiler/luci/pass/src/RemoveUnnecessarySlicePass.cpp b/compiler/luci/pass/src/RemoveUnnecessarySlicePass.cpp new file mode 100644 index 000000000..0720813cd --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessarySlicePass.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2020 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/Pass/RemoveUnnecessarySlicePass.h" + +#include <luci/IR/CircleNodes.h> + +namespace +{ + +/** + * @brief Return value in CircleConst. + * @details Return value in position on CircleConst with int64 format. + * Begin must be larger than or equal to 0. Size must be larger + * than or equal to -1. + */ +int64_t value_from_circle_const(const luci::CircleConst *node, uint32_t idx) +{ + assert(node->rank() == 1 && node->dim(0).value() > idx); + assert(node->dtype() == loco::DataType::S64 || node->dtype() == loco::DataType::S32); + + if (node->dtype() == loco::DataType::S64) + return node->at<loco::DataType::S64>(idx); + return static_cast<int64_t>(node->at<loco::DataType::S32>(idx)); +} + +bool remove_no_effect_slice(luci::CircleNode *node) +{ + auto target_node = dynamic_cast<luci::CircleSlice *>(node); + if (target_node == nullptr) + return false; + + auto begin_const = dynamic_cast<luci::CircleConst *>(target_node->begin()); + if (begin_const == nullptr) + return false; + + auto size_const = dynamic_cast<luci::CircleConst *>(target_node->size()); + if (size_const == nullptr) + return false; + + // Check input output shape. + auto input_node = loco::must_cast<luci::CircleNode *>(target_node->input()); + for (uint32_t i = 0; i < input_node->rank(); i++) + { + if (value_from_circle_const(begin_const, i) != 0) + return false; + + int64_t size_value = value_from_circle_const(size_const, i); + if (size_value == -1) + continue; + if (size_value != static_cast<int64_t>(input_node->dim(i).value())) + return false; + + if (!input_node->dim(i).known()) + return false; + } + replace(target_node).with(input_node); + return true; +} + +} // namespace + +namespace luci +{ +/** + * BEFORE + * + * [CircleNode] + * | + * [CircleSlice] + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode] + * | + * [CircleNode] + * + * Slice OP has no effect if, + * 1. Static Shape : begin_const[idx] is 0 AND size_const[idx] is (-1 OR input_dimension[idx]) + * 2. Dynamic Shape : begin_const[idx] is 0 AND size_const[idx] is -1 + */ +bool RemoveUnnecessarySlicePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto circle_node = loco::must_cast<luci::CircleNode *>(node); + if (remove_no_effect_slice(circle_node)) + { + changed = true; + } + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/RemoveUnnecessarySlicePass.test.cpp b/compiler/luci/pass/src/RemoveUnnecessarySlicePass.test.cpp new file mode 100644 index 000000000..80921a93a --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessarySlicePass.test.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2020 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/Pass/RemoveUnnecessarySlicePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <luci/test/TestIOGraph.h> +#include "test/TestFirstNode.h" + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SliceGraphlet +{ +public: + SliceGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape, bool remove) + { + // Begin Create. + _begin = g->nodes()->create<luci::CircleConst>(); + _begin->rank(1); + _begin->dim(0).set(input_shape.size()); + _begin->shape_status(luci::ShapeStatus::VALID); + _begin->dtype(loco::DataType::S32); + _begin->size<loco::DataType::S32>(input_shape.size()); + for (int i = 0; i < input_shape.size(); ++i) + _begin->at<loco::DataType::S32>(i) = remove ? 0 : 1; + _begin->name("begin"); + + // Size Create. + _size = g->nodes()->create<luci::CircleConst>(); + _size->rank(1); + _size->dim(0).set(input_shape.size()); + _size->shape_status(luci::ShapeStatus::VALID); + _size->dtype(loco::DataType::S32); + _size->size<loco::DataType::S32>(input_shape.size()); + for (int i = 0; i < input_shape.size(); ++i) + _size->at<loco::DataType::S32>(i) = -1; + _size->name("size"); + + // Slice Node create. + _slice = g->nodes()->create<luci::CircleSlice>(); + _slice->dtype(loco::DataType::S32); + _slice->name("slice"); + } + +protected: + luci::CircleSlice *_slice = nullptr; + luci::CircleConst *_begin = nullptr; + luci::CircleConst *_size = nullptr; +}; + +class SliceGraph : public TestIOGraph, public SliceGraphlet +{ +public: + SliceGraph() = default; + +public: + void init(const ShapeU32 shape, bool remove) + { + TestIOGraph::init(shape, shape); + SliceGraphlet::init(g(), shape, remove); + + _slice->input(input()); + _slice->begin(_begin); + _slice->size(_size); + + output()->from(_slice); + } +}; + +} // namespace + +TEST(RemoveUnnecessarySlicePass, name) +{ + luci::RemoveUnnecessarySlicePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(RemoveUnnecessarySlicePass, removed) +{ + SliceGraph g; + + g.init({2, 4, 2, 3}, true); + + // confirm graph has Slice + auto slice_node = luci::test::first_node<luci::CircleSlice>(g.g()); + ASSERT_NE(nullptr, slice_node); + luci::RemoveUnnecessarySlicePass pass; + while (pass.run(g.g())) + ; + + // check Slice is removed + slice_node = luci::test::first_node<luci::CircleSlice>(g.g()); + ASSERT_EQ(nullptr, slice_node); +} + +TEST(RemoveUnnecessarySlicePass, not_removed_NEG) +{ + SliceGraph g; + + g.init({2, 4, 2, 3}, false); + + // confirm graph has Slice + auto slice_node = luci::test::first_node<luci::CircleSlice>(g.g()); + ASSERT_NE(nullptr, slice_node); + luci::RemoveUnnecessarySlicePass pass; + while (pass.run(g.g())) + ; + + // check Slice is NOT removed + slice_node = luci::test::first_node<luci::CircleSlice>(g.g()); + ASSERT_NE(nullptr, slice_node); +} diff --git a/compiler/luci/pass/src/ShapeSignatureInferencePass.cpp b/compiler/luci/pass/src/RemoveUnnecessarySplitPass.cpp index 115b77a96..3243f6213 100644 --- a/compiler/luci/pass/src/ShapeSignatureInferencePass.cpp +++ b/compiler/luci/pass/src/RemoveUnnecessarySplitPass.cpp @@ -14,49 +14,50 @@ * limitations under the License. */ -#include "luci/Pass/ShapeSignatureInferencePass.h" +#include "luci/Pass/RemoveUnnecessarySplitPass.h" -#include <luci/IR/CircleShapeSignature.h> -#include <luci/Service/CircleShapeSignatureInference.h> +#include <luci/IR/CircleNodes.h> -#include <loco.h> - -namespace luci +namespace { - -bool ShapeSignatureInferencePass::run(luci::Module *m) +bool remove_unnecessary_split(luci::CircleNode *node) { - bool changed = false; + auto target_node = dynamic_cast<luci::CircleSplitOut *>(node); + if (target_node == nullptr) + return false; + + auto split_node = dynamic_cast<luci::CircleSplit *>(target_node->input()); + if (split_node == nullptr) + return false; - for (size_t g = 0; g < m->size(); ++g) + if (loco::succs(split_node).size() != 1) + return false; + + if (split_node->num_split() == 1) { - if (run(m->graph(g))) - changed = true; + auto input_node = loco::must_cast<luci::CircleNode *>(split_node->input()); + replace(target_node).with(input_node); + return true; } - - return changed; + return false; } -bool ShapeSignatureInferencePass::run(loco::Graph *g) +} // namespace + +namespace luci { - luci::ssinf::Rule signature_inference_rule; - bool changed = false; - for (auto node : loco::postorder_traversal(loco::output_nodes(g))) +bool RemoveUnnecessarySplitPass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) { - luci::ShapeSignature shape_signature; - auto circle_node = loco::must_cast<luci::CircleNode *>(node); - if (signature_inference_rule.infer(circle_node, shape_signature)) + if (remove_unnecessary_split(circle_node)) { - if (!(circle_node->shape_signature() == shape_signature)) - { - circle_node->shape_signature(shape_signature); - changed = true; - } + changed = true; } } - return changed; } diff --git a/compiler/luci/pass/src/RemoveUnnecessarySplitPass.test.cpp b/compiler/luci/pass/src/RemoveUnnecessarySplitPass.test.cpp new file mode 100644 index 000000000..f292b5357 --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessarySplitPass.test.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2020 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/Pass/RemoveUnnecessarySplitPass.h" + +#include <luci/IR/CircleNodes.h> + +#include <luci/test/TestIOGraph.h> +#include "test/TestFirstNode.h" + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SplitGraphlet +{ +public: + SplitGraphlet() = default; + +public: + void init(loco::Graph *g, uint32_t nout) + { + assert(nout == 1 || nout == 2); + + _dim = g->nodes()->create<luci::CircleConst>(); + set_shape_vector(_dim, {0}); + _dim->name("dim"); + + _split = g->nodes()->create<luci::CircleSplit>(); + _split->num_split(nout); + _split->name("split"); + + _split_out_0 = g->nodes()->create<luci::CircleSplitOut>(); + _split_out_0->index(0); + _split_out_0->name("split_out_0"); + + if (nout == 2) + { + _split_out_1 = g->nodes()->create<luci::CircleSplitOut>(); + _split_out_1->index(1); + _split_out_1->name("split_out_1"); + } + } + +protected: + luci::CircleSplit *_split = nullptr; + luci::CircleConst *_dim = nullptr; + luci::CircleSplitOut *_split_out_0 = nullptr; + luci::CircleSplitOut *_split_out_1 = nullptr; +}; + +class SplitOneGraph : public TestIGraphlet, public TestOGraphlet, public SplitGraphlet +{ +public: + SplitOneGraph() = default; + +public: + void init() + { + TestIGraphlet::init(g(), {1}); + TestOGraphlet::init(g(), {1}); + SplitGraphlet::init(g(), 1); + + _split->input(input()); + _split->split_dim(_dim); + _split_out_0->input(_split); + + output()->from(_split_out_0); + } +}; + +class SplitTwoGraph : public TestIGraphlet, public TestOsGraphlet<2>, public SplitGraphlet +{ +public: + SplitTwoGraph() = default; + +public: + void init() + { + TestIGraphlet::init(g(), {1}); + TestOsGraphlet<2>::init(g(), {{1}, {1}}); + SplitGraphlet::init(g(), 2); + + _split->input(input()); + _split->split_dim(_dim); + _split_out_0->input(_split); + _split_out_1->input(_split); + + output(0)->from(_split_out_0); + output(1)->from(_split_out_1); + } +}; + +// TODO use ::testing::Test + +} // namespace + +TEST(RemoveUnnecessarySplitPass, name) +{ + luci::RemoveUnnecessarySplitPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST(RemoveUnnecessarySplitPass, create_unnecessary_split) +{ + SplitOneGraph g; + + g.init(); + + luci::RemoveUnnecessarySplitPass pass; + while (pass.run(g.g())) + ; + + auto split_node = luci::test::first_node<luci::CircleSplit>(g.g()); + // No Split node is in graph. + ASSERT_EQ(nullptr, split_node); +} + +TEST(RemoveUnnecessarySplitPass, create_unnecessary_split_NEG) +{ + SplitTwoGraph g; + + g.init(); + + luci::RemoveUnnecessarySplitPass pass; + while (pass.run(g.g())) + ; + + auto split_node = luci::test::first_node<luci::CircleSplit>(g.g()); + // Split node is in graph. + ASSERT_NE(nullptr, split_node); +} diff --git a/compiler/luci/pass/src/RemoveUnnecessaryStridedSlicePass.cpp b/compiler/luci/pass/src/RemoveUnnecessaryStridedSlicePass.cpp new file mode 100644 index 000000000..22b1aa64f --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessaryStridedSlicePass.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2021 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/Pass/RemoveUnnecessaryStridedSlicePass.h" + +#include <luci/IR/CircleNodes.h> + +namespace +{ + +/** + * @brief Return value in CircleConst. + * @details Return value in position on CircleConst with int64 format. + */ +int64_t value_from_circle_const(const luci::CircleConst *node, uint32_t idx) +{ + assert(node->rank() == 1 && node->dim(0).value() > idx); + assert(node->dtype() == loco::DataType::S64 || node->dtype() == loco::DataType::S32); + + if (node->dtype() == loco::DataType::S64) + return node->at<loco::DataType::S64>(idx); + return static_cast<int64_t>(node->at<loco::DataType::S32>(idx)); +} + +bool remove_no_effect_strided_slice(luci::CircleStridedSlice *target_node) +{ + auto begin_const = dynamic_cast<luci::CircleConst *>(target_node->begin()); + if (begin_const == nullptr) + return false; + + auto strides_const = dynamic_cast<luci::CircleConst *>(target_node->strides()); + if (strides_const == nullptr) + return false; + + auto end_const = dynamic_cast<luci::CircleConst *>(target_node->end()); + if (end_const == nullptr) + return false; + + auto input_node = loco::must_cast<luci::CircleNode *>(target_node->input()); + for (uint32_t i = 0; i < input_node->rank(); i++) + { + if (value_from_circle_const(begin_const, i) != 0) + return false; + + int64_t strides_value = value_from_circle_const(strides_const, i); + if (strides_value != 1) + return false; + + int64_t end_value = value_from_circle_const(end_const, i); + if (end_value == -1) + continue; + + if (end_value != input_node->dim(i).value()) + return false; + + if (!input_node->dim(i).known()) + return false; + } + + /** + * We check additional attributes on zero after shapes + * for skipping wrong StridedSlice operator. + */ + if (target_node->new_axis_mask() != 0 || target_node->shrink_axis_mask() != 0) + return false; + + replace(target_node).with(input_node); + return true; +} + +} // namespace + +namespace luci +{ +/** + * BEFORE + * + * [CircleNode] + * | + * [CircleStridedSlice] + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode] + * | + * [CircleNode] [CircleStridedSlice] + * + * StridedSlice OP has no effect if, + * 1. Static Shape : begin_const[idx] is 0 AND strides_const[idx] is (not 1 OR + * input_dimension[idx]) + * 2. Dynamic Shape : begin_const[idx] is 0 AND strides_const[idx] is not 1 + * + * StridedSlice OP has effect if, + * 1. begin_const[idx] is 0 AND input_shape[idx] are equal to end_shape[idx] + */ +bool RemoveUnnecessaryStridedSlicePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto target_node = dynamic_cast<luci::CircleStridedSlice *>(node); + if (target_node != nullptr) + if (remove_no_effect_strided_slice(target_node)) + changed = true; + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/RemoveUnnecessaryStridedSlicePass.test.cpp b/compiler/luci/pass/src/RemoveUnnecessaryStridedSlicePass.test.cpp new file mode 100644 index 000000000..7d611c864 --- /dev/null +++ b/compiler/luci/pass/src/RemoveUnnecessaryStridedSlicePass.test.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 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/Pass/RemoveUnnecessaryStridedSlicePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <luci/test/TestIOGraph.h> +#include "test/TestFirstNode.h" + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class StridedSliceGraphlet +{ +public: + StridedSliceGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape, bool remove) + { + // Begin create + _begin = g->nodes()->create<luci::CircleConst>(); + _begin->rank(1); + _begin->dim(0).set(input_shape.size()); + _begin->shape_status(luci::ShapeStatus::VALID); + _begin->dtype(loco::DataType::S32); + _begin->size<loco::DataType::S32>(input_shape.size()); + for (int i = 0; i < input_shape.size(); ++i) + { + _begin->at<loco::DataType::S32>(i) = remove ? 0 : 1; + } + + // Strides create + _strides = g->nodes()->create<luci::CircleConst>(); + _strides->rank(1); + _strides->dim(0).set(input_shape.size()); + _strides->shape_status(luci::ShapeStatus::VALID); + _strides->dtype(loco::DataType::S32); + _strides->size<loco::DataType::S32>(input_shape.size()); + for (int i = 0; i < input_shape.size(); ++i) + { + _strides->at<loco::DataType::S32>(i) = remove ? 1 : -1; + } + + std::vector<uint32_t> shape_vector{input_shape}; + + _end = g->nodes()->create<luci::CircleConst>(); + _end->rank(1); + _end->dim(0).set(input_shape.size()); + _end->shape_status(luci::ShapeStatus::VALID); + _end->dtype(loco::DataType::S32); + _end->size<loco::DataType::S32>(input_shape.size()); + for (int i = 0; i < input_shape.size(); ++i) + { + if (remove) + _end->at<loco::DataType::S32>(i) = static_cast<int32_t>(shape_vector.at(i)); + else + _end->at<loco::DataType::S32>(i) = -1; + } + + // StridedSlice Node create + _strided_slice = g->nodes()->create<luci::CircleStridedSlice>(); + _strided_slice->dtype(loco::DataType::S32); + } + +protected: + luci::CircleStridedSlice *_strided_slice = nullptr; + luci::CircleConst *_begin = nullptr; + luci::CircleConst *_strides = nullptr; + luci::CircleConst *_end = nullptr; +}; + +class StridedSliceGraph : public TestIOGraph, public StridedSliceGraphlet +{ +public: + StridedSliceGraph() = default; + +public: + void init(const ShapeU32 shape, bool remove) + { + TestIOGraph::init(shape, shape); + StridedSliceGraphlet::init(g(), shape, remove); + + _strided_slice->input(input()); + _strided_slice->begin(_begin); + _strided_slice->strides(_strides); + _strided_slice->end(_end); + + output()->from(_strided_slice); + } +}; + +} // namespace + +TEST(RemoveUnnecessaryStridedSlicePass, basic_case) +{ + StridedSliceGraph g; + + g.init({2, 4, 2, 3}, true); + + auto strided_slice_node = luci::test::first_node<luci::CircleStridedSlice>(g.g()); + ASSERT_NE(nullptr, strided_slice_node); + luci::RemoveUnnecessaryStridedSlicePass pass; + while (pass.run(g.g())) + ; + + strided_slice_node = luci::test::first_node<luci::CircleStridedSlice>(g.g()); + ASSERT_EQ(nullptr, strided_slice_node); +} + +TEST(RemoveUnnecessaryStridedSlicePass, basic_fail_case_NEG) +{ + StridedSliceGraph g; + + g.init({2, 4, 2, 3}, false); + + auto strided_slice_node = luci::test::first_node<luci::CircleStridedSlice>(g.g()); + ASSERT_NE(nullptr, strided_slice_node); + luci::RemoveUnnecessaryStridedSlicePass pass; + while (pass.run(g.g())) + ; + + strided_slice_node = luci::test::first_node<luci::CircleStridedSlice>(g.g()); + ASSERT_NE(nullptr, strided_slice_node); +} diff --git a/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.cpp b/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.cpp index 7096c2591..a0cc0194f 100644 --- a/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.cpp +++ b/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.cpp @@ -16,7 +16,10 @@ #include "luci/Pass/ReplaceMulAddWithDepthwiseConvPass.h" +#include "BatchNormPatternFinder.h" + #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { @@ -26,6 +29,9 @@ luci::CircleConst *create_weights_from_gamma(luci::CircleConst *gamma) assert(gamma->rank() == 1); auto channel_size = gamma->dim(0).value(); + auto name = gamma->name(); + assert(name.length() > 0); + // Channel-wise MUL is the same as DEPTHWISE_CONV2D with filter shape (1,1,1,channel_size) auto weights = gamma->graph()->nodes()->create<luci::CircleConst>(); weights->dtype(loco::DataType::FLOAT32); @@ -40,6 +46,7 @@ luci::CircleConst *create_weights_from_gamma(luci::CircleConst *gamma) { weights->at<loco::DataType::FLOAT32>(i) = gamma->at<loco::DataType::FLOAT32>(i); } + weights->name(name + "_weights"); return weights; } @@ -49,6 +56,9 @@ luci::CircleConst *create_bias_from_beta(luci::CircleConst *beta) assert(beta->rank() == 1); auto channel_size = beta->dim(0).value(); + auto name = beta->name(); + assert(name.length() > 0); + // Channel-wise ADD is the same as bias (shape = (channel_size)) of DEPTHWISE_CONV2D auto bias = beta->graph()->nodes()->create<luci::CircleConst>(); bias->dtype(loco::DataType::FLOAT32); @@ -60,83 +70,11 @@ luci::CircleConst *create_bias_from_beta(luci::CircleConst *beta) { bias->at<loco::DataType::FLOAT32>(i) = beta->at<loco::DataType::FLOAT32>(i); } + bias->name(name + "_bias"); return bias; } -bool is_batchnorm_add(const luci::CircleAdd *add, luci::CircleMul *&mul, luci::CircleConst *&beta) -{ - auto x = loco::must_cast<luci::CircleNode *>(add->x()); - auto y = loco::must_cast<luci::CircleNode *>(add->y()); - - luci::CircleMul *pred = nullptr; - luci::CircleConst *constant = nullptr; - - if (x->opcode() == luci::CircleOpcode::CIRCLECONST && y->opcode() == luci::CircleOpcode::MUL) - { - pred = loco::must_cast<luci::CircleMul *>(y); - constant = loco::must_cast<luci::CircleConst *>(x); - } - else if (x->opcode() == luci::CircleOpcode::MUL && y->opcode() == luci::CircleOpcode::CIRCLECONST) - { - pred = loco::must_cast<luci::CircleMul *>(x); - constant = loco::must_cast<luci::CircleConst *>(y); - } - else - { - return false; - } - - if (constant->rank() != 1) - return false; - - auto channel_dim = constant->dim(0); - // Assumption: Layout is channel-last - if (!(channel_dim == add->dim(add->rank() - 1))) - return false; - - mul = pred; - beta = constant; - return true; -} - -// Check if mul is batchnorm mul -bool is_batchnorm_mul(const luci::CircleMul *mul, luci::CircleNode *&pred_node, - luci::CircleConst *&gamma) -{ - auto x = dynamic_cast<luci::CircleConst *>(mul->x()); - auto y = dynamic_cast<luci::CircleConst *>(mul->y()); - - luci::CircleNode *pred = nullptr; - luci::CircleConst *constant = nullptr; - - if (x != nullptr && y == nullptr) - { - pred = loco::must_cast<luci::CircleNode *>(mul->y()); - constant = x; - } - else if (x == nullptr && y != nullptr) - { - pred = loco::must_cast<luci::CircleNode *>(mul->x()); - constant = y; - } - else - { - return false; - } - - if (constant->rank() != 1) - return false; - - auto channel_dim = constant->dim(0); - if (!(channel_dim == mul->dim(mul->rank() - 1))) - return false; - - pred_node = pred; - gamma = constant; - return true; -} - /** * Replace channel-wise Mul/Add with DepthwiseConv2D * @@ -180,6 +118,9 @@ bool replace_mul_add_with_dwconv(luci::CircleAdd *add) auto weights = create_weights_from_gamma(gamma); auto bias = create_bias_from_beta(beta); + auto name = add->name(); + assert(name.length() > 0); + auto dwconv = add->graph()->nodes()->create<luci::CircleDepthwiseConv2D>(); dwconv->input(pred_node); dwconv->filter(weights); @@ -191,6 +132,8 @@ bool replace_mul_add_with_dwconv(luci::CircleAdd *add) dwconv->dilation()->w(1); dwconv->dilation()->h(1); dwconv->fusedActivationFunction(add->fusedActivationFunction()); + dwconv->name(name + "/DepthwiseConv2D"); + luci::add_origin(dwconv, luci::composite_origin({luci::get_origin(mul), luci::get_origin(add)})); loco::replace(add).with(dwconv); return true; @@ -206,14 +149,10 @@ bool ReplaceMulAddWithDepthwiseConvPass::run(loco::Graph *g) bool changed = false; for (auto node : loco::active_nodes(loco::output_nodes(g))) { - auto add = dynamic_cast<luci::CircleAdd *>(node); - if (not add) - continue; - - if (replace_mul_add_with_dwconv(add)) + if (auto add = dynamic_cast<luci::CircleAdd *>(node)) { - changed = true; - break; + if (replace_mul_add_with_dwconv(add)) + changed = true; } } diff --git a/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.test.cpp b/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.test.cpp index a90182aaa..903d4dcc9 100644 --- a/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.test.cpp +++ b/compiler/luci/pass/src/ReplaceMulAddWithDepthwiseConvPass.test.cpp @@ -85,6 +85,13 @@ public: add->x(mul); add->y(beta); output->from(add); + + input->name("input"); + mul->name("mul"); + gamma->name("gamma"); + add->name("add"); + beta->name("beta"); + output->name("output"); } public: @@ -99,6 +106,13 @@ public: } // namespace +TEST(ReplaceMulAddWithDepthwiseConv, name) +{ + luci::ReplaceMulAddWithDepthwiseConvPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + TEST(ReplaceMulAddWithDepthwiseConv, simple) { SimpleGraph g; diff --git a/compiler/luci/pass/src/RequantizePass.cpp b/compiler/luci/pass/src/RequantizePass.cpp index fe84e3bc3..a56536251 100644 --- a/compiler/luci/pass/src/RequantizePass.cpp +++ b/compiler/luci/pass/src/RequantizePass.cpp @@ -113,7 +113,7 @@ void requant_const_int8_to_uint8(CircleConst *node) struct RequantizeNonConst final : public luci::CircleNodeMutableVisitor<bool> { RequantizeNonConst(loco::DataType input, loco::DataType output) - : _input_type(input), _output_type(output) + : _input_type(input), _output_type(output) { } @@ -157,7 +157,7 @@ struct RequantizeNonConst final : public luci::CircleNodeMutableVisitor<bool> struct RequantizeConst final : public luci::CircleNodeMutableVisitor<bool> { RequantizeConst(loco::DataType input, loco::DataType output) - : _input_type(input), _output_type(output) + : _input_type(input), _output_type(output) { } diff --git a/compiler/luci/pass/src/RequantizePass.test.cpp b/compiler/luci/pass/src/RequantizePass.test.cpp new file mode 100644 index 000000000..d26743c9d --- /dev/null +++ b/compiler/luci/pass/src/RequantizePass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/RequantizePass.h" + +#include <gtest/gtest.h> + +TEST(RequantizePassTest, name) +{ + luci::RequantizePass pass(loco::DataType::FLOAT32, loco::DataType::U8); + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/ResolveCustomOpAddPass.cpp b/compiler/luci/pass/src/ResolveCustomOpAddPass.cpp index e52d667d7..1737e5dd6 100644 --- a/compiler/luci/pass/src/ResolveCustomOpAddPass.cpp +++ b/compiler/luci/pass/src/ResolveCustomOpAddPass.cpp @@ -20,6 +20,7 @@ #include <luci/IR/CircleNodes.h> #include <luci/IR/AttrFusedActFunc.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { @@ -67,10 +68,17 @@ bool resolve_with_BroadcastTo(luci::CircleCustom *addv2) auto input = loco::must_cast<const luci::CircleCustomOut *>(addv2->inputs(broadcastTo_idx)); auto broadcastTo = loco::must_cast<luci::CircleCustom *>(input->input()); + auto name = addv2->name(); + assert(name.length() > 0); + auto add = addv2->graph()->nodes()->create<luci::CircleAdd>(); add->fusedActivationFunction(luci::FusedActFunc::NONE); add->x(addv2->inputs(1 - broadcastTo_idx)); add->y(broadcastTo->inputs(0)); + add->name(name + "/Add"); + luci::add_origin( + add, luci::composite_origin({luci::get_origin(broadcastTo), luci::get_origin(addv2)})); + auto customOut = loco::succs(addv2); assert(customOut.size() == 1); replace(*customOut.begin()).with(add); @@ -86,13 +94,39 @@ bool resolve_custom_op(luci::CircleCustom *addv2) if (custom_code != "AddV2") return false; + if (addv2->numInputs() != 2) + return false; + + // check if inputs are suppport data types + for (uint32_t i = 0; i < addv2->numInputs(); i++) + { + auto input = loco::must_cast<luci::CircleNode *>(addv2->inputs(i)); + switch (input->dtype()) + { + case loco::DataType::U8: + case loco::DataType::S8: + case loco::DataType::S16: + case loco::DataType::S32: + case loco::DataType::FLOAT32: + break; + default: + return false; + } + } + if (resolve_with_BroadcastTo(addv2)) return true; + auto name = addv2->name(); + assert(name.length() > 0); + auto add = addv2->graph()->nodes()->create<luci::CircleAdd>(); add->fusedActivationFunction(luci::FusedActFunc::NONE); add->x(addv2->inputs(0)); add->y(addv2->inputs(1)); + add->name(name + "/Add"); + luci::add_origin(add, luci::get_origin(addv2)); + auto customOut = loco::succs(addv2); assert(customOut.size() == 1); replace(*customOut.begin()).with(add); @@ -115,7 +149,8 @@ bool ResolveCustomOpAddPass::run(loco::Graph *g) if (not cop) continue; - changed |= resolve_custom_op(cop); + if (resolve_custom_op(cop)) + changed = true; } return changed; diff --git a/compiler/luci/pass/src/ResolveCustomOpAddPass.test.cpp b/compiler/luci/pass/src/ResolveCustomOpAddPass.test.cpp new file mode 100644 index 000000000..31c245b0e --- /dev/null +++ b/compiler/luci/pass/src/ResolveCustomOpAddPass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/ResolveCustomOpAddPass.h" + +#include <gtest/gtest.h> + +TEST(ResolveCustomOpAddPassTest, name) +{ + luci::ResolveCustomOpAddPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp b/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp index 145e9cb62..5e9466a63 100644 --- a/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp +++ b/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.cpp @@ -19,6 +19,7 @@ #include "flatbuffers/flexbuffers.h" #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { @@ -30,6 +31,9 @@ bool resolve_custom_op(luci::CircleCustom *cop) if (custom_code == "BatchMatMulV2") { + auto name = cop->name(); + assert(name.length() > 0); + auto batch_matmul = cop->graph()->nodes()->create<luci::CircleBatchMatMul>(); // input batch_matmul->x(cop->inputs(0)); @@ -39,10 +43,16 @@ bool resolve_custom_op(luci::CircleCustom *cop) auto map = flexbuffers::GetRoot(custom_options).AsMap(); batch_matmul->adj_x(map["adj_x"].AsBool()); batch_matmul->adj_y(map["adj_y"].AsBool()); + batch_matmul->name(name + "/BatchMatMul"); + luci::add_origin(batch_matmul, luci::get_origin(cop)); + + auto customOut = loco::succs(cop); + assert(customOut.size() == 1); + replace(*customOut.begin()).with(batch_matmul); - replace(cop).with(batch_matmul); return true; } + return false; } @@ -51,6 +61,27 @@ bool resolve_custom_op(luci::CircleCustom *cop) namespace luci { +/** + * BEFORE + * | | + * [CircleNode] [CircleNode] + * \ / + * [CircleCustom]("BatchMatMulV2") + * | + * [CircleCustomOut] + * | + * [CircleNode] + * | + * + * AFTER + * | | + * [CircleNode] [CircleNode] + * \ / + * [CircleBatchMatMul] + * | + * [CircleNode] + * | + */ bool ResolveCustomOpBatchMatMulPass::run(loco::Graph *g) { bool changed = false; @@ -60,7 +91,8 @@ bool ResolveCustomOpBatchMatMulPass::run(loco::Graph *g) if (not cop) continue; - changed |= resolve_custom_op(cop); + if (resolve_custom_op(cop)) + changed = true; } return changed; diff --git a/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.test.cpp b/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.test.cpp new file mode 100644 index 000000000..435016f9d --- /dev/null +++ b/compiler/luci/pass/src/ResolveCustomOpBatchMatMulPass.test.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2021 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/Pass/ResolveCustomOpBatchMatMulPass.h" + +#include <luci/IR/CircleNodes.h> + +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/flexbuffers.h" + +#include <luci/test/TestIOGraph.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +const int N = 1; +const int C = 2; +const int H_X = 1; +const int W_X = 4; +const int H_Y = 4; +const int W_Y = 4; + +/** + * graph having Custom operator BatchMatMulV2 + * + * [CircleInput] [CircleInput] + * \ / + * [CircleCustom] + * | + * [CircleCustomOut] + * | + * [CircleOutput] + */ +class BatchMatmulV2Graphlet +{ +public: + BatchMatmulV2Graphlet() = default; + +public: + void init(loco::Graph *g) + { + // custom option + auto flatbuffer_builder = + std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024)); + auto flex_buffers = std::make_unique<flexbuffers::Builder>(); + size_t map_start = flex_buffers->StartMap(); + flex_buffers->Bool("adj_x", false); + flex_buffers->Bool("adj_y", false); + flex_buffers->Int("T", 0 /* circle::TensorType_FLOAT32 */); + flex_buffers->EndMap(map_start); + flex_buffers->Finish(); + + // CircleCustom(BatchMatMulV2, adj_x=False, adj_y=False) + _batchmatmulv2 = g->nodes()->create<luci::CircleCustom>(2, 1); + _batchmatmulv2->custom_code("BatchMatMulV2"); + _batchmatmulv2->custom_options(flex_buffers->GetBuffer()); + _batchmatmulv2->shape({N, C, H_X, W_Y}); + _batchmatmulv2->dtype(loco::DataType::FLOAT32); + _batchmatmulv2->name("batchmatmulv2"); + + // CircleCustomOut + _batchmatmulv2_out = g->nodes()->create<luci::CircleCustomOut>(); + _batchmatmulv2_out->shape({N, C, H_X, W_Y}); + _batchmatmulv2_out->dtype(loco::DataType::FLOAT32); + _batchmatmulv2_out->index(0); + } + +public: + luci::CircleCustom *batchmatmulv2() { return _batchmatmulv2; } + +protected: + luci::CircleCustom *_batchmatmulv2 = nullptr; + luci::CircleCustomOut *_batchmatmulv2_out = nullptr; +}; + +class BatchMatmulV2Graph : public TestIsGraphlet<2>, + public TestOGraphlet, + public BatchMatmulV2Graphlet +{ +public: + BatchMatmulV2Graph() = default; + + void init(void) + { + TestIsGraphlet<2>::init(g(), {{N, C, H_X, W_X}, {N, C, H_X, W_X}}); + TestOGraphlet::init(g(), {N, C, H_X, W_Y}); + BatchMatmulV2Graphlet::init(g()); + + // TODO how set multiple of shape vector for TestIsGraphlet? + // update shape for second input + input(1)->shape({N, C, H_Y, W_Y}); + + // connect graph + _batchmatmulv2->inputs(0, input(0)); + _batchmatmulv2->inputs(1, input(1)); + _batchmatmulv2_out->input(_batchmatmulv2); + + output()->from(_batchmatmulv2_out); + } +}; + +class BatchMatmulV2GraphTest : public ::testing::Test +{ +public: + BatchMatmulV2Graph g; + luci::ResolveCustomOpBatchMatMulPass pass; +}; + +} // namespace + +TEST(ResolveCustomOpBatchMatMulPassTest, name) +{ + luci::ResolveCustomOpBatchMatMulPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +/** + * Optimized graph looks like below. + * + * [CircleInput] + * | + * [CircleBatchMatMul] + * | + * [CircleOutput] + */ +TEST_F(BatchMatmulV2GraphTest, simple_test) +{ + g.init(); + + auto ret = pass.run(g.g()); + EXPECT_EQ(true, ret); + + auto batchmatmul = dynamic_cast<luci::CircleBatchMatMul *>(g.output()->from()); + EXPECT_NE(nullptr, batchmatmul); + + auto input_0 = dynamic_cast<luci::CircleInput *>(batchmatmul->x()); + auto input_1 = dynamic_cast<luci::CircleInput *>(batchmatmul->y()); + EXPECT_NE(nullptr, input_0); + EXPECT_NE(nullptr, input_1); +} + +TEST_F(BatchMatmulV2GraphTest, wrong_condition_NEG) +{ + g.init(); + + // wrong custom code + g.batchmatmulv2()->custom_code("BatchMatMulv2"); // v is lower case + auto ret = pass.run(g.g()); + + EXPECT_EQ(false, ret); +} diff --git a/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp index 547fd22fc..216778066 100644 --- a/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp +++ b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.cpp @@ -20,11 +20,10 @@ #include <loco/IR/DataTypeTraits.h> #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> #include <loco.h> #include <oops/InternalExn.h> -#include <loco/Service/ShapeInference.h> -#include <loco/Service/TypeInference.h> namespace { @@ -44,6 +43,7 @@ luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype, node->dim(i) = shape.at(i); size *= shape.at(i); } + node->shape_status(luci::ShapeStatus::VALID); #define INIT_VALUES(DT) \ { \ @@ -90,6 +90,9 @@ bool resolve_matmul(luci::CircleCustom *cop) const auto S32 = loco::DataType::S32; const auto FLOAT32 = loco::DataType::FLOAT32; + auto name = cop->name(); + assert(name.length() > 0); + bool transpose_a = map["transpose_a"].AsBool(); bool transpose_b = map["transpose_b"].AsBool(); @@ -97,34 +100,38 @@ bool resolve_matmul(luci::CircleCustom *cop) loco::Node *rhs = cop->inputs(1); // Check that the type of the first input is known - CHECK_OR_FALSE(loco::dtype_known(lhs)); - auto lhs_dtype = loco::dtype_get(cop->inputs(0)); + auto lhs_dtype = loco::must_cast<luci::CircleNode *>(cop->inputs(0))->dtype(); + CHECK_OR_FALSE(lhs_dtype != loco::DataType::Unknown); // If transpose of first input is requested, its shape must be known - CHECK_OR_FALSE(!transpose_a || loco::shape_known(lhs)); + auto circle_lhs = loco::must_cast<luci::CircleNode *>(lhs); + CHECK_OR_FALSE(!transpose_a || circle_lhs->shape_status() == luci::ShapeStatus::VALID); // and its rank should be at least 2 - CHECK_OR_FALSE(!transpose_a || loco::shape_get(lhs).as<loco::TensorShape>().rank() >= 2); + CHECK_OR_FALSE(!transpose_a || circle_lhs->rank() >= 2); // Check that the shape of the 2nd input is known - CHECK_OR_FALSE(loco::shape_known(rhs)); + auto circle_rhs = loco::must_cast<luci::CircleNode *>(rhs); + CHECK_OR_FALSE(circle_rhs->shape_status() == luci::ShapeStatus::VALID); // TODO as of 06/23/20 TFLite only supports rank 2 for 2nd input. Fix this once that changes! - CHECK_OR_FALSE(loco::shape_get(rhs).as<loco::TensorShape>().rank() == 2); + CHECK_OR_FALSE(circle_rhs->rank() == 2); // Check that input data type is supported CHECK_OR_THROW(lhs_dtype == U8 || lhs_dtype == S16 || lhs_dtype == FLOAT32, "Only UInt8, Int16 and Float32 data types are supported by MatMul"); if (transpose_a) { - auto a_shape = loco::shape_get(lhs).as<loco::TensorShape>(); // Create a permutation constant node std::vector<uint32_t> perm; - for (uint32_t i = 0; i < a_shape.rank(); ++i) + for (uint32_t i = 0; i < circle_lhs->rank(); ++i) perm.push_back(i); - std::swap(perm[a_shape.rank() - 1], perm[a_shape.rank() - 2]); - auto perm_node = create_const_node(graph, S32, {a_shape.rank()}, perm); + std::swap(perm[circle_lhs->rank() - 1], perm[circle_lhs->rank() - 2]); + auto perm_node = create_const_node(graph, S32, {circle_lhs->rank()}, perm); + perm_node->name(name + "/lhs/Transpose/perm"); // Now make a transpose node auto transpose_node = graph->nodes()->create<luci::CircleTranspose>(); transpose_node->a(lhs); transpose_node->perm(perm_node); + transpose_node->name(name + "/lhs/Transpose"); + luci::add_origin(transpose_node, luci::get_origin(cop)); lhs = transpose_node; } @@ -135,24 +142,29 @@ bool resolve_matmul(luci::CircleCustom *cop) { const std::vector<uint32_t> perm{1, 0}; auto perm_node = create_const_node(graph, S32, {2}, perm); + perm_node->name(name + "/rhs/Transpose/perm"); auto transpose_node = graph->nodes()->create<luci::CircleTranspose>(); transpose_node->a(rhs); transpose_node->perm(perm_node); + transpose_node->name(name + "/rhs/Transpose"); + luci::add_origin(transpose_node, luci::get_origin(cop)); rhs = transpose_node; } - // Make a constant zero-filled bias node - auto b_shape = loco::shape_get(cop->inputs(1)).as<loco::TensorShape>(); - uint32_t bias_size = b_shape.dim(transpose_b ? 1 : 0).value(); - const std::vector<float> val(bias_size, .0f); - auto bias_node = create_const_node(graph, lhs_dtype, {bias_size}, val); + auto empty_bias = graph->nodes()->create<luci::CircleOutputExclude>(); + empty_bias->dtype(loco::DataType::FLOAT32); // Needed for type inference + auto fc_node = graph->nodes()->create<luci::CircleFullyConnected>(); fc_node->input(lhs); fc_node->weights(rhs); - fc_node->bias(bias_node); + fc_node->bias(empty_bias); fc_node->fusedActivationFunction(luci::FusedActFunc::NONE); + fc_node->name(name + "/FullyConnected"); + luci::add_origin(fc_node, luci::get_origin(cop)); - replace(cop).with(fc_node); + auto customOut = loco::succs(cop); + assert(customOut.size() == 1); + replace(*customOut.begin()).with(fc_node); return true; } diff --git a/compiler/luci/pass/src/ResolveCustomOpMatMulPass.test.cpp b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.test.cpp new file mode 100644 index 000000000..c4ea3ea06 --- /dev/null +++ b/compiler/luci/pass/src/ResolveCustomOpMatMulPass.test.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 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/Pass/ResolveCustomOpMatMulPass.h" + +#include <gtest/gtest.h> + +TEST(ResolveCustomOpMatMulPassTest, name) +{ + luci::ResolveCustomOpMatMulPass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/ShapeInferencePass.cpp b/compiler/luci/pass/src/ShapeInferencePass.cpp deleted file mode 100644 index 4bd0aaed4..000000000 --- a/compiler/luci/pass/src/ShapeInferencePass.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020 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/Pass/ShapeInferencePass.h" - -#include <luci/IR/CircleDialect.h> -#include <luci/Service/CircleShapeInferenceRule.h> - -#include <loco.h> -#include <loco/IR/CanonicalDialect.h> -#include <loco/Service/CanonicalShapeInferenceRule.h> -#include <loco/Service/ShapeInference.h> -#include <loco/Service/MultiDialectShapeInferenceRule.h> - -namespace luci -{ - -bool ShapeInferencePass::run(luci::Module *m) -{ - bool changed = false; - - for (size_t g = 0; g < m->size(); ++g) - { - if (run(m->graph(g))) - changed = true; - } - - return changed; -} - -bool ShapeInferencePass::run(loco::Graph *g) -{ - loco::CanonicalShapeInferenceRule canonical_rule; - luci::CircleShapeInferenceRule circle_rule; - - loco::MultiDialectShapeInferenceRule rules; - - rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(luci::CircleDialect::get(), &circle_rule); - - return loco::apply(&rules).to(g); -} - -} // namespace luci diff --git a/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.cpp b/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.cpp index 6a58f18c5..92060f625 100644 --- a/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.cpp +++ b/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.cpp @@ -72,6 +72,9 @@ luci::CircleConst *shuffle_weight(luci::CircleFullyConnected *fc) { auto the_weights = loco::must_cast<luci::CircleConst *>(fc->weights()); + auto name = fc->name(); + assert(name.length() > 0); + // create CircleConst where shuffled data will be stored luci::CircleConst *new_weights = fc->graph()->nodes()->create<luci::CircleConst>(); new_weights->dtype(loco::DataType::FLOAT32); @@ -82,6 +85,7 @@ luci::CircleConst *shuffle_weight(luci::CircleFullyConnected *fc) { new_weights->dim(r).set(the_weights->dim(r).value()); } + new_weights->name(name + "/shuffle_weight"); // suffle weight const uint32_t MULTIPLE = 16; @@ -96,7 +100,7 @@ luci::CircleConst *shuffle_weight(luci::CircleFullyConnected *fc) for (uint32_t i = 0; i < MULTIPLE; i++) { new_weights->at<loco::DataType::FLOAT32>(index++) = - the_weights->at<loco::DataType::FLOAT32>((r * MULTIPLE + i) * cols + c); + the_weights->at<loco::DataType::FLOAT32>((r * MULTIPLE + i) * cols + c); } } } @@ -131,6 +135,8 @@ bool ShuffleWeightTo16x1Float32Pass::run(loco::Graph *g) fc->weights(new_weights); fc->weights_format(luci::CircleFullyConnected::WeightsFormat::SHUFFLED16x1FLOAT32); } + + changed = true; } return changed; diff --git a/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.test.cpp b/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.test.cpp index 9745e5754..077985977 100644 --- a/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.test.cpp +++ b/compiler/luci/pass/src/ShuffleWeightTo16x1Float32Pass.test.cpp @@ -18,61 +18,86 @@ #include <luci/IR/CircleNodes.h> +#include <luci/test/TestIOGraph.h> +#include "test/TestFirstNode.h" + #include <gtest/gtest.h> -void create_fc_net(loco::Graph *g) +namespace { - assert(g); - - const uint32_t ROW = 16; - const uint32_t COL = 2; - const uint32_t elements_num = ROW * COL; - - // input - auto input = g->nodes()->create<luci::CircleInput>(); - auto graph_input = g->inputs()->create(); - input->index(graph_input->index()); - - // fc weights - auto weights = g->nodes()->create<luci::CircleConst>(); - weights->dtype(loco::DataType::FLOAT32); - weights->size<loco::DataType::FLOAT32>(elements_num); - weights->rank(2); - weights->dim(0).set(ROW); - weights->dim(1).set(COL); - for (uint32_t idx = 0; idx < elements_num; idx++) + +using namespace luci::test; + +class FCGraphlet +{ +public: + FCGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 wshape) { - weights->at<loco::DataType::FLOAT32>(idx) = idx; + const uint32_t elements_num = num_elements(wshape); + + // fc weights + _weights = g->nodes()->create<luci::CircleConst>(); + _weights->dtype(loco::DataType::FLOAT32); + _weights->shape(wshape); + _weights->size<loco::DataType::FLOAT32>(elements_num); + for (uint32_t idx = 0; idx < elements_num; idx++) + { + _weights->at<loco::DataType::FLOAT32>(idx) = idx; + } + _weights->name("weights"); + + // fc + _fc = g->nodes()->create<luci::CircleFullyConnected>(); + _fc->dtype(loco::DataType::FLOAT32); + _fc->name("fc"); } - // fc - auto fc = g->nodes()->create<luci::CircleFullyConnected>(); - fc->dtype(loco::DataType::FLOAT32); - fc->input(input); - fc->weights(weights); - - // output - auto output = g->nodes()->create<luci::CircleOutput>(); - output->from(fc); - auto graph_output = g->outputs()->create(); - output->index(graph_output->index()); -} +protected: + luci::CircleFullyConnected *_fc = nullptr; + luci::CircleConst *_weights = nullptr; +}; -TEST(ShuffleWeightTo16x1Float32PassTest, SimpleTest1) +class FCGraph : public TestIGraphlet, public TestOGraphlet, public FCGraphlet { - auto graph = loco::make_graph(); - create_fc_net(graph.get()); +public: + FCGraph() = default; - luci::CircleFullyConnected *fc_node = nullptr; - for (auto node : loco::active_nodes(loco::output_nodes(graph.get()))) + void init(const ShapeU32 shape, const ShapeU32 wshape) { - auto fc = dynamic_cast<luci::CircleFullyConnected *>(node); - if (not fc) - continue; + TestIGraphlet::init(g(), shape); + TestOGraphlet::init(g(), shape); + FCGraphlet::init(g(), wshape); + + // connect graph + _fc->input(input()); + _fc->weights(_weights); - fc_node = fc; - break; + output()->from(_fc); } +}; + +} // namespace + +TEST(ShuffleWeightTo16x1Float32PassTest, name) +{ + luci::ShuffleWeightTo16x1Float32Pass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +const uint32_t ROW = 16; +const uint32_t COL = 2; + +TEST(ShuffleWeightTo16x1Float32PassTest, SimpleTest1) +{ + FCGraph g; + + g.init({ROW, COL}, {ROW, COL}); + + auto fc_node = luci::test::first_node<luci::CircleFullyConnected>(g.g()); ASSERT_NE(fc_node, nullptr); auto weights = loco::must_cast<luci::CircleConst *>(fc_node->weights()); // before @@ -94,7 +119,7 @@ TEST(ShuffleWeightTo16x1Float32PassTest, SimpleTest1) ASSERT_EQ(15, weights->at<loco::DataType::FLOAT32>(15)); luci::ShuffleWeightTo16x1Float32Pass pass; - while (pass.run(graph.get())) + while (pass.run(g.g())) ; weights = loco::must_cast<luci::CircleConst *>(fc_node->weights()); @@ -116,3 +141,33 @@ TEST(ShuffleWeightTo16x1Float32PassTest, SimpleTest1) ASSERT_EQ(28, weights->at<loco::DataType::FLOAT32>(14)); ASSERT_EQ(30, weights->at<loco::DataType::FLOAT32>(15)); } + +TEST(ShuffleWeightTo16x1Float32PassTest, invalid_weight_shape_NEG) +{ + FCGraph g; + + g.init({ROW, COL}, {1, ROW, COL, 1}); + + auto fc_node = luci::test::first_node<luci::CircleFullyConnected>(g.g()); + ASSERT_NE(fc_node, nullptr); + + luci::ShuffleWeightTo16x1Float32Pass pass; + auto ret = pass.run(g.g()); + + ASSERT_FALSE(ret); +} + +TEST(ShuffleWeightTo16x1Float32PassTest, invalid_weight_row16_NEG) +{ + FCGraph g; + + g.init({COL, ROW}, {COL, ROW}); + + auto fc_node = luci::test::first_node<luci::CircleFullyConnected>(g.g()); + ASSERT_NE(fc_node, nullptr); + + luci::ShuffleWeightTo16x1Float32Pass pass; + auto ret = pass.run(g.g()); + + ASSERT_FALSE(ret); +} diff --git a/compiler/luci/pass/src/Sparsifier.cpp b/compiler/luci/pass/src/Sparsifier.cpp index 210c1a34c..18ab45f98 100644 --- a/compiler/luci/pass/src/Sparsifier.cpp +++ b/compiler/luci/pass/src/Sparsifier.cpp @@ -26,8 +26,8 @@ Sparsifier<T>::Sparsifier(const std::vector<int32_t> &shape, const std::vector<DimensionType> &format, const std::vector<int32_t> &block_size, const std::vector<int32_t> &block_map) - : _dense_shape(shape), _traversal_order(traversal_order), _block_size(block_size), - _block_map(block_map) + : _dense_shape(shape), _traversal_order(traversal_order), _block_size(block_size), + _block_map(block_map) { _dense_size = 1; int32_t block_dim = 0; diff --git a/compiler/luci/pass/src/Sparsifier.test.cpp b/compiler/luci/pass/src/Sparsifier.test.cpp index 272e0e934..14e24aad7 100644 --- a/compiler/luci/pass/src/Sparsifier.test.cpp +++ b/compiler/luci/pass/src/Sparsifier.test.cpp @@ -190,6 +190,6 @@ TEST(SparsifierTest, WrongFormatRank_NEG) const std::vector<int32_t> block_size = {4, 1}; const std::vector<int32_t> block_map = {0, 1}; EXPECT_THROW( - luci::Sparsifier<int32_t>(dense_shape, traversal_order, format, block_size, block_map), - std::out_of_range); + luci::Sparsifier<int32_t>(dense_shape, traversal_order, format, block_size, block_map), + std::out_of_range); } diff --git a/compiler/luci/pass/src/SparsifyTensorPass.cpp b/compiler/luci/pass/src/SparsifyTensorPass.cpp index 2f1a36e77..1a75bfb0c 100644 --- a/compiler/luci/pass/src/SparsifyTensorPass.cpp +++ b/compiler/luci/pass/src/SparsifyTensorPass.cpp @@ -69,11 +69,11 @@ template <loco::DataType DT> void SparsifyTensorPass::sparsify_tensor(luci::Circ else if (_format.at(idx) == DimensionType::SPARSE_CSR) { sparsityparam->dim_metadata.emplace_back( - DimensionType::SPARSE_CSR, /* dense size */ 0, - /* array_segments */ SparseIndexVector{SparseIndexVectorType::U16, - dim_metadata.at(idx * 2)}, - /* array_indices */ SparseIndexVector{SparseIndexVectorType::U16, - dim_metadata.at(idx * 2 + 1)}); + DimensionType::SPARSE_CSR, /* dense size */ 0, + /* array_segments */ + SparseIndexVector{SparseIndexVectorType::U16, dim_metadata.at(idx * 2)}, + /* array_indices */ + SparseIndexVector{SparseIndexVectorType::U16, dim_metadata.at(idx * 2 + 1)}); } } for (uint32_t i = 0; i < _block_size.size(); i++) diff --git a/compiler/luci/pass/src/SparsifyTensorPass.test.cpp b/compiler/luci/pass/src/SparsifyTensorPass.test.cpp new file mode 100644 index 000000000..372e8e5ca --- /dev/null +++ b/compiler/luci/pass/src/SparsifyTensorPass.test.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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/Pass/SparsifyTensorPass.h" + +#include <gtest/gtest.h> + +TEST(SparsifyTensorPassTest, name) +{ + std::vector<int32_t> to; + std::vector<luci::DimensionType> vdt; + std::vector<int32_t> bs; + std::vector<int32_t> bm; + luci::SparsifyTensorPass pass("", to, vdt, bs, bm); + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} diff --git a/compiler/luci/pass/src/SubstitutePackToReshapePass.cpp b/compiler/luci/pass/src/SubstitutePackToReshapePass.cpp index 44e974b91..d8676cd62 100644 --- a/compiler/luci/pass/src/SubstitutePackToReshapePass.cpp +++ b/compiler/luci/pass/src/SubstitutePackToReshapePass.cpp @@ -17,10 +17,22 @@ #include "luci/Pass/SubstitutePackToReshapePass.h" #include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> namespace { +int32_t unknown_dim_count(luci::CircleNode *node) +{ + int32_t count = 0; + + for (uint32_t i = 0; i < node->rank(); ++i) + if (!node->dim(i).known()) + ++count; + + return count; +} + bool substitute_pack_to_reshape(luci::CircleNode *node) { auto target_node = dynamic_cast<luci::CirclePack *>(node); @@ -35,9 +47,14 @@ bool substitute_pack_to_reshape(luci::CircleNode *node) if (axis < 0) axis = axis + static_cast<int32_t>(value_node->rank()) + 1; + auto name = node->name(); + assert(name.length() > 0); + auto graph = target_node->graph(); auto reshape_node = graph->nodes()->create<luci::CircleReshape>(); reshape_node->tensor(value_node); + reshape_node->name(name + "/Reshape"); + luci::add_origin(reshape_node, luci::get_origin(node)); auto const_node = graph->nodes()->create<luci::CircleConst>(); const_node->dtype(loco::DataType::S32); @@ -53,13 +70,16 @@ bool substitute_pack_to_reshape(luci::CircleNode *node) } else if (i < axis) { - const_node->at<loco::DataType::S32>(i) = value_node->dim(i).value(); + const_node->at<loco::DataType::S32>(i) = + value_node->dim(i).known() ? value_node->dim(i).value() : -1; } else { - const_node->at<loco::DataType::S32>(i) = value_node->dim(i - 1).value(); + const_node->at<loco::DataType::S32>(i) = + value_node->dim(i - 1).known() ? value_node->dim(i - 1).value() : -1; } } + const_node->name(name + "/Reshape/shape"); reshape_node->shape(const_node); replace(target_node).with(reshape_node); return true; @@ -71,24 +91,23 @@ namespace luci { /** - * BEFORE - * | - * [CircleNode] - * | - * [CirclePack] - * | - * [CircleNode] - * | + * BEFORE + * | + * [CircleNode] + * | + * [CirclePack] + * | + * [CircleNode] + * | * - * AFTER - * | - * [CircleNode] [CircleConst] - * \ / - * [CircleReshape] + * AFTER * | - * [CircleNode] - * | - * + * [CircleNode] [CircleConst] + * | \ / + * [CirclePack] [CircleReshape] + * | + * [CircleNode] + * | */ bool SubstitutePackToReshapePass::run(loco::Graph *g) { @@ -96,7 +115,7 @@ bool SubstitutePackToReshapePass::run(loco::Graph *g) for (auto node : loco::active_nodes(loco::output_nodes(g))) { auto circle_node = loco::must_cast<luci::CircleNode *>(node); - if (substitute_pack_to_reshape(circle_node)) + if (unknown_dim_count(circle_node) <= 1 && substitute_pack_to_reshape(circle_node)) { changed = true; } diff --git a/compiler/luci/pass/src/SubstitutePackToReshapePass.test.cpp b/compiler/luci/pass/src/SubstitutePackToReshapePass.test.cpp index 143b88896..3b5d4ea2c 100644 --- a/compiler/luci/pass/src/SubstitutePackToReshapePass.test.cpp +++ b/compiler/luci/pass/src/SubstitutePackToReshapePass.test.cpp @@ -22,26 +22,6 @@ namespace { -/** - * BEFORE - * | - * [CircleNode] - * | - * [CirclePack] - * | - * [CircleNode] - * | - * - * AFTER - * | - * [CircleNode] [CircleConst] - * \ / - * [CircleReshape] - * | - * [CircleNode] - * | - * - */ void create_substitute_pack_to_reshape(loco::Graph *g, const std::initializer_list<uint32_t> shape, int32_t axis) { @@ -54,23 +34,33 @@ void create_substitute_pack_to_reshape(loco::Graph *g, const std::initializer_li input->shape_status(luci::ShapeStatus::VALID); input->rank(shape.size()); input->shape(shape); + input->name("input"); // Pack Node create. auto pack = g->nodes()->create<luci::CirclePack>(1); pack->values(0, input); pack->axis(axis); + pack->name("pack"); // Output Connect. auto output = g->nodes()->create<luci::CircleOutput>(); output->from(pack); auto graph_output = g->outputs()->create(); output->index(graph_output->index()); + output->name("output"); return; } } // namespace +TEST(SubstitutePackToReshapePassTest, name) +{ + luci::SubstitutePackToReshapePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + TEST(SubstitutePackToReshapePass, simple_case) { auto graph = loco::make_graph(); diff --git a/compiler/luci/pass/src/SubstituteSqueezeToReshapePass.cpp b/compiler/luci/pass/src/SubstituteSqueezeToReshapePass.cpp new file mode 100644 index 000000000..74be86a4c --- /dev/null +++ b/compiler/luci/pass/src/SubstituteSqueezeToReshapePass.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2021 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/Pass/SubstituteSqueezeToReshapePass.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> + +namespace +{ + +/** + * @brief return TRUE if all dim is known + * @note This pass can be applied even some of dimensions are unknown. + For now, do not consider about it and update logic later. + */ +bool can_squeeze_shape(const luci::CircleNode *node) +{ + for (uint32_t r = 0; r < node->rank(); ++r) + { + if (not node->dim(r).known()) + return false; + } + return true; +} + +/** + * @brief return valid unsigned dim value from 0 ~ (rank-1) + * @note dim can be -rank to (rank-1) + */ +uint32_t valid_unsigned_dim(uint32_t rank, int32_t dim) +{ + int32_t irank = static_cast<int32_t>(rank); + return dim >= 0 ? static_cast<uint32_t>(dim) : static_cast<uint32_t>(irank + dim); +} + +/** + * @brief return TRUE if input dim is 1 for squeeze_dims values + */ +bool is_valid_input(const luci::CircleNode *node, const std::vector<int32_t> &squeeze_dims) +{ + auto rank = node->rank(); + for (auto dim : squeeze_dims) + { + auto udim = valid_unsigned_dim(rank, dim); + if (node->dim(udim).value() != 1) + return false; + } + return true; +} + +/** + * @brief return shape vector from input + */ +std::vector<uint32_t> node_shape(const luci::CircleNode *input) +{ + std::vector<uint32_t> shape; + uint32_t rank = input->rank(); + for (uint32_t r = 0; r < rank; ++r) + shape.push_back(input->dim(r).value()); + + return shape; +} + +/** + * @brief return CircleConst ptr with values of new_shape + */ +luci::CircleConst *create_shape_const(loco::Graph *graph, const std::vector<uint32_t> &new_shape) +{ + // NOTE dim_size can be 0 + uint32_t dim_size = static_cast<uint32_t>(new_shape.size()); + + auto shape_const = graph->nodes()->create<luci::CircleConst>(); + + // const shape/dtype + shape_const->dtype(loco::DataType::S32); + if (dim_size > 0) + { + shape_const->rank(1); + shape_const->dim(0).set(dim_size); + } + else + shape_const->rank(0); + shape_const->shape_status(luci::ShapeStatus::VALID); + + // constant values + shape_const->size<loco::DataType::S32>(dim_size); + for (uint32_t i = 0; i < dim_size; ++i) + shape_const->at<loco::DataType::S32>(i) = new_shape.at(i); + + return shape_const; +} + +bool substitute_squeeze_to_reshape(luci::CircleSqueeze *squeeze) +{ + assert(squeeze != nullptr); + + auto input = loco::must_cast<luci::CircleNode *>(squeeze->input()); + // we need input node shape and all dim should be known + if (input->shape_status() != luci::ShapeStatus::VALID) + return false; + if (not can_squeeze_shape(input)) + return false; + + // we will use squeeze shape for new shape + if (squeeze->shape_status() != luci::ShapeStatus::VALID) + return false; + + auto squeeze_dims = squeeze->squeeze_dims(); + if (not is_valid_input(input, squeeze_dims)) + throw std::runtime_error("Invalid values in squeeze_dims: " + squeeze->name()); + + auto name = squeeze->name(); + assert(name.length() > 0); + + auto reshape_shape = node_shape(squeeze); + auto graph = squeeze->graph(); + auto reshape = graph->nodes()->create<luci::CircleReshape>(); + auto shape_const = create_shape_const(graph, reshape_shape); + reshape->name(name + "/Reshape"); + luci::add_origin(reshape, luci::get_origin(squeeze)); + shape_const->name(name + "/Reshape/shape"); + + // graph connection + reshape->tensor(input); + reshape->shape(shape_const); + replace(squeeze).with(reshape); + + return true; +} + +} // namespace + +namespace luci +{ + +/** + * BEFORE + * | + * [CircleNode] + * | + * [CircleSqueeze] + * | + * [CircleNode] + * | + * + * AFTER + * | + * [CircleNode] [CircleConst] + * | \ / + * [CircleSqueeze] [CircleReshape] + * | + * [CircleNode] + * | + */ +bool SubstituteSqueezeToReshapePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto squeeze = dynamic_cast<luci::CircleSqueeze *>(node)) + { + if (substitute_squeeze_to_reshape(squeeze)) + changed = true; + } + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/SubstituteSqueezeToReshapePass.test.cpp b/compiler/luci/pass/src/SubstituteSqueezeToReshapePass.test.cpp new file mode 100644 index 000000000..d917af678 --- /dev/null +++ b/compiler/luci/pass/src/SubstituteSqueezeToReshapePass.test.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2021 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/Pass/SubstituteSqueezeToReshapePass.h" +#include "luci/Pass/CircleShapeInferencePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +using uilist = std::initializer_list<uint32_t>; +using ilist = std::initializer_list<int32_t>; + +class PassTestGraph +{ +public: + PassTestGraph() = default; + +public: + void init(const uilist shape_in, const uilist shape_out) + { + _graph_input = _g.inputs()->create(); + _graph_output = _g.outputs()->create(); + + _input = _g.nodes()->create<luci::CircleInput>(); + _input->shape(shape_in); + _input->shape_status(luci::ShapeStatus::VALID); + _input->name("input"); + + _output = _g.nodes()->create<luci::CircleOutput>(); + _output->shape(shape_out); + _output->shape_status(luci::ShapeStatus::VALID); + _output->name("output"); + + _input->index(_graph_input->index()); + _output->index(_graph_output->index()); + + auto input_shape = std::make_unique<loco::TensorShape>(); + set(input_shape.get(), shape_in); + _graph_input->shape(std::move(input_shape)); + + auto output_shape = std::make_unique<loco::TensorShape>(); + set(output_shape.get(), shape_out); + _graph_output->shape(std::move(output_shape)); + } + +protected: + void set(loco::TensorShape *shape, const uilist &values) + { + uint32_t r = 0; + shape->rank(values.size()); + for (auto v : values) + shape->dim(r++).set(v); + } + +public: + loco::Graph *g(void) { return &_g; } + luci::CircleOutput *output(void) { return _output; } + +protected: + loco::Graph _g; + loco::GraphInput *_graph_input = nullptr; + loco::GraphOutput *_graph_output = nullptr; + luci::CircleInput *_input = nullptr; + luci::CircleOutput *_output = nullptr; +}; + +class SubstituteSqueezeToReshapeGraph : public PassTestGraph +{ +public: + SubstituteSqueezeToReshapeGraph() = default; + +public: + void init(const uilist shape_in, const uilist shape_out, const ilist squeeze_dims) + { + PassTestGraph::init(shape_in, shape_out); + + _squeeze = _g.nodes()->create<luci::CircleSqueeze>(); + _squeeze->input(_input); + _squeeze->squeeze_dims(squeeze_dims); + _squeeze->name("squeeze"); + + _output->from(_squeeze); + } + +protected: + luci::CircleSqueeze *_squeeze = nullptr; +}; + +class SubstituteSqueezeToReshapeTest : public ::testing::Test +{ +public: + SubstituteSqueezeToReshapeTest() = default; + + void run_pass(void) + { + while (_shapeinf.run(_graph.g()) || _pass.run(_graph.g())) + ; + } + +protected: + SubstituteSqueezeToReshapeGraph _graph; + luci::SubstituteSqueezeToReshapePass _pass; + luci::CircleShapeInferencePass _shapeinf; +}; + +} // namespace + +TEST(SubstituteSqueezeToReshapePassTest, name) +{ + luci::SubstituteSqueezeToReshapePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(SubstituteSqueezeToReshapeTest, simple_with_squeeze_dims) +{ + _graph.init({1, 16, 1, 1}, {1, 16}, {2, 3}); + + run_pass(); + + auto reshape = dynamic_cast<luci::CircleReshape *>(_graph.output()->from()); + auto squeeze = dynamic_cast<luci::CircleSqueeze *>(_graph.output()->from()); + ASSERT_NE(nullptr, reshape); + ASSERT_EQ(nullptr, squeeze); + auto reshape_shape = loco::must_cast<luci::CircleConst *>(reshape->shape()); + ASSERT_EQ(2, reshape_shape->size<loco::DataType::S32>()); + ASSERT_EQ(1, reshape_shape->at<loco::DataType::S32>(0)); + ASSERT_EQ(16, reshape_shape->at<loco::DataType::S32>(1)); +} + +TEST_F(SubstituteSqueezeToReshapeTest, simple_without_squeeze_dims) +{ + _graph.init({1, 16, 1, 1}, {16}, {}); + + run_pass(); + + auto reshape = dynamic_cast<luci::CircleReshape *>(_graph.output()->from()); + auto squeeze = dynamic_cast<luci::CircleSqueeze *>(_graph.output()->from()); + ASSERT_NE(nullptr, reshape); + ASSERT_EQ(nullptr, squeeze); + auto reshape_shape = loco::must_cast<luci::CircleConst *>(reshape->shape()); + ASSERT_EQ(1, reshape_shape->size<loco::DataType::S32>()); + ASSERT_EQ(16, reshape_shape->at<loco::DataType::S32>(0)); +} + +TEST_F(SubstituteSqueezeToReshapeTest, input_with_0_dims) +{ + _graph.init({1, 16, 0, 1}, {16, 0}, {}); + + run_pass(); + + auto reshape = dynamic_cast<luci::CircleReshape *>(_graph.output()->from()); + auto squeeze = dynamic_cast<luci::CircleSqueeze *>(_graph.output()->from()); + ASSERT_NE(nullptr, reshape); + ASSERT_EQ(nullptr, squeeze); + auto reshape_shape = loco::must_cast<luci::CircleConst *>(reshape->shape()); + ASSERT_EQ(2, reshape_shape->size<loco::DataType::S32>()); + ASSERT_EQ(16, reshape_shape->at<loco::DataType::S32>(0)); + ASSERT_EQ(0, reshape_shape->at<loco::DataType::S32>(1)); +} + +TEST_F(SubstituteSqueezeToReshapeTest, nothing_to_squeeze) +{ + _graph.init({2, 16, 16, 3}, {2, 16, 16, 3}, {}); + + run_pass(); + + auto reshape = dynamic_cast<luci::CircleReshape *>(_graph.output()->from()); + auto squeeze = dynamic_cast<luci::CircleSqueeze *>(_graph.output()->from()); + ASSERT_NE(nullptr, reshape); + ASSERT_EQ(nullptr, squeeze); +} + +TEST_F(SubstituteSqueezeToReshapeTest, all_to_squeeze) +{ + _graph.init({1, 1}, {}, {}); + + run_pass(); + + auto reshape = dynamic_cast<luci::CircleReshape *>(_graph.output()->from()); + auto squeeze = dynamic_cast<luci::CircleSqueeze *>(_graph.output()->from()); + ASSERT_NE(nullptr, reshape); + ASSERT_EQ(nullptr, squeeze); +} + +TEST_F(SubstituteSqueezeToReshapeTest, wrong_squeeze_dims_NEG) +{ + _graph.init({1, 16, 1, 1}, {1, 16, 1, 1}, {1}); + + // shape inference will throw for invalid squeeze_dims + EXPECT_THROW(run_pass(), std::exception); +} diff --git a/compiler/luci/pass/src/SubstituteTransposeToReshapePass.cpp b/compiler/luci/pass/src/SubstituteTransposeToReshapePass.cpp new file mode 100644 index 000000000..dfd5e6cf2 --- /dev/null +++ b/compiler/luci/pass/src/SubstituteTransposeToReshapePass.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2021 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/Pass/SubstituteTransposeToReshapePass.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> + +namespace +{ + +/** + * @brief Convert transpose op in a certain condition to reshape op + * @details Convert transpose op if it have condition below + * 1. have a CircleConst perm value. + * 2. input have an unknown dimension less then 2 + * 3. the order of shape that except dim value 1 remains same on input and output + * eg) input shape = (126, 201, 1, 1) => (126, 201) + * output shape = (1, 126, 1, 201) => (126, 201) + */ +bool substitute_transpose_to_reshape(luci::CircleTranspose *node) +{ + auto perm_const = dynamic_cast<luci::CircleConst *>(node->perm()); + if (perm_const == nullptr) + return false; + + assert(perm_const->dtype() == loco::DataType::S32); + + auto input_node = loco::must_cast<luci::CircleNode *>(node->a()); + if (perm_const->dim(0).value() != input_node->rank()) + return false; + + // If input have more than 2 unknown dimension, transpose will not be changed. + int count = 0; + for (uint32_t i = 0; i < input_node->rank(); i++) + if (!input_node->dim(i).known()) + count++; + if (count > 1) + return false; + + uint32_t idx = 0; + auto size_items = perm_const->size<loco::DataType::S32>(); + for (uint32_t i = 0; i < size_items; i++) + { + assert(perm_const->at<loco::DataType::S32>(i) >= 0 && + perm_const->at<loco::DataType::S32>(i) < static_cast<int32_t>(input_node->rank())); + const auto perm_value = static_cast<uint32_t>(perm_const->at<loco::DataType::S32>(i)); + if (input_node->dim(perm_value).known() && input_node->dim(perm_value).value() == 1) + continue; + // To check idx values are increasing + if (idx > perm_value) + return false; + idx = perm_value; + } + + auto name = node->name(); + assert(name.length() > 0); + + auto new_const_node = node->graph()->nodes()->create<luci::CircleConst>(); + new_const_node->dtype(loco::DataType::S32); + new_const_node->size<loco::DataType::S32>(size_items); + new_const_node->shape_status(luci::ShapeStatus::VALID); + new_const_node->rank(1); + new_const_node->dim(0).set(size_items); + for (uint32_t i = 0; i < size_items; i++) + { + if (input_node->dim(static_cast<uint32_t>(perm_const->at<loco::DataType::S32>(i))).known()) + new_const_node->at<loco::DataType::S32>(i) = static_cast<int32_t>( + input_node->dim(static_cast<uint32_t>(perm_const->at<loco::DataType::S32>(i))).value()); + else + new_const_node->at<loco::DataType::S32>(i) = -1; + } + + auto new_reshape_node = node->graph()->nodes()->create<luci::CircleReshape>(); + new_reshape_node->tensor(input_node); + new_reshape_node->shape(new_const_node); + new_reshape_node->name(name + "/Reshape"); + luci::add_origin(new_reshape_node, luci::get_origin(node)); + new_const_node->name(name + "/Reshape/shape"); + + replace(node).with(new_reshape_node); + return true; +} + +} // namespace + +namespace luci +{ + +/** + * BEFORE + * + * [CircleNode] [CircleConst] + * \ / + * [CircleTranspose] + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode] [CircleConst] + * \ / + * [CircleReshape] + * | + * [CircleNode] + * + */ +bool SubstituteTransposeToReshapePass::run(loco::Graph *g) +{ + bool changed = false; + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto circle_node = dynamic_cast<luci::CircleTranspose *>(node)) + { + if (substitute_transpose_to_reshape(circle_node)) + { + changed = true; + } + } + } + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/SubstituteTransposeToReshapePass.test.cpp b/compiler/luci/pass/src/SubstituteTransposeToReshapePass.test.cpp new file mode 100644 index 000000000..f81f7e615 --- /dev/null +++ b/compiler/luci/pass/src/SubstituteTransposeToReshapePass.test.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2021 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/Pass/SubstituteTransposeToReshapePass.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +class SubstituteTransposeToReshapeTest : public ::testing::Test +{ +public: + SubstituteTransposeToReshapeTest() {} + + void buildGraph(const std::initializer_list<uint32_t> shape, const std::vector<int32_t> perm) + { + // Input Create. + input = g.nodes()->create<luci::CircleInput>(); + auto graph_input = g.inputs()->create(); + input->index(graph_input->index()); + input->shape_status(luci::ShapeStatus::VALID); + input->rank(shape.size()); + input->shape(shape); + input->name("input"); + + // Permutation Create. + auto perm_const = g.nodes()->create<luci::CircleConst>(); + perm_const->dtype(loco::DataType::S32); + perm_const->size<loco::DataType::S32>(perm.size()); + perm_const->shape_status(luci::ShapeStatus::VALID); + perm_const->rank(1); + perm_const->dim(0).set(perm.size()); + for (uint32_t i = 0; i < static_cast<uint32_t>(perm.size()); i++) + { + perm_const->at<loco::DataType::S32>(i) = perm.at(i); + } + perm_const->name("perm_const"); + + // Transpose Create. + auto transpose_node = g.nodes()->create<luci::CircleTranspose>(); + transpose_node->a(input); + transpose_node->perm(perm_const); + transpose_node->name("transpose_node"); + + // Output Connect. + output = g.nodes()->create<luci::CircleOutput>(); + output->from(transpose_node); + auto graph_output = g.outputs()->create(); + output->index(graph_output->index()); + output->name("output"); + } + +public: + loco::Graph g; + luci::CircleInput *input = nullptr; + luci::CircleOutput *output = nullptr; +}; + +} // namespace + +TEST(SubstituteTransposeToReshapePassTest, name) +{ + luci::SubstituteTransposeToReshapePass pass; + auto const name = pass.name(); + ASSERT_NE(nullptr, name); +} + +TEST_F(SubstituteTransposeToReshapeTest, simple_case) +{ + // Create graph that tranpose input {126, 201, 1, 1} with permutation {2, 0, 3, 1} + buildGraph({126, 201, 1, 1}, std::vector<int32_t>({2, 0, 3, 1})); + // With this input shape and permutation values, output shape will be [1, 126, 1, 201]. + // The order of non-one values is unchanged (126, 201). + // So this Transpose op can be converted to Reshape op. + luci::SubstituteTransposeToReshapePass pass; + while (pass.run(&g)) + ; + + auto reshape_node = dynamic_cast<luci::CircleReshape *>(output->from()); + auto transpose_node = dynamic_cast<luci::CircleTranspose *>(output->from()); + ASSERT_NE(nullptr, reshape_node); + ASSERT_EQ(nullptr, transpose_node); + auto new_shape = loco::must_cast<luci::CircleConst *>(reshape_node->shape()); + ASSERT_EQ(1, new_shape->at<loco::DataType::S32>(0)); + ASSERT_EQ(126, new_shape->at<loco::DataType::S32>(1)); + ASSERT_EQ(1, new_shape->at<loco::DataType::S32>(2)); + ASSERT_EQ(201, new_shape->at<loco::DataType::S32>(3)); +} + +TEST_F(SubstituteTransposeToReshapeTest, failed_to_substitute_NEG) +{ + // Create graph that tranpose input {126, 201, 1, 1} with permutation {2, 1, 3, 0} + buildGraph({126, 201, 1, 1}, std::vector<int32_t>({2, 1, 3, 0})); + // With this input shape and permutation values, output shape will be [1, 201, 1, 126]. + // The order of non-one values is changed (126, 201) -> (201, 126). + // So this Transpose op cannot be converted to Reshape op. + luci::SubstituteTransposeToReshapePass pass; + while (pass.run(&g)) + ; + + auto reshape_node = dynamic_cast<luci::CircleReshape *>(output->from()); + auto transpose_node = dynamic_cast<luci::CircleTranspose *>(output->from()); + ASSERT_EQ(nullptr, reshape_node); + ASSERT_NE(nullptr, transpose_node); +} diff --git a/compiler/luci/pass/src/TransformMinMaxToRelu6Pass.cpp b/compiler/luci/pass/src/TransformMinMaxToRelu6Pass.cpp new file mode 100644 index 000000000..c15a3b676 --- /dev/null +++ b/compiler/luci/pass/src/TransformMinMaxToRelu6Pass.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 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/Pass/TransformMinMaxToRelu6Pass.h" + +#include "helpers/NodeFiller.h" +#include "helpers/TypeMapper.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Profile/CircleNodeOrigin.h> + +namespace +{ + +template <loco::DataType DT> +bool is_scalar_with_value(luci::CircleConst *node, typename loco::DataTypeImpl<DT>::Type val) +{ + if (node->dtype() != DT) + return false; + if (node->rank() != 0) + return false; + if (node->size<DT>() != 1) + return false; + if (node->at<DT>(0) != static_cast<typename loco::DataTypeImpl<DT>::Type>(val)) + return false; + + return true; +} + +/** + * BEFORE + * [CircleNode] + * | + * [CircleMinimum] + * | + * [CircleMaximum] + * | + * [CircleNode] + * + * AFTER + * + * [CircleNode] + * | + * [CircleRelu6] + * | + * [CircleNode] + * + * NOTE Only max(min(input, 6), 0) pattern will be transformed. + */ +template <loco::DataType DT> bool transform_min_max_pattern(luci::CircleMaximum *maxi) +{ + if (not maxi) + return false; + + if (maxi->dtype() != DT) + return false; + + luci::CircleConst *maxi_const = nullptr; + luci::CircleMinimum *mini = nullptr; + + // There are two ways Maximum takes inputs. + // 1. Maximum(x = CircleConst, y = CircleMinimum) + // 2. Maximum(x = CircleMinimum, y = CircleConst) + if (not luci::fill(&maxi_const, &mini).with_commutative_args_of(maxi)) + return false; + + // Maximum constant should be scalar whose value is 0. + if (not is_scalar_with_value<DT>(maxi_const, + static_cast<typename loco::DataTypeImpl<DT>::Type>(0))) + return false; + + luci::CircleConst *mini_const = nullptr; + loco::Node *mini_input = nullptr; + + // There are two ways Miminum takes inputs. + // 1. Miminum(x = CircleNode, y = CircleMinimum) + // 2. Miminum(x = CircleMinimum, y = CircleNode) + if (not luci::fill(&mini_const, &mini_input).with_commutative_args_of(mini)) + return false; + + // Miminum constant should be scalar whose value is 6. + if (not is_scalar_with_value<DT>(mini_const, + static_cast<typename loco::DataTypeImpl<DT>::Type>(6))) + return false; + + auto name = maxi->name(); + assert(name.length() > 0); + + // Create Relu6 op + auto relu6 = mini->graph()->nodes()->create<luci::CircleRelu6>(); + relu6->features(mini_input); + relu6->name(name + "/Relu6"); + luci::add_origin(relu6, luci::composite_origin({luci::get_origin(maxi), luci::get_origin(mini)})); + + replace(maxi).with(relu6); + + return true; +} + +} // namespace + +namespace luci +{ + +bool TransformMinMaxToRelu6Pass::run(loco::Graph *g) +{ + bool changed = false; + + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + if (auto maxi = dynamic_cast<luci::CircleMaximum *>(node)) + { + if (transform_min_max_pattern<loco::DataType::FLOAT32>(maxi)) + changed = true; + } + } + + return changed; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/TransformMinMaxToRelu6Pass.test.cpp b/compiler/luci/pass/src/TransformMinMaxToRelu6Pass.test.cpp new file mode 100644 index 000000000..9755a70cf --- /dev/null +++ b/compiler/luci/pass/src/TransformMinMaxToRelu6Pass.test.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 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/Pass/TransformMinMaxToRelu6Pass.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +namespace +{ + +/** + * Minimum-Maximum pattern graph + * + * [CircleInput] [CircleConst] + * \ / + * [CircleMinimum] [CircleConst] + * | / + * [CircleMaximum] + * | + * [CircleOutput] + */ +struct MinMaxGraph +{ + loco::Graph _g; + luci::CircleInput *_input = nullptr; + luci::CircleMinimum *_mini = nullptr; + luci::CircleConst *_mini_const = nullptr; + luci::CircleMaximum *_maxi = nullptr; + luci::CircleConst *_maxi_const = nullptr; + luci::CircleOutput *_output = nullptr; +}; + +class TransformMinMaxToRelu6PassTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + const int N = 1; + const int H = 4; + const int W = 4; + const int C = 3; + + // graph input and output + auto graph_input = _min_max_g._g.inputs()->create(); + auto graph_output = _min_max_g._g.outputs()->create(); + + // CircleInput + _min_max_g._input = _min_max_g._g.nodes()->create<luci::CircleInput>(); + _min_max_g._input->index(graph_input->index()); + _min_max_g._input->shape({N, H, W, C}); + _min_max_g._input->dtype(loco::DataType::FLOAT32); + _min_max_g._input->name("input"); + + // CircleConst + _min_max_g._mini_const = _min_max_g._g.nodes()->create<luci::CircleConst>(); + _min_max_g._mini_const->shape({}); // scalar + _min_max_g._mini_const->dtype(loco::DataType::FLOAT32); + _min_max_g._mini_const->size<loco::DataType::FLOAT32>(1); + _min_max_g._mini_const->at<loco::DataType::FLOAT32>(0) = 6.; + _min_max_g._mini_const->name("mini_const"); + + // CircleMinimum + _min_max_g._mini = _min_max_g._g.nodes()->create<luci::CircleMinimum>(); + _min_max_g._mini->x(_min_max_g._input); + _min_max_g._mini->y(_min_max_g._mini_const); + _min_max_g._mini->shape({N, H, W, C}); + _min_max_g._mini->dtype(loco::DataType::FLOAT32); + _min_max_g._mini->name("mini"); + + // CircleConst + _min_max_g._maxi_const = _min_max_g._g.nodes()->create<luci::CircleConst>(); + _min_max_g._mini_const->shape({}); // scalar + _min_max_g._maxi_const->dtype(loco::DataType::FLOAT32); + _min_max_g._maxi_const->size<loco::DataType::FLOAT32>(1); + _min_max_g._maxi_const->at<loco::DataType::FLOAT32>(0) = 0.; + _min_max_g._maxi_const->name("maxi_const"); + + // CircleMaximum + _min_max_g._maxi = _min_max_g._g.nodes()->create<luci::CircleMaximum>(); + _min_max_g._maxi->x(_min_max_g._mini); + _min_max_g._maxi->y(_min_max_g._maxi_const); + _min_max_g._maxi->shape({N, H, W, C}); + _min_max_g._maxi->dtype(loco::DataType::FLOAT32); + _min_max_g._maxi->name("maxi"); + + // CircleOutput + _min_max_g._output = _min_max_g._g.nodes()->create<luci::CircleOutput>(); + _min_max_g._output->index(graph_output->index()); + _min_max_g._output->from(_min_max_g._maxi); + _min_max_g._output->shape({N, H, W, C}); + _min_max_g._output->dtype(loco::DataType::FLOAT32); + _min_max_g._output->name("output"); + } + +protected: + luci::TransformMinMaxToRelu6Pass _pass; + MinMaxGraph _min_max_g; +}; + +} // namespace + +TEST_F(TransformMinMaxToRelu6PassTest, name) +{ + auto const name = _pass.name(); + ASSERT_NE(nullptr, name); +} + +/** + * Optimized graph looks like below. + * + * [CircleInput] + * | + * [CircleRelu6] + * | + * [CircleOutput] + */ +TEST_F(TransformMinMaxToRelu6PassTest, simple_test) +{ + auto ret = _pass.run(&_min_max_g._g); + EXPECT_TRUE(ret); + + auto relu6 = dynamic_cast<luci::CircleRelu6 *>(_min_max_g._output->from()); + EXPECT_NE(nullptr, relu6); + + auto input = dynamic_cast<luci::CircleInput *>(relu6->features()); + EXPECT_NE(nullptr, input); +} + +TEST_F(TransformMinMaxToRelu6PassTest, wrong_condition_NEG) +{ + _min_max_g._maxi_const->at<loco::DataType::FLOAT32>(0) = 2.; + + auto ret = _pass.run(&_min_max_g._g); + + EXPECT_FALSE(ret); +} diff --git a/compiler/luci/pass/src/TypeInferencePass.cpp b/compiler/luci/pass/src/TypeInferencePass.cpp deleted file mode 100644 index 63744045c..000000000 --- a/compiler/luci/pass/src/TypeInferencePass.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020 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/Pass/TypeInferencePass.h" - -#include <luci/IR/CircleDialect.h> -#include <luci/Service/CircleTypeInferenceRule.h> - -#include <loco.h> -#include <loco/IR/CanonicalDialect.h> -#include <loco/Service/TypeInference.h> - -namespace luci -{ - -bool TypeInferencePass::run(luci::Module *m) -{ - bool changed = false; - - for (size_t g = 0; g < m->size(); ++g) - { - if (run(m->graph(g))) - changed = true; - } - - return changed; -} - -bool TypeInferencePass::run(loco::Graph *g) -{ - loco::CanonicalTypeInferenceRule canonical_rule; - luci::CircleTypeInferenceRule circle_rule; - - loco::MultiDialectTypeInferenceRule rules; - - rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(luci::CircleDialect::get(), &circle_rule); - - return loco::apply(&rules).to(g); -} - -} // namespace luci diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeChannelWiseGranularity.h b/compiler/luci/pass/src/VerifyQuantizedNodeChannelWiseGranularity.h new file mode 100644 index 000000000..32f0d1a34 --- /dev/null +++ b/compiler/luci/pass/src/VerifyQuantizedNodeChannelWiseGranularity.h @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2021 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 __LUCI_VERIFY_QUANTIZED_NODE_CHANNELWISE_GRANULARITY_H__ +#define __LUCI_VERIFY_QUANTIZED_NODE_CHANNELWISE_GRANULARITY_H__ + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Pass/QuantizationParameters.h> + +using Granularity = luci::QuantizationGranularity; + +// This macro is undef at the end of the file +#define RETURN_FALSE_UNLESS(ARG) \ + if (not(ARG)) \ + { \ + return false; \ + } + +namespace luci +{ + +/** + * @brief Verify the granualrity of channel-wise quantized node + * @details + * + * Targets to verify + * - node's output (i.e., node itself) + * - node's inputs + */ +struct VerifyQuantizedNodeChannelWiseGranularity final : public luci::CircleNodeVisitor<bool> +{ +private: + bool is_lwq(const loco::Node *node) + { + auto circle_node = loco::must_cast<const luci::CircleNode *>(node); + + if (circle_node->quantparam() == nullptr) + return false; + + if (circle_node->quantparam()->scale.size() != 1) + return false; + + if (circle_node->quantparam()->zerop.size() != 1) + return false; + + return true; + } + + uint32_t rank(const loco::Node *node) + { + auto circle_node = loco::must_cast<const luci::CircleNode *>(node); + return circle_node->rank(); + } + + bool is_cwq_const(const loco::Node *node, uint32_t channel_dim) + { + auto circle_node = loco::must_cast<const luci::CircleConst *>(node); + + assert(channel_dim < circle_node->rank()); // FIX_CALLER_UNLESS + auto channel_size = circle_node->dim(channel_dim).value(); + + if (circle_node->quantparam() == nullptr) + return false; + + if (circle_node->quantparam()->quantized_dimension != static_cast<int32_t>(channel_dim)) + return false; + + if (circle_node->quantparam()->scale.size() != channel_size) + return false; + + if (circle_node->quantparam()->zerop.size() != channel_size) + return false; + + return true; + } + +private: + bool visit(const luci::CircleConv2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_cwq_const(node->filter(), 0)) + RETURN_FALSE_UNLESS(is_cwq_const(node->bias(), rank(node->bias()) - 1)) + return true; + } + + bool visit(const luci::CircleConcatenation *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + for (uint32_t i = 0; i < node->numValues(); i++) + { + RETURN_FALSE_UNLESS(is_lwq(node->values(i))); + } + return true; + } + + bool visit(const luci::CircleDepthToSpace *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + return true; + } + + bool visit(const luci::CircleDepthwiseConv2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_cwq_const(node->filter(), 3)) + RETURN_FALSE_UNLESS(is_cwq_const(node->bias(), rank(node->bias()) - 1)) + return true; + } + + bool visit(const luci::CircleInstanceNorm *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_cwq_const(node->gamma(), rank(node->gamma()) - 1)) + RETURN_FALSE_UNLESS(is_cwq_const(node->beta(), rank(node->beta()) - 1)) + return true; + } + + bool visit(const luci::CirclePad *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + return true; + } + + bool visit(const luci::CirclePRelu *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_cwq_const(node->alpha(), rank(node->alpha()) - 1)) + return true; + } + + bool visit(const luci::CircleTransposeConv *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->outBackprop())) + RETURN_FALSE_UNLESS(is_cwq_const(node->filter(), 0)) + luci::CircleConst *bias = dynamic_cast<luci::CircleConst *>(node->bias()); + if (bias != nullptr) + RETURN_FALSE_UNLESS(is_cwq_const(node->bias(), rank(node->bias()) - 1)) + + return true; + } + + bool visit(const luci::CircleFullyConnected *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_cwq_const(node->weights(), 0)) + RETURN_FALSE_UNLESS(is_cwq_const(node->bias(), rank(node->bias()) - 1)) + return true; + } + + bool visit(const luci::CircleAdd *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleAveragePool2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->value())); + return true; + } + + bool visit(const luci::CircleLogicalOr *) + { + // Logical OR has bool-type inputs and output + // Nothing to be checked + return true; + } + + bool visit(const luci::CircleMaxPool2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->value())); + return true; + } + + bool visit(const luci::CircleMean *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleMul *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleNotEqual *node) + { + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleRelu *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->features())); + return true; + } + + bool visit(const luci::CircleReshape *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->tensor())); + return true; + } + + bool visit(const luci::CircleLogistic *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleSoftmax *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->logits())); + return true; + } + + bool visit(const luci::CircleSpaceToBatchND *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSpaceToDepth *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSlice *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSplit *node) + { + // node's output is the input of CircleSplitOut, thus not quantized + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSplitOut *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + return true; + } + + bool visit(const luci::CircleStridedSlice *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleArgMax *node) + { + // node's output is index, thus not quantized + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleBatchToSpaceND *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleTanh *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleTranspose *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->a())); + return true; + } + + bool visit(const luci::CircleFloor *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleGreater *node) + { + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleGreaterEqual *node) + { + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleDiv *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleFloorDiv *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleRsqrt *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleSqrt *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleElu *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->features())); + return true; + } + + bool visit(const luci::CirclePow *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleResizeBilinear *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + // TODO: Implement more Ops + + bool visit(const luci::CircleNode *) { return true; } +}; + +} // namespace luci + +#undef RETURN_FALSE_UNLESS + +#endif // __LUCI_VERIFY_QUANTIZED_NODE_CHANNELWISE_GRANULARITY_H__ diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeLayerWiseGranularity.h b/compiler/luci/pass/src/VerifyQuantizedNodeLayerWiseGranularity.h new file mode 100644 index 000000000..1e6fd53c0 --- /dev/null +++ b/compiler/luci/pass/src/VerifyQuantizedNodeLayerWiseGranularity.h @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2021 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 __LUCI_VERIFY_QUANTIZED_NODE_LAYERWISE_GRANULARITY_H__ +#define __LUCI_VERIFY_QUANTIZED_NODE_LAYERWISE_GRANULARITY_H__ + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> +#include <luci/Pass/QuantizationParameters.h> + +using Granularity = luci::QuantizationGranularity; + +// This macro is undef at the end of the file +#define RETURN_FALSE_UNLESS(ARG) \ + if (not(ARG)) \ + { \ + return false; \ + } + +namespace luci +{ + +/** + * @brief Verify the granualrity of layer-wise quantized node + * @details + * + * Targets to verify + * - node's output (i.e., node itself) + * - node's inputs + */ +struct VerifyQuantizedNodeLayerWiseGranularity final : public luci::CircleNodeVisitor<bool> +{ +private: + bool is_lwq(const loco::Node *node) + { + auto circle_node = loco::must_cast<const luci::CircleNode *>(node); + + if (circle_node->quantparam() == nullptr) + return false; + + if (circle_node->quantparam()->scale.size() != 1) + return false; + + if (circle_node->quantparam()->zerop.size() != 1) + return false; + + return true; + } + + bool is_lwq_const(const loco::Node *node) + { + auto circle_node = loco::must_cast<const luci::CircleConst *>(node); + + if (circle_node->quantparam() == nullptr) + return false; + + if (circle_node->quantparam()->scale.size() != 1) + return false; + + if (circle_node->quantparam()->zerop.size() != 1) + return false; + + return true; + } + +private: + bool visit(const luci::CircleConv2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_lwq_const(node->filter())) + RETURN_FALSE_UNLESS(is_lwq_const(node->bias())) + return true; + } + + bool visit(const luci::CircleConcatenation *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + for (uint32_t i = 0; i < node->numValues(); i++) + { + RETURN_FALSE_UNLESS(is_lwq(node->values(i))); + } + return true; + } + + bool visit(const luci::CircleDepthToSpace *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + return true; + } + + bool visit(const luci::CircleDepthwiseConv2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_lwq_const(node->filter())) + RETURN_FALSE_UNLESS(is_lwq_const(node->bias())) + return true; + } + + bool visit(const luci::CircleInstanceNorm *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_lwq_const(node->gamma())) + RETURN_FALSE_UNLESS(is_lwq_const(node->beta())) + return true; + } + + bool visit(const luci::CirclePad *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + return true; + } + + bool visit(const luci::CirclePRelu *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_lwq_const(node->alpha())) + return true; + } + + bool visit(const luci::CircleTransposeConv *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->outBackprop())) + RETURN_FALSE_UNLESS(is_lwq_const(node->filter())) + luci::CircleConst *bias = dynamic_cast<luci::CircleConst *>(node->bias()); + if (bias != nullptr) + RETURN_FALSE_UNLESS(is_lwq_const(node->bias())) + return true; + } + + bool visit(const luci::CircleFullyConnected *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())) + RETURN_FALSE_UNLESS(is_lwq_const(node->weights())) + RETURN_FALSE_UNLESS(is_lwq_const(node->bias())) + return true; + } + + bool visit(const luci::CircleAdd *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleAveragePool2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->value())); + return true; + } + + bool visit(const luci::CircleLogicalOr *) + { + // Logical OR has bool-type inputs and output + // Nothing to be checked + return true; + } + + bool visit(const luci::CircleMaxPool2D *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->value())); + return true; + } + + bool visit(const luci::CircleMean *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleMul *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleNotEqual *node) + { + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleRelu *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->features())); + return true; + } + + bool visit(const luci::CircleReshape *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)) + RETURN_FALSE_UNLESS(is_lwq(node->tensor())); + return true; + } + + bool visit(const luci::CircleLogistic *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleSoftmax *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->logits())); + return true; + } + + bool visit(const luci::CircleSpaceToBatchND *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSpaceToDepth *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSlice *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSplit *node) + { + // node's output is the input of CircleSplitOut, thus not quantized + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleSplitOut *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + return true; + } + + bool visit(const luci::CircleStridedSlice *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleArgMax *node) + { + // node's output is index, thus not quantized + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleBatchToSpaceND *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + bool visit(const luci::CircleTanh *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleTranspose *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->a())); + return true; + } + + bool visit(const luci::CircleFloor *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleGreater *node) + { + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleGreaterEqual *node) + { + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleDiv *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleFloorDiv *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleRsqrt *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleSqrt *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + return true; + } + + bool visit(const luci::CircleElu *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->features())); + return true; + } + + bool visit(const luci::CirclePow *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->x())); + RETURN_FALSE_UNLESS(is_lwq(node->y())); + return true; + } + + bool visit(const luci::CircleResizeBilinear *node) + { + RETURN_FALSE_UNLESS(is_lwq(node)); + RETURN_FALSE_UNLESS(is_lwq(node->input())); + return true; + } + + // TODO: Implement more Ops + + bool visit(const luci::CircleNode *) { return true; } +}; + +} // namespace luci + +#undef RETURN_FALSE_UNLESS + +#endif // __LUCI_VERIFY_QUANTIZED_NODE_LAYERWISE_GRANULARITY_H__ diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeS16Type.h b/compiler/luci/pass/src/VerifyQuantizedNodeS16Type.h new file mode 100644 index 000000000..e05d8325f --- /dev/null +++ b/compiler/luci/pass/src/VerifyQuantizedNodeS16Type.h @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2021 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 __LUCI_VERIFY_QUANTIZED_NODE_S16_TYPE_H__ +#define __LUCI_VERIFY_QUANTIZED_NODE_S16_TYPE_H__ + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> + +using Type = loco::DataType; + +// This macro is undef at the end of the file +#define RETURN_FALSE_UNLESS(ARG) \ + if (not(ARG)) \ + { \ + return false; \ + } + +namespace luci +{ + +/** + * @brief Verify the data type of INT16 quantized node + * @details + * + * Targets to verify + * - node's output (i.e., node itself) + * - node's inputs + */ +struct VerifyQuantizedNodeS16Type final : public luci::CircleNodeVisitor<bool> +{ +private: + bool has_type(const loco::Node *node, Type dtype) + { + auto circle_node = loco::must_cast<const luci::CircleNode *>(node); + return circle_node->dtype() == dtype; + } + +private: + bool visit(const luci::CircleConv2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->filter(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->bias(), Type::S64)) + return true; + } + + bool visit(const luci::CircleConcatenation *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + for (uint32_t i = 0; i < node->numValues(); i++) + { + RETURN_FALSE_UNLESS(has_type(node->values(i), Type::S16)) + } + return true; + } + + bool visit(const luci::CircleDepthToSpace *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + return true; + } + + bool visit(const luci::CircleDepthwiseConv2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->filter(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->bias(), Type::S64)) + return true; + } + + bool visit(const luci::CircleInstanceNorm *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->gamma(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->beta(), Type::S16)) + return true; + } + + bool visit(const luci::CirclePad *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->paddings(), Type::S32)) + return true; + } + + bool visit(const luci::CirclePRelu *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->alpha(), Type::S16)) + return true; + } + + bool visit(const luci::CircleTransposeConv *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->outBackprop(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->filter(), Type::S16)) + luci::CircleConst *bias = dynamic_cast<luci::CircleConst *>(node->bias()); + if (bias != nullptr) + RETURN_FALSE_UNLESS(has_type(bias, Type::S64)) + return true; + } + + bool visit(const luci::CircleFullyConnected *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->weights(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->bias(), Type::S64)) + return true; + } + + bool visit(const luci::CircleAdd *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleAveragePool2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->value(), Type::S16)) + return true; + } + + bool visit(const luci::CircleLogicalOr *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::BOOL)) + return true; + } + + bool visit(const luci::CircleMaxPool2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->value(), Type::S16)) + return true; + } + + bool visit(const luci::CircleMean *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->reduction_indices(), Type::S32)) + return true; + } + + bool visit(const luci::CircleMul *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleNotEqual *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleRelu *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->features(), Type::S16)) + return true; + } + + bool visit(const luci::CircleReshape *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->tensor(), Type::S16)) + luci::CircleConst *shape = dynamic_cast<luci::CircleConst *>(node->shape()); + if (shape != nullptr) + RETURN_FALSE_UNLESS(has_type(shape, Type::S32)) + return true; + } + + bool visit(const luci::CircleLogistic *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + return true; + } + + bool visit(const luci::CircleSoftmax *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->logits(), Type::S16)) + return true; + } + + bool visit(const luci::CircleSpaceToBatchND *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + return true; + } + + bool visit(const luci::CircleSpaceToDepth *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + return true; + } + + bool visit(const luci::CircleSlice *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->begin(), Type::S32) || has_type(node->begin(), Type::S64)) + RETURN_FALSE_UNLESS(has_type(node->size(), Type::S32) || has_type(node->size(), Type::S64)) + return true; + } + + bool visit(const luci::CircleSplit *node) + { + // node's output is the input of CircleSplitOut, thus not quantized + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + return true; + } + + bool visit(const luci::CircleSplitOut *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + return true; + } + + bool visit(const luci::CircleStridedSlice *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + return true; + } + + bool visit(const luci::CircleArgMax *node) + { + RETURN_FALSE_UNLESS(has_type(node, node->output_type())) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->dimension(), Type::S32) || + has_type(node->dimension(), Type::S64)) + return true; + } + + bool visit(const luci::CircleBatchToSpaceND *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + return true; + } + + bool visit(const luci::CircleTanh *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + return true; + } + + bool visit(const luci::CircleTranspose *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->a(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->perm(), Type::S32)) + return true; + } + + bool visit(const luci::CircleFloor *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + return true; + } + + bool visit(const luci::CircleGreater *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleGreaterEqual *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleDiv *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleFloorDiv *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleRsqrt *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + return true; + } + + bool visit(const luci::CircleSqrt *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + return true; + } + + bool visit(const luci::CircleElu *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->features(), Type::S16)) + return true; + } + + bool visit(const luci::CirclePow *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::S16)) + return true; + } + + bool visit(const luci::CircleResizeBilinear *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::S16)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::S16)) + return true; + } + + // TODO: Implement more Ops + + bool visit(const luci::CircleNode *) { return true; } +}; + +} // namespace luci + +#undef RETURN_FALSE_UNLESS + +#endif // __LUCI_VERIFY_QUNTIZED_NODE_S16_TYPE_H__ diff --git a/compiler/luci/pass/src/VerifyQuantizedNodeU8Type.h b/compiler/luci/pass/src/VerifyQuantizedNodeU8Type.h new file mode 100644 index 000000000..72ce5b8f8 --- /dev/null +++ b/compiler/luci/pass/src/VerifyQuantizedNodeU8Type.h @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2021 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 __LUCI_VERIFY_QUANTIZED_NODE_U8_TYPE_H__ +#define __LUCI_VERIFY_QUANTIZED_NODE_U8_TYPE_H__ + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNodeVisitor.h> + +using Type = loco::DataType; + +// This macro is undef at the end of the file +#define RETURN_FALSE_UNLESS(ARG) \ + if (not(ARG)) \ + { \ + return false; \ + } + +namespace luci +{ + +/** + * @brief Verify the data type of UINT8 quantized node + * @details + * + * Targets to verify + * - node's output (i.e., node itself) + * - node's inputs + */ +struct VerifyQuantizedNodeU8Type final : public luci::CircleNodeVisitor<bool> +{ +private: + bool has_type(const loco::Node *node, Type dtype) + { + auto circle_node = loco::must_cast<const luci::CircleNode *>(node); + return circle_node->dtype() == dtype; + } + +private: + bool visit(const luci::CircleConv2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->filter(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->bias(), Type::S32)) + return true; + } + + bool visit(const luci::CircleConcatenation *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + for (uint32_t i = 0; i < node->numValues(); i++) + { + RETURN_FALSE_UNLESS(has_type(node->values(i), Type::U8)) + } + return true; + } + + bool visit(const luci::CircleDepthToSpace *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + return true; + } + + bool visit(const luci::CircleDepthwiseConv2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->filter(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->bias(), Type::S32)) + return true; + } + + bool visit(const luci::CircleInstanceNorm *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->gamma(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->beta(), Type::U8)) + return true; + } + + bool visit(const luci::CirclePad *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->paddings(), Type::S32)) + return true; + } + + bool visit(const luci::CirclePRelu *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->alpha(), Type::U8)) + return true; + } + + bool visit(const luci::CircleTransposeConv *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->outBackprop(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->filter(), Type::U8)) + luci::CircleConst *bias = dynamic_cast<luci::CircleConst *>(node->bias()); + if (bias != nullptr) + RETURN_FALSE_UNLESS(has_type(bias, Type::S32)) + return true; + } + + bool visit(const luci::CircleFullyConnected *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->weights(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->bias(), Type::S32)) + return true; + } + + bool visit(const luci::CircleAdd *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleAveragePool2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->value(), Type::U8)) + return true; + } + + bool visit(const luci::CircleBatchToSpaceND *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + return true; + } + + bool visit(const luci::CircleLogicalOr *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::BOOL)) + return true; + } + + bool visit(const luci::CircleMaxPool2D *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->value(), Type::U8)) + return true; + } + + bool visit(const luci::CircleMean *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->reduction_indices(), Type::S32)) + return true; + } + + bool visit(const luci::CircleMul *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleNotEqual *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleRelu *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->features(), Type::U8)) + return true; + } + + bool visit(const luci::CircleReshape *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->tensor(), Type::U8)) + luci::CircleConst *shape = dynamic_cast<luci::CircleConst *>(node->shape()); + if (shape != nullptr) + RETURN_FALSE_UNLESS(has_type(shape, Type::S32)) + return true; + } + + bool visit(const luci::CircleLogistic *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + return true; + } + + bool visit(const luci::CircleSoftmax *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->logits(), Type::U8)) + return true; + } + + bool visit(const luci::CircleSpaceToBatchND *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + return true; + } + + bool visit(const luci::CircleSpaceToDepth *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + return true; + } + + bool visit(const luci::CircleSlice *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->begin(), Type::S32) || has_type(node->begin(), Type::S64)) + RETURN_FALSE_UNLESS(has_type(node->size(), Type::S32) || has_type(node->size(), Type::S64)) + return true; + } + + bool visit(const luci::CircleSplit *node) + { + // node's output is the input of CircleSplitOut, thus not quantized + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + return true; + } + + bool visit(const luci::CircleSplitOut *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + return true; + } + + bool visit(const luci::CircleStridedSlice *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + return true; + } + + bool visit(const luci::CircleArgMax *node) + { + RETURN_FALSE_UNLESS(has_type(node, node->output_type())) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->dimension(), Type::S32) || + has_type(node->dimension(), Type::S64)) + return true; + } + + bool visit(const luci::CircleTanh *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + return true; + } + + bool visit(const luci::CircleTranspose *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->a(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->perm(), Type::S32)) + return true; + } + + bool visit(const luci::CircleFloor *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + return true; + } + + bool visit(const luci::CircleGreater *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleGreaterEqual *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::BOOL)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleDiv *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleFloorDiv *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleRsqrt *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + return true; + } + + bool visit(const luci::CircleSqrt *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + return true; + } + + bool visit(const luci::CircleElu *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->features(), Type::U8)) + return true; + } + + bool visit(const luci::CirclePow *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->x(), Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->y(), Type::U8)) + return true; + } + + bool visit(const luci::CircleResizeBilinear *node) + { + RETURN_FALSE_UNLESS(has_type(node, Type::U8)) + RETURN_FALSE_UNLESS(has_type(node->input(), Type::U8)) + return true; + } + + // TODO: Implement more Ops + + bool visit(const luci::CircleNode *) { return true; } +}; + +} // namespace luci + +#undef RETURN_FALSE_UNLESS + +#endif // __LUCI_VERIFY_QUNTIZED_NODE_U8_TYPE_H__ diff --git a/compiler/luci/pass/src/helpers/InferenceCandidates.cpp b/compiler/luci/pass/src/helpers/InferenceCandidates.cpp new file mode 100644 index 000000000..2c8565932 --- /dev/null +++ b/compiler/luci/pass/src/helpers/InferenceCandidates.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 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 "InferenceCandidates.h" + +#include <luci/IR/DeadNodeQueryService.h> + +namespace luci +{ + +std::vector<loco::Node *> inference_candidates(loco::Graph *g) +{ + auto candidates = loco::postorder_traversal(loco::output_nodes(g)); + + for (auto node : loco::all_nodes(g)) + { + // already included as candidate + if (std::find(candidates.begin(), candidates.end(), node) != candidates.end()) + continue; + + // As the node is not used for both graph output and multiple output operation, + // it cannot be candidate. + if (node->dialect()->service<DeadNodeQueryServiceImpl>()->isDeadNode(node)) + continue; + + candidates.emplace_back(node); + } + + return candidates; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/helpers/InferenceCandidates.h b/compiler/luci/pass/src/helpers/InferenceCandidates.h new file mode 100644 index 000000000..f27e4fe60 --- /dev/null +++ b/compiler/luci/pass/src/helpers/InferenceCandidates.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 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 __LUCI_INFERENCE_CANDIDATES_H__ +#define __LUCI_INFERENCE_CANDIDATES_H__ + +#include <loco.h> + +#include <vector> + +namespace luci +{ + +/** + * @brief Enumerate all the nodes whose shape/dtype should be inferenced to export graph. + */ +std::vector<loco::Node *> inference_candidates(loco::Graph *g); + +} // namespace luci + +#endif // __LUCI_INFERENCE_CANDIDATES_H__ diff --git a/compiler/luci/pass/src/helpers/InferenceCandidates.test.cpp b/compiler/luci/pass/src/helpers/InferenceCandidates.test.cpp new file mode 100644 index 000000000..e34421f5e --- /dev/null +++ b/compiler/luci/pass/src/helpers/InferenceCandidates.test.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021 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 "InferenceCandidates.h" +#include "luci/IR/CircleNode.h" + +#include <algorithm> + +#include <gtest/gtest.h> + +namespace +{ + +bool contains(const std::vector<loco::Node *> &vec, loco::Node *val) +{ + return std::any_of(vec.begin(), vec.end(), [val](loco::Node *node) { return node == val; }); +} + +} // namespace + +TEST(LuciPassHelpersInferenceCandidates, inference_candidates) +{ + auto g = loco::make_graph(); + + // Create nodes + auto input = g->nodes()->create<luci::CircleInput>(); + auto split = g->nodes()->create<luci::CircleSplit>(); + auto split_out1 = g->nodes()->create<luci::CircleSplitOut>(); + auto split_out2 = g->nodes()->create<luci::CircleSplitOut>(); + auto split_dim = g->nodes()->create<luci::CircleConst>(); + auto output = g->nodes()->create<luci::CircleOutput>(); + + // Build up initial graph + auto graph_input1 = g->inputs()->create(); + input->index(graph_input1->index()); + + split->split_dim(split_dim); + split->input(input); + split->num_split(2); + + split_out1->input(split); + split_out1->index(0); + + split_out2->input(split); + split_out2->index(1); + + auto graph_output = g->outputs()->create(); + output->from(split_out1); + output->index(graph_output->index()); + + auto s = luci::inference_candidates(g.get()); + + ASSERT_EQ(6, s.size()); + ASSERT_TRUE(contains(s, input)); + ASSERT_TRUE(contains(s, split)); + ASSERT_TRUE(contains(s, split_out1)); + ASSERT_TRUE(contains(s, split_out2)); + ASSERT_TRUE(contains(s, split_dim)); + ASSERT_TRUE(contains(s, output)); +} + +TEST(LuciPassHelpersInferenceCandidates, inference_candidates_NEG) +{ + auto g = loco::make_graph(); + + // Create nodes + auto input = g->nodes()->create<luci::CircleInput>(); + auto split = g->nodes()->create<luci::CircleSplit>(); + auto split_out1 = g->nodes()->create<luci::CircleSplitOut>(); + auto split_out2 = g->nodes()->create<luci::CircleSplitOut>(); + auto split_dim = g->nodes()->create<luci::CircleConst>(); + auto relu1 = g->nodes()->create<luci::CircleRelu>(); + auto relu2 = g->nodes()->create<luci::CircleRelu>(); + auto output = g->nodes()->create<luci::CircleOutput>(); + + // Build up initial graph + auto graph_input1 = g->inputs()->create(); + input->index(graph_input1->index()); + + split->split_dim(split_dim); + split->input(input); + split->num_split(2); + + split_out1->input(split); + split_out1->index(0); + + split_out2->input(split); + split_out2->index(1); + + relu1->features(split_out2); + + relu2->features(input); + + auto graph_output = g->outputs()->create(); + output->from(split_out1); + output->index(graph_output->index()); + + auto s = luci::inference_candidates(g.get()); + + ASSERT_EQ(6, s.size()); + ASSERT_TRUE(contains(s, input)); + ASSERT_TRUE(contains(s, split)); + ASSERT_TRUE(contains(s, split_out1)); + ASSERT_TRUE(contains(s, split_out2)); + ASSERT_TRUE(contains(s, split_dim)); + ASSERT_TRUE(contains(s, output)); + ASSERT_FALSE(contains(s, relu1)); + ASSERT_FALSE(contains(s, relu2)); +} diff --git a/compiler/luci/pass/src/helpers/NodeFiller.cpp b/compiler/luci/pass/src/helpers/NodeFiller.cpp new file mode 100644 index 000000000..b1416655d --- /dev/null +++ b/compiler/luci/pass/src/helpers/NodeFiller.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 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 "NodeFiller.h" + +// NOTE Do NOT delete this file; this file enforces compiler to check whether 'NodeFiller.h' is +// complete. diff --git a/compiler/luci/pass/src/helpers/NodeFiller.h b/compiler/luci/pass/src/helpers/NodeFiller.h new file mode 100644 index 000000000..b80f085b0 --- /dev/null +++ b/compiler/luci/pass/src/helpers/NodeFiller.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021 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. + */ + +namespace luci +{ + +/** + * INTRODUCTION + * Binary operation f(x,y) is 'commutative' when + * f(x,y) == f(y,x) holds for all x, y. + * For examples, ADD, MUL and SQUARED_DIFFERENCE are commutative. + * These helpers make it easy to find commutative arguments of commutative node. + * + * HOW TO USE + * COMM_NODE *node; + * ARG_TYPE_1 *arg1; + * ARG_TYPE_2 *arg2; + * + * bool ok = fill(&arg1, &arg2).with_commutative_args_of(node); + * + * Result + * If 'node's commutative argument types are actually {ARG_TYPE_1, ARG_TYPE_2} + * (as a set), 'arg1' and 'arg2' set as actual 'node's arguments with matching + * type, and return value 'ok' is true. + * Otherwise, 'arg1' and 'arg2' not changed, 'ok' is false. + */ + +template <class ARG_TYPE_1, class ARG_TYPE_2> class NodeFiller final +{ +public: + NodeFiller(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) : _arg_1(arg_1), _arg_2(arg_2) + { + // DO NOTHING + } + + /** + * @return true When 'node's argument types are 'ARG_TYPE_1' and 'ARG_TYPE_2' + * In such case, it assign '_arg_1' and '_arg_2' to actual arguments + * + * @return false When 'node's argument types are NOT matched with 'ARG_TYPE_*' + * In such case, it does not amend '_arg_1' and '_arg_2' + * + * @require COMM_NODE has member x() and y() + */ + template <class COMM_NODE> bool with_commutative_args_of(const COMM_NODE *node); + +private: + ARG_TYPE_1 **_arg_1; + ARG_TYPE_2 **_arg_2; +}; + +template <class ARG_TYPE_1, class ARG_TYPE_2> +inline NodeFiller<ARG_TYPE_1, ARG_TYPE_2> fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) +{ + return NodeFiller<ARG_TYPE_1, ARG_TYPE_2>{arg_1, arg_2}; +} + +template <class ARG_TYPE_1, class ARG_TYPE_2> +template <class COMM_NODE> +bool NodeFiller<ARG_TYPE_1, ARG_TYPE_2>::with_commutative_args_of(const COMM_NODE *node) +{ + // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2 + { + auto x = dynamic_cast<ARG_TYPE_1 *>(node->x()); + auto y = dynamic_cast<ARG_TYPE_2 *>(node->y()); + + if (x && y) + { + *_arg_1 = x; + *_arg_2 = y; + return true; + } + } + + // Case 2) X == ARG_TYPE_2 / Y == ARG_TYPE_1 + { + auto x = dynamic_cast<ARG_TYPE_2 *>(node->x()); + auto y = dynamic_cast<ARG_TYPE_1 *>(node->y()); + + if (x && y) + { + *_arg_1 = y; + *_arg_2 = x; + return true; + } + } + + return false; +} + +} // namespace luci diff --git a/compiler/luci/pass/src/helpers/NodeFiller.test.cpp b/compiler/luci/pass/src/helpers/NodeFiller.test.cpp new file mode 100644 index 000000000..9bbc7f264 --- /dev/null +++ b/compiler/luci/pass/src/helpers/NodeFiller.test.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 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/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +#include "NodeFiller.h" + +TEST(NodeFillerTest, simple_test) +{ + luci::CircleConst maxi_const; + luci::CircleMinimum mini; + luci::CircleMaximum maxi; + maxi.x(&maxi_const); + maxi.y(&mini); + + luci::CircleConst *x = nullptr; + luci::CircleMinimum *y = nullptr; + + EXPECT_TRUE(luci::fill(&x, &y).with_commutative_args_of(&maxi)); + EXPECT_TRUE(x == &maxi_const); + EXPECT_TRUE(y == &mini); + + x = nullptr; + y = nullptr; + + EXPECT_TRUE(luci::fill(&y, &x).with_commutative_args_of(&maxi)); + EXPECT_TRUE(x == &maxi_const); + EXPECT_TRUE(y == &mini); +} + +TEST(NodeFillerTest, wrong_condition_NEG) +{ + luci::CircleConst add_const; + luci::CircleMinimum mini; + luci::CircleAdd add; + add.x(&add_const); + add.y(&mini); + + luci::CircleMul *x = nullptr; + luci::CircleMinimum *y = nullptr; + + EXPECT_FALSE(luci::fill(&x, &y).with_commutative_args_of(&add)); + EXPECT_FALSE(luci::fill(&y, &x).with_commutative_args_of(&add)); +} diff --git a/compiler/luci/pass/src/helpers/Strings.cpp b/compiler/luci/pass/src/helpers/Strings.cpp new file mode 100644 index 000000000..d020f6ddc --- /dev/null +++ b/compiler/luci/pass/src/helpers/Strings.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 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 "Strings.h" + +#include <algorithm> + +namespace luci +{ + +bool in_array(const std::string &str, const std::vector<std::string> &array) +{ + return std::find(array.begin(), array.end(), str) != array.end(); +} + +std::string to_string(const std::vector<std::string> &strings) +{ + assert(!strings.empty()); + + std::string res; + for (unsigned int i = 0; i < strings.size() - 1; i++) + res += strings[i] + ", "; + + res += strings[strings.size() - 1]; + return res; +} + +std::string to_lower_case(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; +} + +loco::DataType str_to_dtype(const std::string &str) +{ + if (to_lower_case(str).compare("uint8") == 0) + return loco::DataType::U8; + if (to_lower_case(str).compare("uint16") == 0) + return loco::DataType::U16; + if (to_lower_case(str).compare("uint32") == 0) + return loco::DataType::U32; + if (to_lower_case(str).compare("uint64") == 0) + return loco::DataType::U64; + + if (to_lower_case(str).compare("int8") == 0) + return loco::DataType::S8; + if (to_lower_case(str).compare("int16") == 0) + return loco::DataType::S16; + if (to_lower_case(str).compare("int32") == 0) + return loco::DataType::S32; + if (to_lower_case(str).compare("int64") == 0) + return loco::DataType::S64; + + if (to_lower_case(str).compare("float16") == 0) + return loco::DataType::FLOAT16; + if (to_lower_case(str).compare("float32") == 0) + return loco::DataType::FLOAT32; + if (to_lower_case(str).compare("float64") == 0) + return loco::DataType::FLOAT64; + + if (to_lower_case(str).compare("bool") == 0) + return loco::DataType::BOOL; + + return loco::DataType::Unknown; +} + +QuantizationGranularity str_to_granularity(const std::string &str) +{ + if (to_lower_case(str).compare("layer") == 0) + return QuantizationGranularity::LayerWise; + + if (to_lower_case(str).compare("channel") == 0) + return QuantizationGranularity::ChannelWise; + + throw std::runtime_error("Quantization granularity must be either 'layer' or 'channel'"); +} + +} // namespace luci diff --git a/compiler/luci/pass/src/helpers/Strings.h b/compiler/luci/pass/src/helpers/Strings.h new file mode 100644 index 000000000..793d137fb --- /dev/null +++ b/compiler/luci/pass/src/helpers/Strings.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 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 __LUCI_PASS_HELPERS_STRINGS_H__ +#define __LUCI_PASS_HELPERS_STRINGS_H__ + +#include "luci/Pass/QuantizationParameters.h" + +#include <loco.h> + +#include <vector> +#include <sstream> +#include <string> + +namespace luci +{ + +bool in_array(const std::string &, const std::vector<std::string> &); + +std::string to_string(const std::vector<std::string> &); + +std::string to_lower_case(std::string); + +loco::DataType str_to_dtype(const std::string &); + +QuantizationGranularity str_to_granularity(const std::string &); + +template <typename T> std::vector<T> csv_to_vector(const std::string &str) +{ + std::vector<T> ret; + std::istringstream is(str); + for (T i; is >> i;) + { + assert(i != ','); + ret.push_back(i); + if (is.peek() == ',') + is.ignore(); + } + return ret; +} + +} // namespace luci + +#endif // __LUCI_PASS_HELPERS_STRINGS_H__ diff --git a/compiler/luci/pass/src/helpers/Strings.test.cpp b/compiler/luci/pass/src/helpers/Strings.test.cpp new file mode 100644 index 000000000..f6bb48951 --- /dev/null +++ b/compiler/luci/pass/src/helpers/Strings.test.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 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 "Strings.h" + +#include "luci/Pass/QuantizationParameters.h" + +#include <gtest/gtest.h> + +TEST(StringsTest, str_to_dtype) +{ + ASSERT_EQ(loco::DataType::U8, luci::str_to_dtype("uint8")); + ASSERT_EQ(loco::DataType::U16, luci::str_to_dtype("uint16")); + ASSERT_EQ(loco::DataType::U32, luci::str_to_dtype("uint32")); + ASSERT_EQ(loco::DataType::U64, luci::str_to_dtype("uint64")); + + ASSERT_EQ(loco::DataType::S8, luci::str_to_dtype("int8")); + ASSERT_EQ(loco::DataType::S16, luci::str_to_dtype("int16")); + ASSERT_EQ(loco::DataType::S32, luci::str_to_dtype("int32")); + ASSERT_EQ(loco::DataType::S64, luci::str_to_dtype("int64")); + + ASSERT_EQ(loco::DataType::FLOAT16, luci::str_to_dtype("float16")); + ASSERT_EQ(loco::DataType::FLOAT32, luci::str_to_dtype("float32")); + ASSERT_EQ(loco::DataType::FLOAT64, luci::str_to_dtype("float64")); + + ASSERT_EQ(loco::DataType::BOOL, luci::str_to_dtype("bool")); + + ASSERT_EQ(loco::DataType::Unknown, luci::str_to_dtype("foo")); +} + +TEST(StringsTest, str_to_granularity) +{ + ASSERT_EQ(luci::QuantizationGranularity::LayerWise, luci::str_to_granularity("layer")); + ASSERT_EQ(luci::QuantizationGranularity::ChannelWise, luci::str_to_granularity("channel")); + + EXPECT_THROW(luci::str_to_granularity("foo"), std::runtime_error); +} + +TEST(StringsTest, csv_to_vector_int32) +{ + auto ret = luci::csv_to_vector<int32_t>("1,2,3"); + ASSERT_EQ(3, ret.size()); + ASSERT_EQ(1, ret.at(0)); + ASSERT_EQ(3, ret.at(2)); +} diff --git a/compiler/luci/pass/src/helpers/TypeMapper.cpp b/compiler/luci/pass/src/helpers/TypeMapper.cpp new file mode 100644 index 000000000..ffa0159dd --- /dev/null +++ b/compiler/luci/pass/src/helpers/TypeMapper.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 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 "TypeMapper.h" + +// NOTE Do NOT delete this file; this file enforces compiler to check whether 'TypeMapper.h' is +// complete. diff --git a/compiler/luci/pass/src/helpers/TypeMapper.h b/compiler/luci/pass/src/helpers/TypeMapper.h new file mode 100644 index 000000000..90760e95b --- /dev/null +++ b/compiler/luci/pass/src/helpers/TypeMapper.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 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 <loco/IR/DataType.h> + +#include <cstdint> + +namespace luci +{ + +/** + * @brief TypeMapper maps between c++ primitive data type and loco::DataType. + */ +template <typename T> struct TypeMapper +{ + static constexpr loco::DataType get() { return loco::DataType::Unknown; } +}; + +template <> struct TypeMapper<float> +{ + static constexpr loco::DataType get() { return loco::DataType::FLOAT32; } +}; + +template <> struct TypeMapper<uint8_t> +{ + static constexpr loco::DataType get() { return loco::DataType::U8; } +}; + +template <> struct TypeMapper<uint16_t> +{ + static constexpr loco::DataType get() { return loco::DataType::U16; } +}; + +template <> struct TypeMapper<uint32_t> +{ + static constexpr loco::DataType get() { return loco::DataType::U32; } +}; + +template <> struct TypeMapper<uint64_t> +{ + static constexpr loco::DataType get() { return loco::DataType::U64; } +}; + +template <> struct TypeMapper<int8_t> +{ + static constexpr loco::DataType get() { return loco::DataType::S8; } +}; + +template <> struct TypeMapper<int16_t> +{ + static constexpr loco::DataType get() { return loco::DataType::S16; } +}; + +template <> struct TypeMapper<int32_t> +{ + static constexpr loco::DataType get() { return loco::DataType::S32; } +}; + +template <> struct TypeMapper<int64_t> +{ + static constexpr loco::DataType get() { return loco::DataType::S64; } +}; + +} // namespace luci diff --git a/compiler/luci/pass/src/helpers/TypeMapper.test.cpp b/compiler/luci/pass/src/helpers/TypeMapper.test.cpp new file mode 100644 index 000000000..a7ac08a63 --- /dev/null +++ b/compiler/luci/pass/src/helpers/TypeMapper.test.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 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/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +#include "TypeMapper.h" + +#include <vector> + +namespace +{ + +template <typename T> bool fill_const_node(luci::CircleConst *node, std::vector<T> &data) +{ + if (node->dtype() != luci::TypeMapper<T>::get()) + return false; + + node->size<luci::TypeMapper<T>::get()>(data.size()); + for (uint32_t i = 0; i < data.size(); i++) + { + node->at<luci::TypeMapper<T>::get()>(i) = data.at(i); + } + + return true; +} + +class STRANGER +{ +}; + +} // namespace + +TEST(TypeMapperTest, simple_test) +{ + EXPECT_EQ(loco::DataType::FLOAT32, luci::TypeMapper<float>::get()); + EXPECT_EQ(loco::DataType::U8, luci::TypeMapper<uint8_t>::get()); + EXPECT_EQ(loco::DataType::U16, luci::TypeMapper<uint16_t>::get()); + EXPECT_EQ(loco::DataType::U32, luci::TypeMapper<uint32_t>::get()); + EXPECT_EQ(loco::DataType::U64, luci::TypeMapper<uint64_t>::get()); + EXPECT_EQ(loco::DataType::S8, luci::TypeMapper<int8_t>::get()); + EXPECT_EQ(loco::DataType::S16, luci::TypeMapper<int16_t>::get()); + EXPECT_EQ(loco::DataType::S32, luci::TypeMapper<int32_t>::get()); + EXPECT_EQ(loco::DataType::S64, luci::TypeMapper<int64_t>::get()); +} + +TEST(TypeMapperTest, with_template_test) +{ + std::vector<int32_t> int32_vec{0, 1, 2, 3, 4, 5, 6, 7}; + luci::CircleConst const_node; + const_node.dtype(loco::DataType::S32); + EXPECT_TRUE(fill_const_node(&const_node, int32_vec)); + EXPECT_EQ(8, const_node.size<loco::DataType::S32>()); + EXPECT_EQ(0, const_node.at<loco::DataType::S32>(0)); + EXPECT_EQ(1, const_node.at<loco::DataType::S32>(1)); + EXPECT_EQ(2, const_node.at<loco::DataType::S32>(2)); + EXPECT_EQ(3, const_node.at<loco::DataType::S32>(3)); + EXPECT_EQ(4, const_node.at<loco::DataType::S32>(4)); + EXPECT_EQ(5, const_node.at<loco::DataType::S32>(5)); + EXPECT_EQ(6, const_node.at<loco::DataType::S32>(6)); + EXPECT_EQ(7, const_node.at<loco::DataType::S32>(7)); + + std::vector<float> f32_vec{0.0, 1.1, 2.2, 3.3, 4.4, 5.5}; + const_node.dtype(loco::DataType::FLOAT32); + EXPECT_FALSE(fill_const_node(&const_node, int32_vec)); + EXPECT_TRUE(fill_const_node(&const_node, f32_vec)); + EXPECT_EQ(6, const_node.size<loco::DataType::FLOAT32>()); + EXPECT_FLOAT_EQ(0.0, const_node.at<loco::DataType::FLOAT32>(0)); + EXPECT_FLOAT_EQ(1.1, const_node.at<loco::DataType::FLOAT32>(1)); + EXPECT_FLOAT_EQ(2.2, const_node.at<loco::DataType::FLOAT32>(2)); + EXPECT_FLOAT_EQ(3.3, const_node.at<loco::DataType::FLOAT32>(3)); + EXPECT_FLOAT_EQ(4.4, const_node.at<loco::DataType::FLOAT32>(4)); + EXPECT_FLOAT_EQ(5.5, const_node.at<loco::DataType::FLOAT32>(5)); +} + +TEST(TypeMapperTest, wrong_condition_NEG) +{ + EXPECT_EQ(loco::DataType::Unknown, luci::TypeMapper<STRANGER>::get()); +} diff --git a/compiler/luci/pass/src/test/TestFirstNode.h b/compiler/luci/pass/src/test/TestFirstNode.h new file mode 100644 index 000000000..21f859fcd --- /dev/null +++ b/compiler/luci/pass/src/test/TestFirstNode.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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 __LUCI_PASS_TEST_FIRST_NODE_H__ +#define __LUCI_PASS_TEST_FIRST_NODE_H__ + +#include <luci/IR/CircleNodes.h> + +#include <loco.h> + +namespace luci +{ +namespace test +{ + +template <class T> T *first_node(loco::Graph *g) +{ + for (auto node : loco::active_nodes(loco::output_nodes(g))) + { + auto target_node = dynamic_cast<T *>(node); + if (target_node != nullptr) + return target_node; + } + return nullptr; +} + +} // namespace test +} // namespace luci + +#endif // __LUCI_PASS_TEST_FIRST_NODE_H__ diff --git a/compiler/luci/pass/src/test/TestFirstNode.test.cpp b/compiler/luci/pass/src/test/TestFirstNode.test.cpp new file mode 100644 index 000000000..b07ac6199 --- /dev/null +++ b/compiler/luci/pass/src/test/TestFirstNode.test.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 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 "TestFirstNode.h" + +// This file validates "TestFirstNode.h". Pleaes DO NOT remove this file. diff --git a/compiler/luci/pass/src/test/TestIOGraph.h b/compiler/luci/pass/src/test/TestIOGraph.h new file mode 100644 index 000000000..b1fc41f90 --- /dev/null +++ b/compiler/luci/pass/src/test/TestIOGraph.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021 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 __LUCI_PASS_TEST_IO_GRAPH_H__ +#define __LUCI_PASS_TEST_IO_GRAPH_H__ + +#include "TestShape.h" + +#include <luci/IR/CircleNodes.h> + +namespace luci +{ +namespace test +{ + +/** + * @brief Graphlet with Inputs and loco::Graph for multiple inputs + * @note Every Graph will have Input(s) and Output(s) + * We put loco::Graph only in IsGraphlet not to declare separate + * class for loco::Graph + */ +template <unsigned N> class TestIsGraphlet +{ +public: + TestIsGraphlet() + { + for (uint32_t n = 0; n < N; ++n) + { + _graph_inputs[n] = nullptr; + _inputs[n] = nullptr; + } + } + +public: + virtual void init(loco::Graph *g, const ShapeU32 shape_in) + { + for (uint32_t n = 0; n < N; ++n) + { + _graph_inputs[n] = g->inputs()->create(); + + _inputs[n] = g->nodes()->create<luci::CircleInput>(); + _inputs[n]->shape(shape_in); + _inputs[n]->shape_status(luci::ShapeStatus::VALID); + _inputs[n]->dtype(loco::DataType::FLOAT32); + _inputs[n]->name("input_" + std::to_string(n)); + + _inputs[n]->index(_graph_inputs[n]->index()); + + auto input_shape = std::make_unique<loco::TensorShape>(); + set_shape_vector(input_shape.get(), shape_in); + _graph_inputs[n]->shape(std::move(input_shape)); + _graph_inputs[n]->dtype(loco::DataType::FLOAT32); + } + } + +public: + loco::Graph *g(void) { return &_g; } + luci::CircleInput *input(int idx) { return _inputs[idx]; } + +protected: + loco::Graph _g; + std::array<loco::GraphInput *, N> _graph_inputs; + std::array<luci::CircleInput *, N> _inputs; +}; + +/** + * @brief Graphlet with one Input + */ +class TestIGraphlet : public TestIsGraphlet<1> +{ +public: + luci::CircleInput *input() { return _inputs[0]; } +}; + +/** + * @brief Graphlet with Outputs for multiple outputs + */ +template <unsigned N> class TestOsGraphlet +{ +public: + TestOsGraphlet() + { + for (uint32_t n = 0; n < N; ++n) + { + _graph_outputs[n] = nullptr; + _outputs[n] = nullptr; + } + } + +public: + virtual void init(loco::Graph *g, const ShapeU32 shape_out) + { + for (uint32_t n = 0; n < N; ++n) + { + _graph_outputs[n] = g->outputs()->create(); + + _outputs[n] = g->nodes()->create<luci::CircleOutput>(); + _outputs[n]->shape(shape_out); + _outputs[n]->shape_status(luci::ShapeStatus::VALID); + _outputs[n]->dtype(loco::DataType::FLOAT32); + _outputs[n]->name("output_" + std::to_string(n)); + + _outputs[n]->index(_graph_outputs[n]->index()); + + auto output_shape = std::make_unique<loco::TensorShape>(); + set_shape_vector(output_shape.get(), shape_out); + _graph_outputs[n]->shape(std::move(output_shape)); + _graph_outputs[n]->dtype(loco::DataType::FLOAT32); + } + } + +public: + luci::CircleOutput *output(int idx) { return _outputs[idx]; } + +protected: + std::array<loco::GraphOutput *, N> _graph_outputs; + std::array<luci::CircleOutput *, N> _outputs; +}; + +/** + * @brief Graphlet with one Output + */ +class TestOGraphlet : public TestOsGraphlet<1> +{ +public: + luci::CircleOutput *output() { return _outputs[0]; } +}; + +/** + * @brief Graph with Input and Output + */ +class TestIOGraph : public TestIGraphlet, public TestOGraphlet +{ +public: + TestIOGraph() = default; + +public: + virtual void init(const ShapeU32 shape_in, const ShapeU32 shape_out) + { + TestIsGraphlet<1>::init(g(), shape_in); + TestOsGraphlet<1>::init(g(), shape_out); + } +}; + +} // namespace test +} // namespace luci + +#endif // __LUCI_PASS_TEST_IO_GRAPH_H__ diff --git a/compiler/luci/pass/src/test/TestIOGraph.test.cpp b/compiler/luci/pass/src/test/TestIOGraph.test.cpp new file mode 100644 index 000000000..e58a13f2b --- /dev/null +++ b/compiler/luci/pass/src/test/TestIOGraph.test.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2021 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 "TestIOGraph.h" + +// This file validates "TestIOGraph.h". Pleaes DO NOT remove this file. diff --git a/compiler/luci/export/src/TypeBridge.h b/compiler/luci/pass/src/test/TestShape.h index a63fbce54..ccc55c9da 100644 --- a/compiler/luci/export/src/TypeBridge.h +++ b/compiler/luci/pass/src/test/TestShape.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,31 +14,27 @@ * limitations under the License. */ -#ifndef __TYPE_BRIDGE_H__ -#define __TYPE_BRIDGE_H__ +#ifndef __LUCI_PASS_TEST_SHAPE_H__ +#define __LUCI_PASS_TEST_SHAPE_H__ #include <luci/IR/CircleNode.h> -#include <loco.h> +#include <initializer_list> namespace luci { +namespace test +{ -/** - * @brief node_shape() will return loco::TensorShape of CircleNode - */ -loco::TensorShape node_shape(CircleNode *node); +using ShapeU32 = std::initializer_list<uint32_t>; +using ShapeI32 = std::initializer_list<int32_t>; -/** - * @brief node_dtype() will return loco::DataType of CircleNode - */ -loco::DataType node_dtype(CircleNode *node); +void set_shape_vector(loco::TensorShape *shape, const ShapeU32 &values); +void set_shape_vector(luci::CircleConst *const_node, const ShapeI32 &values); -/** - * @brief copy_shape_dtype() will copy shape and dtype inference data to CircleNode - */ -void copy_shape_dtype(loco::Graph *graph); +uint32_t num_elements(const ShapeU32 shape); +} // namespace test } // namespace luci -#endif // __TYPE_BRIDGE_H__ +#endif // __LUCI_PASS_TEST_SHAPE_H__ diff --git a/compiler/luci/pass/src/test/TestShape.test.cpp b/compiler/luci/pass/src/test/TestShape.test.cpp new file mode 100644 index 000000000..39790c614 --- /dev/null +++ b/compiler/luci/pass/src/test/TestShape.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 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 "TestShape.h" + +/** + * @note This file does not hold any test cases but provides methods for tests + */ + +namespace luci +{ +namespace test +{ + +void set_shape_vector(loco::TensorShape *shape, const ShapeU32 &values) +{ + uint32_t r = 0; + shape->rank(values.size()); + for (auto v : values) + shape->dim(r++).set(v); +} + +void set_shape_vector(luci::CircleConst *const_node, const ShapeI32 &values) +{ + const_node->rank(1); + const_node->dim(0).set(values.size()); + const_node->shape_status(luci::ShapeStatus::VALID); + const_node->dtype(loco::DataType::S32); + const_node->size<loco::DataType::S32>(values.size()); + uint32_t idx = 0; + for (auto val : values) + const_node->at<loco::DataType::S32>(idx++) = val; +} + +uint32_t num_elements(const ShapeU32 shape) +{ + uint32_t result = 1; + for (auto val : shape) + result = result * val; + return result; +} + +} // namespace test +} // namespace luci diff --git a/compiler/luci/profile/CMakeLists.txt b/compiler/luci/profile/CMakeLists.txt new file mode 100644 index 000000000..f2c6665da --- /dev/null +++ b/compiler/luci/profile/CMakeLists.txt @@ -0,0 +1,22 @@ +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE TESTS "src/*.test.cpp") +list(REMOVE_ITEM SOURCES ${TESTS}) + +add_library(luci_profile SHARED ${SOURCES}) +target_include_directories(luci_profile PRIVATE src) +target_include_directories(luci_profile PUBLIC include) +target_link_libraries(luci_profile PUBLIC loco) +target_link_libraries(luci_profile PUBLIC luci_lang) + +install(TARGETS luci_profile DESTINATION lib) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(luci_profile_test ${TESTS}) +target_include_directories(luci_profile_test PRIVATE src) +target_link_libraries(luci_profile_test luci_lang) +target_link_libraries(luci_profile_test luci_profile) diff --git a/compiler/luci/profile/README.md b/compiler/luci/profile/README.md new file mode 100644 index 000000000..577e60a7c --- /dev/null +++ b/compiler/luci/profile/README.md @@ -0,0 +1,119 @@ +# luci-profile + +`luci-profile` provides profiling related items. + +## CircleNodeOrigin + +`CircleNodeOrigin` allow us know where some node is originated from. + +Let's assume following graph transformations are done. + +``` + | | | + [node1] --------+ | | +(id = 1) | | | + | +--------> [node5] ----------------> [node6] + | | (origin = [1,2]) (origin = [1,2]) + [node2] --------+ | | +(id = 2) | | + | | | + [node3] -----------------> [node3] --------+-------> [node3] +(id = 3) (origin = [3]) | (origin = [3,4]) + | | | | + [node4] -----------------> [node4] --------+ | +(id = 4) (origin = [4]) | + | | | + +<Circle1> -- optimizer --> <circle2> -- quantizer --> <circle3> +``` + +The most important purpose of using `CircleNodeOrigin` is preserving origin information. +Following changes show how origin information is preserved even after graph is transformed. + +- `node3` + - `node4` is absorbed to **existing** `node3`. + - origin of `node4` is absorbed to origin of `node3`. +- `node5` + - `node1` and `node2` are fused to **newly created** `node5`. + - origin of `node1` and `node2` are inherited to origin of `node4`. +- `node6` + - `node5` is **replaced with newly created** `node6`. + - origin of `node5` is copied to origin of `node6`. + +**Therefore, when using `CircleNodeOrigin`, please aware of the most important principle. "Preserve origin information"** + +Next items are about implementation details to store the origin information. + +### Source Table + +Source table includes a set of id and name of origin node. + +#### Binary format + +``` +[ entry_number : uint32_t ] +[ id : uint32_t ][ length : uint32_t ][ data : char * length ] * entry_number +``` +- entry_number : The number of entries + - Each entry consists of id, length, and data. +- id : ID of origin node +- length : Length of data +- data : Name of origin node **(null-terminated string)** + +#### In-memory format +```cpp +// size = entry_number +std::map<uint32_t /* id */, std::string /* name */> +``` + +#### Example + +Following example means "Name of origin 1 is node1". + +``` +[Binary Format] + 0x01 00 00 00 0x01 00 00 00 0x06 00 00 00 0x6e 0x6f 0x64 0x65 0x31 00 + ------------- ------------- ------------- ---- ---- ---- ---- ---- ---- +entry_number=1 id=1 length=6 'n' 'o' 'd' 'e' '1' '\0' +``` +```cpp +[In-memory Format] +std::map<uint32_t, std::string>({1, "node1"}); +``` + +### Op Table + +Op table includes a set of id of operation and id(s) of operation's origin nodes. + +#### Binary format + +Op table is stored in circle file as binary with following format. +``` +[ entry_number : uint32_t ] +[ id : uint32_t ][ node_num : uint32_t ][ node_ids : uint32_t * node_num ] * entry_number +``` +- entry_number : The number of entries + - Each entry consists of id, node_num, and node_ids. +- id : ID of operation in circle model file +- node_num : The number of operation's origin nodes +- node_ids : Set of IDs of origin nodes + +#### In-memory format +```cpp +std::map<uint32_t /* id */, std::set<uint32_t> /* node_ids */> +``` + +#### Example + +Following example means "Operation 5 is originated from origin 1 and origin 2". + +``` +[Binary Format] + 0x01 00 00 00 0x05 00 00 00 0x02 00 00 00 0x01 00 00 00 0x02 00 00 00 + ------------- ------------- ------------- --------------------------- +entry_number=1 id=5 node_num=2 node_ids : 1, 2 +``` +```cpp +[In-memory Format] +std::map<uint32_t, std::set<uint32_t>>({5, std::set{1, 2}}); +``` diff --git a/compiler/luci/pass/src/FuseActivationFunctionPassInternal.h b/compiler/luci/profile/include/luci/Profile/CircleNodeID.h index 0cfb9d507..165866bcf 100644 --- a/compiler/luci/pass/src/FuseActivationFunctionPassInternal.h +++ b/compiler/luci/profile/include/luci/Profile/CircleNodeID.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,18 +14,22 @@ * limitations under the License. */ -#ifndef __LUCI_CIRCLE_FUSE_ACTIVATION_FUNCTION_PASS_INTERNAL_H__ -#define __LUCI_CIRCLE_FUSE_ACTIVATION_FUNCTION_PASS_INTERNAL_H__ +#ifndef __LUCI_PROFILE_CIRCLE_NODE_ID_H__ +#define __LUCI_PROFILE_CIRCLE_NODE_ID_H__ -#include <luci/IR/CircleNodes.h> +#include <luci/IR/CircleNode.h> namespace luci { -// Fuse activation function with preceding Op -/// @return true if success -bool fuse_activation_function(luci::CircleNode *node); +using CircleNodeID = uint32_t; + +bool has_node_id(const luci::CircleNode *circle_node); + +void set_node_id(luci::CircleNode *circle_node, CircleNodeID id); + +CircleNodeID get_node_id(const luci::CircleNode *circle_node); } // namespace luci -#endif // __LUCI_CIRCLE_FUSE_ACTIVATION_FUNCTION_PASS_INTERNAL_H__ +#endif // __LUCI_PROFILE_CIRCLE_NODE_ID_H__ diff --git a/compiler/luci/profile/include/luci/Profile/CircleNodeOrigin.h b/compiler/luci/profile/include/luci/Profile/CircleNodeOrigin.h new file mode 100644 index 000000000..2d6558c92 --- /dev/null +++ b/compiler/luci/profile/include/luci/Profile/CircleNodeOrigin.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 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 __LUCI_PROFILE_CIRCLE_NODE_ORIGIN_H__ +#define __LUCI_PROFILE_CIRCLE_NODE_ORIGIN_H__ + +#include "CircleNodeID.h" + +#include <luci/IR/CircleNode.h> + +#include <set> + +namespace luci +{ + +class CircleNodeOrigin +{ +protected: + struct Source + { + public: + std::string name(void) const { return _name; } + void name(const std::string &name) { _name = name; } + + uint32_t id(void) const { return _id; } + void id(const uint32_t id) { _id = id; } + + private: + std::string _name; + uint32_t _id = 0; + }; + +public: + virtual std::set<const Source *> sources(void) const = 0; +}; + +std::shared_ptr<CircleNodeOrigin> single_origin(uint32_t id, const std::string &name); + +std::shared_ptr<CircleNodeOrigin> +composite_origin(const std::initializer_list<std::shared_ptr<CircleNodeOrigin>> origins); + +std::shared_ptr<CircleNodeOrigin> +composite_origin(const std::vector<std::shared_ptr<CircleNodeOrigin>> &origins); + +} // namespace luci + +namespace luci +{ + +bool has_origin(const luci::CircleNode *circle_node); + +void add_origin(luci::CircleNode *circle_node, const std::shared_ptr<CircleNodeOrigin> origin); + +// NOTE When circle_node does not have origin, nullptr is returned +const std::shared_ptr<luci::CircleNodeOrigin> get_origin(const luci::CircleNode *circle_node); + +} // namespace luci + +#endif // __LUCI_PROFILE_CIRCLE_NODE_ORIGIN_H__ diff --git a/compiler/luci/profile/src/CircleNodeID.cpp b/compiler/luci/profile/src/CircleNodeID.cpp new file mode 100644 index 000000000..750b36cae --- /dev/null +++ b/compiler/luci/profile/src/CircleNodeID.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 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/Profile/CircleNodeID.h" + +#include <loco.h> + +#include <stdexcept> + +namespace +{ + +/** + * @brief Set annotation for circle node id + * @note Once CircleNodeID is annotated, it should not be changed. + * If CircleNodeID is needed to be changed, create new CircleNodeID. + */ +class CircleNodeIDAnnotation final : public loco::NodeAnnotation +{ +public: + CircleNodeIDAnnotation() = delete; + + CircleNodeIDAnnotation(luci::CircleNodeID node_id) : _node_id{node_id} + { + // Do nothing + } + +public: + luci::CircleNodeID node_id(void) const { return _node_id; } + // No setter + +private: + luci::CircleNodeID _node_id; +}; + +} // namespace + +namespace luci +{ + +bool has_node_id(const luci::CircleNode *circle_node) +{ + return circle_node->annot<CircleNodeIDAnnotation>() != nullptr; +} + +void set_node_id(luci::CircleNode *circle_node, luci::CircleNodeID id) +{ + circle_node->annot<CircleNodeIDAnnotation>(nullptr); + circle_node->annot(std::make_unique<CircleNodeIDAnnotation>(id)); +} + +luci::CircleNodeID get_node_id(const luci::CircleNode *circle_node) +{ + if (!has_node_id(circle_node)) + throw std::runtime_error("Cannot find CircleNodeID"); + + return circle_node->annot<CircleNodeIDAnnotation>()->node_id(); +} + +} // namespace luci diff --git a/compiler/luci/profile/src/CircleNodeID.test.cpp b/compiler/luci/profile/src/CircleNodeID.test.cpp new file mode 100644 index 000000000..d80c09b2c --- /dev/null +++ b/compiler/luci/profile/src/CircleNodeID.test.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 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/Profile/CircleNodeID.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +TEST(LuciCircleNodeID, simple_circle_node_id) +{ + auto g = loco::make_graph(); + auto add = g->nodes()->create<luci::CircleAdd>(); + + ASSERT_FALSE(has_node_id(add)); + + set_node_id(add, 3); + + ASSERT_TRUE(has_node_id(add)); + ASSERT_EQ(3, get_node_id(add)); +} + +TEST(LuciCircleNodeID, simple_circle_node_id_NEG) +{ + auto g = loco::make_graph(); + auto add = g->nodes()->create<luci::CircleAdd>(); + + ASSERT_FALSE(has_node_id(add)); + + ASSERT_ANY_THROW(get_node_id(add)); +} diff --git a/compiler/luci/profile/src/CircleNodeOrigin.cpp b/compiler/luci/profile/src/CircleNodeOrigin.cpp new file mode 100644 index 000000000..0a731a9ad --- /dev/null +++ b/compiler/luci/profile/src/CircleNodeOrigin.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2021 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/Profile/CircleNodeOrigin.h" + +#include <loco.h> + +#include <cassert> +#include <vector> + +namespace +{ + +/** + * @brief Set annotation for recording origin information + * @note Once CircleNodeOrigin is annotated, it should not be changed. + * If CircleNodeOrigin is needed to be changed, create new CircleNodeOrigin. + */ +class CircleNodeOriginAnnotation final : public loco::NodeAnnotation +{ +public: + CircleNodeOriginAnnotation() = delete; + + CircleNodeOriginAnnotation(const std::shared_ptr<luci::CircleNodeOrigin> origin) : _origin(origin) + { + // Do nothing + } + +public: + const std::shared_ptr<luci::CircleNodeOrigin> origin(void) const { return _origin; } + // No setter + +private: + const std::shared_ptr<luci::CircleNodeOrigin> _origin; +}; + +} // namespace + +namespace +{ + +class SingleOrigin final : public luci::CircleNodeOrigin +{ +public: + SingleOrigin() = delete; + + SingleOrigin(uint32_t id, const std::string &name) + { + _source.id(id); + _source.name(name); + } + +public: + std::set<const Source *> sources(void) const final + { + std::set<const Source *> res; + res.emplace(&_source); + return res; + } + +private: + Source _source; +}; + +class CompositeOrigin final : public luci::CircleNodeOrigin +{ +public: + CompositeOrigin() = delete; + + template <typename T> CompositeOrigin(T origins) + { + if (origins.size() == 0) + throw std::invalid_argument("No origins provided"); + + for (auto &origin : origins) + { + if (origin != nullptr) + _origins.emplace_back(origin); + } + } + +public: + std::set<const Source *> sources(void) const final + { + std::set<const Source *> res; + + for (auto &origin : _origins) + { + for (auto source : origin->sources()) + { + res.emplace(source); + } + } + + return res; + } + +private: + std::vector<std::shared_ptr<CircleNodeOrigin>> _origins; +}; + +} // namespace + +namespace luci +{ + +std::shared_ptr<CircleNodeOrigin> single_origin(uint32_t id, const std::string &name) +{ + return std::make_shared<SingleOrigin>(id, name); +} + +std::shared_ptr<CircleNodeOrigin> +composite_origin(const std::initializer_list<std::shared_ptr<CircleNodeOrigin>> origins) +{ + return std::make_shared<CompositeOrigin>(origins); +} + +std::shared_ptr<CircleNodeOrigin> +composite_origin(const std::vector<std::shared_ptr<CircleNodeOrigin>> &origins) +{ + return std::make_shared<CompositeOrigin>(origins); +} + +} // namespace luci + +namespace luci +{ + +bool has_origin(const luci::CircleNode *circle_node) +{ + return circle_node->annot<CircleNodeOriginAnnotation>() != nullptr; +} + +/** + * @brief 'origin' is added to the existing origin of circle_node. + * @note If 'origin' is nullptr, nothing is changed. + * For more detail, please refer to CompositeOrigin constructor. + */ +void add_origin(luci::CircleNode *circle_node, const std::shared_ptr<CircleNodeOrigin> origin) +{ + auto new_origin = composite_origin({get_origin(circle_node), origin}); + circle_node->annot<CircleNodeOriginAnnotation>(nullptr); + circle_node->annot(std::make_unique<CircleNodeOriginAnnotation>(new_origin)); +} + +const std::shared_ptr<luci::CircleNodeOrigin> get_origin(const luci::CircleNode *circle_node) +{ + if (!has_origin(circle_node)) + return nullptr; + + assert(circle_node->annot<CircleNodeOriginAnnotation>()->origin() != nullptr); + return circle_node->annot<CircleNodeOriginAnnotation>()->origin(); +} + +} // namespace luci diff --git a/compiler/luci/profile/src/CircleNodeOrigin.test.cpp b/compiler/luci/profile/src/CircleNodeOrigin.test.cpp new file mode 100644 index 000000000..34618e1ab --- /dev/null +++ b/compiler/luci/profile/src/CircleNodeOrigin.test.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021 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/Profile/CircleNodeID.h" +#include "luci/Profile/CircleNodeOrigin.h" + +#include <luci/IR/CircleNodes.h> + +#include <gtest/gtest.h> + +TEST(LuciCircleNodeOrigin, simple_single_origin) +{ + auto g = loco::make_graph(); + auto add = g->nodes()->create<luci::CircleAdd>(); + + ASSERT_FALSE(has_origin(add)); + + auto origin = luci::single_origin(3, "add"); + add_origin(add, origin); + + ASSERT_TRUE(has_origin(add)); + + auto sources = get_origin(add)->sources(); + ASSERT_EQ(1, sources.size()); + for (auto source : sources) + { + ASSERT_EQ(3, source->id()); + ASSERT_EQ(0, source->name().compare("add")); + } +} + +TEST(LuciCircleNodeOrigin, simple_composite_origin_with_initializer) +{ + auto g = loco::make_graph(); + auto mul = g->nodes()->create<luci::CircleMul>(); + + ASSERT_FALSE(has_origin(mul)); + + auto origin = + luci::composite_origin({luci::single_origin(3, "add"), luci::single_origin(7, "sub")}); + add_origin(mul, origin); + + ASSERT_TRUE(has_origin(mul)); + + bool add_origin_passed = false; + bool sub_origin_passed = false; + auto sources = get_origin(mul)->sources(); + ASSERT_EQ(2, sources.size()); + for (auto source : sources) + { + if (source->id() == 3 && source->name().compare("add") == 0) + add_origin_passed = true; + if (source->id() == 7 && source->name().compare("sub") == 0) + sub_origin_passed = true; + } + + ASSERT_EQ(true, add_origin_passed); + ASSERT_EQ(true, sub_origin_passed); +} + +TEST(LuciCircleNodeOrigin, simple_composite_origin_with_vector) +{ + auto g = loco::make_graph(); + auto mul = g->nodes()->create<luci::CircleMul>(); + + ASSERT_FALSE(has_origin(mul)); + + std::vector<std::shared_ptr<luci::CircleNodeOrigin>> vec; + vec.push_back(luci::single_origin(3, "add")); + vec.push_back(luci::single_origin(7, "sub")); + auto origin = luci::composite_origin(vec); + add_origin(mul, origin); + + ASSERT_TRUE(has_origin(mul)); + + bool add_origin_passed = false; + bool sub_origin_passed = false; + auto sources = get_origin(mul)->sources(); + ASSERT_EQ(2, sources.size()); + for (auto source : sources) + { + if (source->id() == 3 && source->name().compare("add") == 0) + add_origin_passed = true; + if (source->id() == 7 && source->name().compare("sub") == 0) + sub_origin_passed = true; + } + + ASSERT_EQ(true, add_origin_passed); + ASSERT_EQ(true, sub_origin_passed); +} + +TEST(LuciCircleNodeOrigin, composite_origin_empty_ctor_NEG) +{ + ASSERT_ANY_THROW(luci::composite_origin({})); +} diff --git a/compiler/luci/service/CMakeLists.txt b/compiler/luci/service/CMakeLists.txt index 9f50c9c4f..1c78031ab 100644 --- a/compiler/luci/service/CMakeLists.txt +++ b/compiler/luci/service/CMakeLists.txt @@ -22,4 +22,5 @@ nnas_find_package(GTest REQUIRED) GTest_AddTest(luci_service_test ${TESTS}) target_include_directories(luci_service_test PRIVATE src) target_link_libraries(luci_service_test luci_service) +target_link_libraries(luci_service_test luci_testhelper) target_link_libraries(luci_service_test oops) diff --git a/compiler/luci/service/include/luci/Service/CircleNodeClone.h b/compiler/luci/service/include/luci/Service/CircleNodeClone.h new file mode 100644 index 000000000..2429997cc --- /dev/null +++ b/compiler/luci/service/include/luci/Service/CircleNodeClone.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 __LUCI_CIRCLE_NODE_CLONE__ +#define __LUCI_CIRCLE_NODE_CLONE__ + +#include <luci/IR/CircleNodes.h> + +#include <loco/IR/Graph.h> + +namespace luci +{ + +/** + * @brief Copy common attributes of CircleNode from src to dst. + */ +void copy_common_attributes(const luci::CircleNode *src, luci::CircleNode *dst); + +/** + * @brief Return a new cloned CircleNode object with same attributes value of node to graph. + * @note Will return nullptr if clone has failed + */ +CircleNode *clone_node(const CircleNode *node, loco::Graph *graph); + +} // namespace luci + +#endif // __LUCI_CIRCLE_NODE_CLONE__ diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInference.h b/compiler/luci/service/include/luci/Service/CircleShapeInference.h index c301db5f4..60bc16e48 100644 --- a/compiler/luci/service/include/luci/Service/CircleShapeInference.h +++ b/compiler/luci/service/include/luci/Service/CircleShapeInference.h @@ -17,29 +17,15 @@ #ifndef __LUCI_CIRCLE_SHAPE_INFERENCE_H__ #define __LUCI_CIRCLE_SHAPE_INFERENCE_H__ -#include "ShapeDescription.h" - #include <loco/IR/Nodes.h> #include <luci/IR/CircleNodes.h> #include <luci/IR/CircleNodeVisitor.h> -#include <luci/Service/CircleShapeInferenceHelper.h> +#include <luci/Service/CircleShapeInferenceRule.h> namespace luci { -/** - * @brief Get the shape of each node as a node annotation - * - * HOW TO USE - * - * ShapeInference::get(g->nodes()->at(..)); - */ -struct ShapeInference -{ - static ShapeDescription get(loco::Node *node); -}; - namespace sinf // namespace for Shape Inference { @@ -52,7 +38,12 @@ class Algorithm final : public luci::CircleNodeVisitor<loco::TensorShape> { public: // TODO Remove this when all of visit function is implemented - loco::TensorShape visit(const luci::CircleNode *node) final { return sinf::circle_shape(node); } + loco::TensorShape visit(const luci::CircleNode *node) final + { + loco::NodeShape shape; + luci::CircleShapeInferenceRule().infer(node, shape); + return shape.as<loco::TensorShape>(); + } // loco::TensorShape visit(const luci::CircleAbs *node) final; // loco::TensorShape visit(const luci::CircleAdd *node) final; @@ -77,6 +68,7 @@ public: // loco::TensorShape visit(const luci::CircleEqual *node) final; // loco::TensorShape visit(const luci::CircleExp *node) final; // loco::TensorShape visit(const luci::CircleExpandDims *node) final; + // loco::TensorShape visit(const luci::CircleFakeQuant *node) final; // loco::TensorShape visit(const luci::CircleFill *node) final; // loco::TensorShape visit(const luci::CircleFloor *node) final; // loco::TensorShape visit(const luci::CircleFloorDiv *node) final; @@ -106,10 +98,12 @@ public: // loco::TensorShape visit(const luci::CircleMean *node) final; // loco::TensorShape visit(const luci::CircleMinimum *node) final; // loco::TensorShape visit(const luci::CircleMirrorPad *node) final; + // loco::TensorShape visit(const luci::CircleMul *node) final; // loco::TensorShape visit(const luci::CircleNeg *node) final; // loco::TensorShape visit(const luci::CircleNonMaxSuppressionV4 *node) final; // loco::TensorShape visit(const luci::CircleNonMaxSuppressionV5 *node) final; // loco::TensorShape visit(const luci::CircleNotEqual *node) final; + // loco::TensorShape visit(const luci::CircleOneHot *node) final; // loco::TensorShape visit(const luci::CirclePack *node) final; // loco::TensorShape visit(const luci::CirclePad *node) final; // loco::TensorShape visit(const luci::CirclePadV2 *node) final; @@ -117,8 +111,6 @@ public: // loco::TensorShape visit(const luci::CirclePRelu *node) final; // loco::TensorShape visit(const luci::CircleRange *node) final; // loco::TensorShape visit(const luci::CircleRank *node) final; - // loco::TensorShape visit(const luci::CircleMul *node) final; - // loco::TensorShape visit(const luci::CircleOneHot *node) final; // loco::TensorShape visit(const luci::CircleReduceAny *node) final; // loco::TensorShape visit(const luci::CircleReduceMax *node) final; // loco::TensorShape visit(const luci::CircleReduceMin *node) final; @@ -171,14 +163,14 @@ public: // loco::TensorShape visit(const luci::CircleInstanceNorm *node) final; // Virtual + // loco::TensorShape visit(const luci::CircleCustomOut *node) final; + loco::TensorShape visit(const luci::CircleIfOut *node) final; // 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::CircleOutputDummy *node) final; // loco::TensorShape visit(const luci::CircleOutputExclude *node) final; - // loco::TensorShape visit(const luci::CircleCustomOut *node) final; - // loco::TensorShape visit(const luci::CircleIfOut *node) final; - // loco::TensorShape visit(const luci::CircleNonMaxSuppressionV4Out *node) final; - // loco::TensorShape visit(const luci::CircleNonMaxSuppressionV5Out *node) final; // loco::TensorShape visit(const luci::CircleSplitOut *node) final; // loco::TensorShape visit(const luci::CircleSplitVOut *node) final; // loco::TensorShape visit(const luci::CircleTopKV2Out *node) final; diff --git a/compiler/luci/service/include/luci/Service/CircleShapeSignatureInference.h b/compiler/luci/service/include/luci/Service/CircleShapeSignatureInference.h deleted file mode 100644 index f7ea89bb8..000000000 --- a/compiler/luci/service/include/luci/Service/CircleShapeSignatureInference.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2020 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 __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_H__ -#define __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_H__ - -#include <luci/IR/CircleNodes.h> -#include <luci/IR/CircleNodeVisitor.h> -#include <luci/IR/CircleShapeSignature.h> -#include <luci/Service/CircleShapeSignatureInferenceHelper.h> - -namespace luci -{ - -namespace ssinf // namespace for Shape Signature Inference -{ - -struct Rule -{ - bool infer(const luci::CircleNode *, ShapeSignature &) const; -}; - -class Algorithm final : public luci::CircleNodeVisitor<ShapeSignature> -{ -public: - // TODO Remove this when visit function is implemented for all the operations. - ShapeSignature visit(const luci::CircleNode *node) final { return node->shape_signature(); } - - // ShapeSignature visit(const luci::CircleAbs *node) final; - // ShapeSignature visit(const luci::CircleAdd *node) final; - // ShapeSignature visit(const luci::CircleAddN *node) final; - // ShapeSignature visit(const luci::CircleArgMax *node) final; - // ShapeSignature visit(const luci::CircleArgMin *node) final; - // ShapeSignature visit(const luci::CircleAveragePool2D *node) final; - // ShapeSignature visit(const luci::CircleBatchMatMul *node) final; - // ShapeSignature visit(const luci::CircleBatchToSpaceND *node) final; - // ShapeSignature visit(const luci::CircleCast *node) final; - // ShapeSignature visit(const luci::CircleCeil *node) final; - // ShapeSignature visit(const luci::CircleConcatenation *node) final; - // ShapeSignature visit(const luci::CircleConst *node) final; - // ShapeSignature visit(const luci::CircleConv2D *node) final; - // ShapeSignature visit(const luci::CircleCos *node) final; - // ShapeSignature visit(const luci::CircleCustom *node) final; - // ShapeSignature visit(const luci::CircleDepthToSpace *node) final; - // ShapeSignature visit(const luci::CircleDepthwiseConv2D *node) final; - // ShapeSignature visit(const luci::CircleDequantize *node) final; - // ShapeSignature visit(const luci::CircleDiv *node) final; - // ShapeSignature visit(const luci::CircleElu *node) final; - // ShapeSignature visit(const luci::CircleEqual *node) final; - // ShapeSignature visit(const luci::CircleExp *node) final; - // ShapeSignature visit(const luci::CircleExpandDims *node) final; - // ShapeSignature visit(const luci::CircleFill *node) final; - // ShapeSignature visit(const luci::CircleFloor *node) final; - // ShapeSignature visit(const luci::CircleFloorDiv *node) final; - // ShapeSignature visit(const luci::CircleFloorMod *node) final; - // ShapeSignature visit(const luci::CircleFullyConnected *node) final; - // ShapeSignature visit(const luci::CircleGather *node) final; - // ShapeSignature visit(const luci::CircleGatherNd *node) final; - // ShapeSignature visit(const luci::CircleGreater *node) final; - // ShapeSignature visit(const luci::CircleGreaterEqual *node) final; - // ShapeSignature visit(const luci::CircleIf *node) final; - // ShapeSignature visit(const luci::CircleL2Normalize *node) final; - // ShapeSignature visit(const luci::CircleL2Pool2D *node) final; - // ShapeSignature visit(const luci::CircleLeakyRelu *node) final; - // ShapeSignature visit(const luci::CircleLess *node) final; - // ShapeSignature visit(const luci::CircleLessEqual *node) final; - // ShapeSignature visit(const luci::CircleLocalResponseNormalization *node) final; - // ShapeSignature visit(const luci::CircleLog *node) final; - // ShapeSignature visit(const luci::CircleLogicalAnd *node) final; - // ShapeSignature visit(const luci::CircleLogicalNot *node) final; - // ShapeSignature visit(const luci::CircleLogicalOr *node) final; - // ShapeSignature visit(const luci::CircleLogistic *node) final; - // ShapeSignature visit(const luci::CircleLogSoftmax *node) final; - // ShapeSignature visit(const luci::CircleMatrixDiag *node) final; - // ShapeSignature visit(const luci::CircleMatrixSetDiag *node) final; - // ShapeSignature visit(const luci::CircleMaximum *node) final; - // ShapeSignature visit(const luci::CircleMaxPool2D *node) final; - ShapeSignature visit(const luci::CircleMean *node) final; - // ShapeSignature visit(const luci::CircleMinimum *node) final; - // ShapeSignature visit(const luci::CircleMirrorPad *node) final; - // ShapeSignature visit(const luci::CircleNeg *node) final; - // ShapeSignature visit(const luci::CircleNonMaxSuppressionV4 *node) final; - // ShapeSignature visit(const luci::CircleNonMaxSuppressionV5 *node) final; - // ShapeSignature visit(const luci::CircleNotEqual *node) final; - // ShapeSignature visit(const luci::CirclePack *node) final; - // ShapeSignature visit(const luci::CirclePad *node) final; - // ShapeSignature visit(const luci::CirclePadV2 *node) final; - // ShapeSignature visit(const luci::CirclePow *node) final; - // ShapeSignature visit(const luci::CirclePRelu *node) final; - // ShapeSignature visit(const luci::CircleRange *node) final; - // ShapeSignature visit(const luci::CircleRank *node) final; - // ShapeSignature visit(const luci::CircleMul *node) final; - // ShapeSignature visit(const luci::CircleOneHot *node) final; - ShapeSignature visit(const luci::CircleReduceAny *node) final; - ShapeSignature visit(const luci::CircleReduceMax *node) final; - ShapeSignature visit(const luci::CircleReduceMin *node) final; - ShapeSignature visit(const luci::CircleReduceProd *node) final; - ShapeSignature visit(const luci::CircleRelu *node) final; - ShapeSignature visit(const luci::CircleRelu6 *node) final; - ShapeSignature visit(const luci::CircleReluN1To1 *node) final; - // ShapeSignature visit(const luci::CircleReshape *node) final; - // ShapeSignature visit(const luci::CircleResizeBilinear *node) final; - // ShapeSignature visit(const luci::CircleResizeNearestNeighbor *node) final; - // ShapeSignature visit(const luci::CircleReverseSequence *node) final; - // ShapeSignature visit(const luci::CircleReverseV2 *node) final; - // ShapeSignature visit(const luci::CircleRound *node) final; - // ShapeSignature visit(const luci::CircleRsqrt *node) final; - // ShapeSignature visit(const luci::CircleScatterNd *node) final; - // ShapeSignature visit(const luci::CircleSegmentSum *node) final; - // ShapeSignature visit(const luci::CircleSelect *node) final; - // ShapeSignature visit(const luci::CircleSelectV2 *node) final; - // ShapeSignature visit(const luci::CircleShape *node) final; - // ShapeSignature visit(const luci::CircleSin *node) final; - // ShapeSignature visit(const luci::CircleSlice *node) final; - // ShapeSignature visit(const luci::CircleSoftmax *node) final; - // ShapeSignature visit(const luci::CircleSpaceToBatchND *node) final; - // ShapeSignature visit(const luci::CircleSpaceToDepth *node) final; - // ShapeSignature visit(const luci::CircleSparseToDense *node) final; - // ShapeSignature visit(const luci::CircleSplit *node) final; - // ShapeSignature visit(const luci::CircleSplitV *node) final; - // ShapeSignature visit(const luci::CircleSqrt *node) final; - // ShapeSignature visit(const luci::CircleSquare *node) final; - // ShapeSignature visit(const luci::CircleSquaredDifference *node) final; - // ShapeSignature visit(const luci::CircleSqueeze *node) final; - // ShapeSignature visit(const luci::CircleStridedSlice *node) final; - // ShapeSignature visit(const luci::CircleSub *node) final; - ShapeSignature visit(const luci::CircleSum *node) final; - // ShapeSignature visit(const luci::CircleTanh *node) final; - // ShapeSignature visit(const luci::CircleTile *node) final; - // ShapeSignature visit(const luci::CircleTopKV2 *node) final; - // ShapeSignature visit(const luci::CircleTranspose *node) final; - // ShapeSignature visit(const luci::CircleTransposeConv *node) final; - // ShapeSignature visit(const luci::CircleUnidirectionalSequenceLSTM *node) final; - // ShapeSignature visit(const luci::CircleUnique *node) final; - // ShapeSignature visit(const luci::CircleUnpack *node) final; - // ShapeSignature visit(const luci::CircleWhere *node) final ; - // ShapeSignature visit(const luci::CircleWhile *node) final; - // ShapeSignature visit(const luci::CircleZerosLike *node) final; - - // Circle Only - // ShapeSignature visit(const luci::CircleBCQFullyConnected *node) final; - // ShapeSignature visit(const luci::CircleBCQGather *node) final; - // ShapeSignature visit(const luci::CircleInstanceNorm *node) final; - - // Virtual - ShapeSignature visit(const luci::CircleInput *node) final; - ShapeSignature visit(const luci::CircleOutput *node) final; - ShapeSignature visit(const luci::CircleOutputDummy *node) final; - ShapeSignature visit(const luci::CircleOutputExclude *node) final; - // ShapeSignature visit(const luci::CircleCustomOut *node) final; - // ShapeSignature visit(const luci::CircleIfOut *node) final; - // ShapeSignature visit(const luci::CircleNonMaxSuppressionV4Out *node) final; - // ShapeSignature visit(const luci::CircleNonMaxSuppressionV5Out *node) final; - // ShapeSignature visit(const luci::CircleSplitOut *node) final; - // ShapeSignature visit(const luci::CircleSplitVOut *node) final; - // ShapeSignature visit(const luci::CircleTopKV2Out *node) final; - // ShapeSignature visit(const luci::CircleUniqueOut *node) final; - // ShapeSignature visit(const luci::CircleUnpackOut *node) final; - // ShapeSignature visit(const luci::CircleWhileOut *node) final; -}; - -} // namespace ssinf - -} // namespace luci - -#endif // __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_H__ diff --git a/compiler/luci/service/include/luci/Service/CircleShapeSignatureInferenceHelper.h b/compiler/luci/service/include/luci/Service/CircleShapeSignatureInferenceHelper.h deleted file mode 100644 index fb5b3b302..000000000 --- a/compiler/luci/service/include/luci/Service/CircleShapeSignatureInferenceHelper.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020 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 __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_HELPER_H__ -#define __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_HELPER_H__ - -#include <luci/IR/CircleNodes.h> -#include <luci/IR/CircleShapeSignature.h> - -namespace luci -{ - -namespace ssinf // Namespace for Shape Signature Inference -{ - -// Return empty signature if all of dimensions are known. -// If at least one of dimensions is unknown, return signature without change. -ShapeSignature legalized_signature(const luci::ShapeSignature &signature); - -// Return reduced input_signature with indices and keep_dims. -// - indices : reduction index -// - keep_dims : If true, rank is not changed. If false, rank is reduced along indices. -ShapeSignature reduced_signature(const loco::Node *node, const loco::Node *indices, bool keep_dims); - -// Return signature of index-th argument of node. -ShapeSignature input_arg_signature(const luci::CircleNode *node, uint32_t index); - -} // namespace ssinf - -} // namespace luci - -#endif // __LUCI_CIRCLE_SHAPE_SIGNATURE_INFERENCE_HELPER_H__ diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInference.h b/compiler/luci/service/include/luci/Service/CircleTypeInference.h index 342214887..8eef469ac 100644 --- a/compiler/luci/service/include/luci/Service/CircleTypeInference.h +++ b/compiler/luci/service/include/luci/Service/CircleTypeInference.h @@ -23,24 +23,11 @@ #include <luci/IR/CircleNodes.h> #include <luci/IR/CircleNodeVisitor.h> -#include <luci/Service/CircleTypeInferenceHelper.h> +#include <luci/Service/CircleTypeInferenceRule.h> namespace luci { -/** - * @brief Get the type of each node as NodeAnnotation - * - * HOW TO USE - * - * TypeInference::get(g->nodes()->at(0)); - * TypeInference::get(g->nodes()->at(...)); - */ -struct TypeInference -{ - static circle::TensorType get(loco::Node *node); -}; - namespace tinf // namespace for Type Inference { @@ -53,7 +40,12 @@ class Algorithm final : public luci::CircleNodeVisitor<loco::DataType> { public: // TODO Remove this when all of visit function is implemented - loco::DataType visit(const luci::CircleNode *node) final { return node->dtype(); } + loco::DataType visit(const luci::CircleNode *node) final + { + loco::DataType dtype; + luci::CircleTypeInferenceRule().infer(node, dtype); + return dtype; + } // loco::DataType visit(const luci::CircleAbs *node) final; // loco::DataType visit(const luci::CircleAdd *node) final; @@ -78,6 +70,7 @@ public: // loco::DataType visit(const luci::CircleEqual *node) final; // loco::DataType visit(const luci::CircleExp *node) final; // loco::DataType visit(const luci::CircleExpandDims *node) final; + // loco::DataType visit(const luci::CircleFakeQuant *node) final; // loco::DataType visit(const luci::CircleFill *node) final; // loco::DataType visit(const luci::CircleFloor *node) final; // loco::DataType visit(const luci::CircleFloorDiv *node) final; @@ -177,7 +170,7 @@ public: // loco::DataType visit(const luci::CircleOutputDummy *node) final; // loco::DataType visit(const luci::CircleOutputExclude *node) final; // loco::DataType visit(const luci::CircleCustomOut *node) final; - // loco::DataType visit(const luci::CircleIfOut *node) final; + loco::DataType visit(const luci::CircleIfOut *node) final; // loco::DataType visit(const luci::CircleNonMaxSuppressionV4Out *node) final; // loco::DataType visit(const luci::CircleNonMaxSuppressionV5Out *node) final; // loco::DataType visit(const luci::CircleSplitOut *node) final; diff --git a/compiler/luci/service/include/luci/Service/Nodes/CircleConst.h b/compiler/luci/service/include/luci/Service/Nodes/CircleConst.h new file mode 100644 index 000000000..6049b4297 --- /dev/null +++ b/compiler/luci/service/include/luci/Service/Nodes/CircleConst.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 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 __LUCI_SERVICE_CIRCLE_CONST_H__ +#define __LUCI_SERVICE_CIRCLE_CONST_H__ + +#include <luci/IR/Nodes/CircleConst.h> + +namespace luci +{ + +/** + * @brief Return cloned object of CircleConst node + */ +luci::CircleConst *clone(luci::CircleConst *node); + +} // namespace luci + +#endif // __LUCI_SERVICE_CIRCLE_CONST_H__ diff --git a/compiler/luci/service/include/luci/Service/ShapeDescription.h b/compiler/luci/service/include/luci/Service/ShapeDescription.h index 4d92be13f..4671096fd 100644 --- a/compiler/luci/service/include/luci/Service/ShapeDescription.h +++ b/compiler/luci/service/include/luci/Service/ShapeDescription.h @@ -37,10 +37,6 @@ struct ShapeDescription // TODO remove these when CircleDialect is fully functioal ShapeDescription to_shape_description(const luci::CircleNode *node); ShapeDescription to_shape_description(const loco::TensorShape &shape); -ShapeDescription to_shape_description(const loco::FeatureShape &shape); -ShapeDescription to_shape_description(const loco::FilterShape &shape); -ShapeDescription to_shape_description(const loco::BiasShape &shape); -ShapeDescription to_shape_description(const loco::MatrixShape &shape); ShapeDescription to_shape_description(const loco::NodeShape &shape); template <typename Permutation> inline bool isNHWC(Permutation *perm); diff --git a/compiler/luci/service/include/luci/Service/Validate.h b/compiler/luci/service/include/luci/Service/Validate.h index 4b80d1d16..456d6e504 100644 --- a/compiler/luci/service/include/luci/Service/Validate.h +++ b/compiler/luci/service/include/luci/Service/Validate.h @@ -17,6 +17,8 @@ #ifndef __LUCI_SERVICE_VALIDATE_H__ #define __LUCI_SERVICE_VALIDATE_H__ +#include <luci/IR/Module.h> + #include <loco.h> namespace luci @@ -24,6 +26,17 @@ namespace luci bool validate(loco::Graph *); +/** + * @brief Return true if all nodes in graph have non empty name + */ +bool validate_name(loco::Graph *); + +/** + * @brief Return true if all names in the Module are unique + * @note CircleOutput may have duplicate name + */ +bool validate_unique_name(luci::Module *); + } // namespace luci #endif // __LUCI_SERVICE_VALIDATE_H__ diff --git a/compiler/luci/service/src/CircleCloneNode.h b/compiler/luci/service/src/CircleCloneNode.h new file mode 100644 index 000000000..02c7cd256 --- /dev/null +++ b/compiler/luci/service/src/CircleCloneNode.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021 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_CLONE_NODE_H__ +#define __CIRCLE_CLONE_NODE_H__ + +#include <luci/IR/CircleNodes.h> + +#include <luci/IR/CircleNodeVisitor.h> + +namespace luci +{ + +class CloneNode final : public luci::CircleNodeVisitor<luci::CircleNode *> +{ +public: + CloneNode(loco::Graph *graph) : _graph(graph){}; + +public: + luci::CircleNode *visit(const luci::CircleAbs *) final; + luci::CircleNode *visit(const luci::CircleAdd *) final; + luci::CircleNode *visit(const luci::CircleAddN *) final; + luci::CircleNode *visit(const luci::CircleArgMax *) final; + luci::CircleNode *visit(const luci::CircleArgMin *) final; + luci::CircleNode *visit(const luci::CircleAveragePool2D *) final; + luci::CircleNode *visit(const luci::CircleBatchMatMul *) final; + luci::CircleNode *visit(const luci::CircleBatchToSpaceND *) final; + luci::CircleNode *visit(const luci::CircleCast *) final; + luci::CircleNode *visit(const luci::CircleCeil *) final; + luci::CircleNode *visit(const luci::CircleConcatenation *) final; + luci::CircleNode *visit(const luci::CircleConst *) final; + luci::CircleNode *visit(const luci::CircleConv2D *) final; + luci::CircleNode *visit(const luci::CircleCos *) final; + luci::CircleNode *visit(const luci::CircleCustom *) final; + luci::CircleNode *visit(const luci::CircleDepthToSpace *) final; + luci::CircleNode *visit(const luci::CircleDepthwiseConv2D *) final; + luci::CircleNode *visit(const luci::CircleDequantize *) final; + luci::CircleNode *visit(const luci::CircleDiv *) final; + luci::CircleNode *visit(const luci::CircleElu *) final; + luci::CircleNode *visit(const luci::CircleEqual *) final; + luci::CircleNode *visit(const luci::CircleExp *) final; + luci::CircleNode *visit(const luci::CircleExpandDims *) final; + luci::CircleNode *visit(const luci::CircleFakeQuant *) final; + luci::CircleNode *visit(const luci::CircleFill *) final; + luci::CircleNode *visit(const luci::CircleFloor *) final; + luci::CircleNode *visit(const luci::CircleFloorDiv *) final; + luci::CircleNode *visit(const luci::CircleFloorMod *) final; + luci::CircleNode *visit(const luci::CircleFullyConnected *) final; + luci::CircleNode *visit(const luci::CircleGather *) final; + luci::CircleNode *visit(const luci::CircleGatherNd *) final; + luci::CircleNode *visit(const luci::CircleGreater *) final; + luci::CircleNode *visit(const luci::CircleGreaterEqual *) final; + // luci::CircleNode *visit(const luci::CircleIf *) final; + luci::CircleNode *visit(const luci::CircleL2Normalize *) final; + luci::CircleNode *visit(const luci::CircleL2Pool2D *) final; + luci::CircleNode *visit(const luci::CircleLeakyRelu *) final; + luci::CircleNode *visit(const luci::CircleLess *) final; + luci::CircleNode *visit(const luci::CircleLessEqual *) final; + luci::CircleNode *visit(const luci::CircleLocalResponseNormalization *) final; + luci::CircleNode *visit(const luci::CircleLog *) final; + luci::CircleNode *visit(const luci::CircleLogicalAnd *) final; + luci::CircleNode *visit(const luci::CircleLogicalNot *) final; + luci::CircleNode *visit(const luci::CircleLogicalOr *) final; + luci::CircleNode *visit(const luci::CircleLogistic *) final; + luci::CircleNode *visit(const luci::CircleLogSoftmax *) final; + luci::CircleNode *visit(const luci::CircleMatrixDiag *) final; + luci::CircleNode *visit(const luci::CircleMatrixSetDiag *) final; + luci::CircleNode *visit(const luci::CircleMaximum *) final; + luci::CircleNode *visit(const luci::CircleMaxPool2D *) final; + luci::CircleNode *visit(const luci::CircleMean *) final; + luci::CircleNode *visit(const luci::CircleMinimum *) final; + luci::CircleNode *visit(const luci::CircleMirrorPad *) final; + luci::CircleNode *visit(const luci::CircleMul *) final; + luci::CircleNode *visit(const luci::CircleNeg *) final; + luci::CircleNode *visit(const luci::CircleNonMaxSuppressionV4 *) final; + luci::CircleNode *visit(const luci::CircleNonMaxSuppressionV5 *) final; + luci::CircleNode *visit(const luci::CircleNotEqual *) final; + luci::CircleNode *visit(const luci::CircleOneHot *) final; + luci::CircleNode *visit(const luci::CirclePack *) final; + luci::CircleNode *visit(const luci::CirclePad *) final; + luci::CircleNode *visit(const luci::CirclePadV2 *) final; + luci::CircleNode *visit(const luci::CirclePow *) final; + luci::CircleNode *visit(const luci::CirclePRelu *) final; + luci::CircleNode *visit(const luci::CircleRange *) final; + luci::CircleNode *visit(const luci::CircleRank *) final; + luci::CircleNode *visit(const luci::CircleReduceAny *) final; + luci::CircleNode *visit(const luci::CircleReduceMax *) final; + luci::CircleNode *visit(const luci::CircleReduceMin *) final; + luci::CircleNode *visit(const luci::CircleReduceProd *) final; + luci::CircleNode *visit(const luci::CircleRelu *) final; + luci::CircleNode *visit(const luci::CircleRelu6 *) final; + luci::CircleNode *visit(const luci::CircleReluN1To1 *) final; + luci::CircleNode *visit(const luci::CircleReshape *) final; + luci::CircleNode *visit(const luci::CircleResizeBilinear *) final; + luci::CircleNode *visit(const luci::CircleResizeNearestNeighbor *) final; + luci::CircleNode *visit(const luci::CircleReverseSequence *) final; + luci::CircleNode *visit(const luci::CircleReverseV2 *) final; + luci::CircleNode *visit(const luci::CircleRound *) final; + luci::CircleNode *visit(const luci::CircleRsqrt *) final; + luci::CircleNode *visit(const luci::CircleScatterNd *) final; + luci::CircleNode *visit(const luci::CircleSegmentSum *) final; + luci::CircleNode *visit(const luci::CircleSelect *) final; + luci::CircleNode *visit(const luci::CircleSelectV2 *) final; + luci::CircleNode *visit(const luci::CircleShape *) final; + luci::CircleNode *visit(const luci::CircleSin *) final; + luci::CircleNode *visit(const luci::CircleSlice *) final; + luci::CircleNode *visit(const luci::CircleSoftmax *) final; + luci::CircleNode *visit(const luci::CircleSpaceToBatchND *) final; + luci::CircleNode *visit(const luci::CircleSpaceToDepth *) final; + luci::CircleNode *visit(const luci::CircleSparseToDense *) final; + luci::CircleNode *visit(const luci::CircleSplit *) final; + luci::CircleNode *visit(const luci::CircleSplitV *) final; + luci::CircleNode *visit(const luci::CircleSqrt *) final; + luci::CircleNode *visit(const luci::CircleSquare *) final; + luci::CircleNode *visit(const luci::CircleSquaredDifference *) final; + luci::CircleNode *visit(const luci::CircleSqueeze *) final; + luci::CircleNode *visit(const luci::CircleStridedSlice *) final; + luci::CircleNode *visit(const luci::CircleSub *) final; + luci::CircleNode *visit(const luci::CircleSum *) final; + luci::CircleNode *visit(const luci::CircleTanh *) final; + luci::CircleNode *visit(const luci::CircleTile *) final; + luci::CircleNode *visit(const luci::CircleTopKV2 *) final; + luci::CircleNode *visit(const luci::CircleTranspose *) final; + luci::CircleNode *visit(const luci::CircleTransposeConv *) final; + luci::CircleNode *visit(const luci::CircleUnidirectionalSequenceLSTM *) final; + luci::CircleNode *visit(const luci::CircleUnique *) final; + luci::CircleNode *visit(const luci::CircleUnpack *) final; + luci::CircleNode *visit(const luci::CircleWhere *) final; + // luci::CircleNode *visit(const luci::CircleWhile *) final; + luci::CircleNode *visit(const luci::CircleZerosLike *) final; + + // Circle Only + luci::CircleNode *visit(const luci::CircleBCQFullyConnected *) final; + luci::CircleNode *visit(const luci::CircleBCQGather *) final; + luci::CircleNode *visit(const luci::CircleInstanceNorm *) final; + + // Virtual + luci::CircleNode *visit(const luci::CircleCustomOut *) final; + // luci::CircleNode *visit(const luci::CircleIfOut *) final; + // luci::CircleNode *visit(const luci::CircleInput *) final; + luci::CircleNode *visit(const luci::CircleNonMaxSuppressionV4Out *) final; + luci::CircleNode *visit(const luci::CircleNonMaxSuppressionV5Out *) final; + // luci::CircleNode *visit(const luci::CircleOutput *) final; + luci::CircleNode *visit(const luci::CircleOutputDummy *) final; + luci::CircleNode *visit(const luci::CircleOutputExclude *) final; + luci::CircleNode *visit(const luci::CircleSplitOut *) final; + luci::CircleNode *visit(const luci::CircleSplitVOut *) final; + luci::CircleNode *visit(const luci::CircleTopKV2Out *) final; + luci::CircleNode *visit(const luci::CircleUniqueOut *) final; + luci::CircleNode *visit(const luci::CircleUnpackOut *) final; + // luci::CircleNode *visit(const luci::CircleWhileOut *) final; + + // NOTE CircleNodeVisitor will throw if not supported here + +protected: + loco::Graph *_graph = nullptr; +}; + +} // namespace luci + +#endif // __CIRCLE_CLONE_NODE_H__ diff --git a/compiler/luci/service/src/CircleNodeClone.cpp b/compiler/luci/service/src/CircleNodeClone.cpp new file mode 100644 index 000000000..d2033dd0c --- /dev/null +++ b/compiler/luci/service/src/CircleNodeClone.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include "CircleCloneNode.h" + +#include <oops/UserExn.h> + +#include <cassert> + +namespace luci +{ + +/** + * @note Attributes of specific node type like keep_dims() of CircleSum are + * not copied. + */ +void copy_common_attributes(const luci::CircleNode *src, luci::CircleNode *dst) +{ + assert(src != nullptr); + assert(dst != nullptr); + + dst->name(src->name()); + dst->dtype(src->dtype()); + + dst->rank(src->rank()); + for (uint32_t i = 0; i < src->rank(); i++) + { + dst->dim(i) = src->dim(i); + } + dst->shape_status(src->shape_status()); + + // quantparam + const auto *quantparam = src->quantparam(); + if (quantparam != nullptr) + { + auto qparam = std::make_unique<luci::CircleQuantParam>(); + qparam->scale = quantparam->scale; + qparam->zerop = quantparam->zerop; + qparam->min = quantparam->min; + qparam->max = quantparam->max; + qparam->quantized_dimension = quantparam->quantized_dimension; + + dst->quantparam(std::move(qparam)); + } + + // sparsity + const auto *sparsity = src->sparsityparam(); + if (sparsity != nullptr) + { + auto sparam = std::make_unique<luci::SparsityParam>(); + sparam->traversal_order = sparsity->traversal_order; + sparam->block_map = sparsity->block_map; + sparam->dim_metadata = sparsity->dim_metadata; + + dst->sparsityparam(std::move(sparam)); + } + + // op version + dst->op_version(src->op_version()); +} + +/** + * @note Each visit implementation must copy node specific attributes. + */ +luci::CircleNode *clone_node(const luci::CircleNode *node, loco::Graph *graph) +{ + if (node == nullptr || graph == nullptr) + return nullptr; + + CloneNode cn(graph); + auto cloned = node->accept(&cn); + if (cloned != nullptr) + copy_common_attributes(node, cloned); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/CircleNodeClone.test.cpp b/compiler/luci/service/src/CircleNodeClone.test.cpp new file mode 100644 index 000000000..5908eeb82 --- /dev/null +++ b/compiler/luci/service/src/CircleNodeClone.test.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +// NOTE any node will do for testing +#include <luci/IR/Nodes/CircleAdd.h> + +#include <gtest/gtest.h> + +namespace +{ + +luci::CircleAdd *build_simple_add_graph(loco::Graph *g) +{ + auto node = g->nodes()->create<luci::CircleAdd>(); + + node->name("name"); + node->dtype(loco::DataType::FLOAT32); + node->rank(1); + node->dim(0).set(3); + node->shape_status(luci::ShapeStatus::VALID); + node->fusedActivationFunction(luci::FusedActFunc::NONE); + + auto qparam = std::make_unique<luci::CircleQuantParam>(); + qparam->scale = {1.0}; + qparam->zerop = {0}; + qparam->min = {0.0}; + qparam->max = {1.0}; + qparam->quantized_dimension = 0; + node->quantparam(std::move(qparam)); + + auto sparam = std::make_unique<luci::SparsityParam>(); + sparam->traversal_order = {0}; + sparam->block_map = {0}; + sparam->dim_metadata = {luci::DimMetaData(luci::DimensionType::DENSE, 1)}; + node->sparsityparam(std::move(sparam)); + + node->op_version(2); + + return node; +} + +} // namespace + +TEST(CircleNodeCloneTest, copy_attribites) +{ + auto g = loco::make_graph(); + auto node = build_simple_add_graph(g.get()); + + auto copy = g->nodes()->create<luci::CircleAdd>(); + luci::copy_common_attributes(node, copy); + + ASSERT_EQ(node->name(), copy->name()); + ASSERT_EQ(node->dtype(), copy->dtype()); + ASSERT_EQ(node->rank(), copy->rank()); + ASSERT_EQ(node->shape_status(), copy->shape_status()); + + const auto *qparam_node = node->quantparam(); + const auto *qparam_copy = copy->quantparam(); + ASSERT_EQ(qparam_node->scale, qparam_copy->scale); + + const auto *sparsity_node = node->sparsityparam(); + const auto *sparsity_copy = copy->sparsityparam(); + ASSERT_EQ(sparsity_node->traversal_order, sparsity_copy->traversal_order); + + ASSERT_EQ(node->op_version(), copy->op_version()); +} + +TEST(CircleNodeCloneTest, clone_add_node) +{ + auto g = loco::make_graph(); + auto node = build_simple_add_graph(g.get()); + + auto cg = loco::make_graph(); + auto clone = clone_node(node, cg.get()); + + ASSERT_NE(nullptr, clone); + ASSERT_EQ(cg.get(), clone->graph()); + ASSERT_EQ(node->name(), clone->name()); + ASSERT_EQ(node->dtype(), clone->dtype()); + ASSERT_EQ(node->rank(), clone->rank()); + ASSERT_EQ(node->shape_status(), clone->shape_status()); +} + +TEST(CircleNodeCloneTest, clone_node_NEG) +{ + auto g = loco::make_graph(); + auto node = build_simple_add_graph(g.get()); + + auto cg = loco::make_graph(); + auto clone = luci::clone_node(nullptr, cg.get()); + ASSERT_EQ(nullptr, clone); + auto clone2 = luci::clone_node(node, nullptr); + ASSERT_EQ(nullptr, clone2); +} diff --git a/compiler/luci/service/src/CircleShapeInference.cpp b/compiler/luci/service/src/CircleShapeInference.cpp index db8ffd8ad..73472069b 100644 --- a/compiler/luci/service/src/CircleShapeInference.cpp +++ b/compiler/luci/service/src/CircleShapeInference.cpp @@ -15,27 +15,16 @@ */ #include "luci/Service/CircleShapeInference.h" -#include "luci/Service/ShapeDescription.h" + +#include "CircleShapeInferenceHelper.h" #include <loco.h> -#include <loco/Service/ShapeInference.h> #include <luci/Log.h> #include <cassert> #include <iostream> -namespace luci -{ - -ShapeDescription ShapeInference::get(loco::Node *node) -{ - assert(loco::shape_known(node)); - return to_shape_description(loco::shape_get(node)); -} - -} // namespace luci - namespace { @@ -46,7 +35,11 @@ std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape { if (r) os << ","; - os << tensor_shape.dim(r).value(); + + if (tensor_shape.dim(r).known()) + os << tensor_shape.dim(r).value(); + else + os << "?"; } os << "]"; return os; @@ -90,5 +83,5 @@ bool Rule::infer(const luci::CircleNode *circle_node, loco::TensorShape &shape) return true; } -} // namespace ssinf +} // namespace sinf } // namespace luci diff --git a/compiler/luci/service/src/CircleShapeInferenceHelper.cpp b/compiler/luci/service/src/CircleShapeInferenceHelper.cpp index f7eb6c3ec..2009aa59f 100644 --- a/compiler/luci/service/src/CircleShapeInferenceHelper.cpp +++ b/compiler/luci/service/src/CircleShapeInferenceHelper.cpp @@ -14,7 +14,24 @@ * limitations under the License. */ -#include "luci/Service/CircleShapeInferenceHelper.h" +#include "CircleShapeInferenceHelper.h" + +namespace luci +{ + +loco::NodeShape shape_get(const loco::Node *node) +{ + assert(luci::shape_known(node)); + return loco::NodeShape{sinf::circle_shape(loco::must_cast<const luci::CircleNode *>(node))}; +} + +bool shape_known(const loco::Node *node) +{ + return loco::must_cast<const luci::CircleNode *>(node)->shape_status() != + luci::ShapeStatus::UNDEFINED; +} + +} // namespace luci namespace luci { @@ -26,7 +43,7 @@ loco::TensorShape circle_shape(const luci::CircleNode *node) loco::TensorShape shape; shape.rank(node->rank()); for (uint32_t r = 0; r < node->rank(); ++r) - shape.dim(r) = loco::Dimension(node->dim(r).value()); + shape.dim(r) = node->dim(r); return shape; } diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInferenceHelper.h b/compiler/luci/service/src/CircleShapeInferenceHelper.h index dd6a5a454..7c7ea496c 100644 --- a/compiler/luci/service/include/luci/Service/CircleShapeInferenceHelper.h +++ b/compiler/luci/service/src/CircleShapeInferenceHelper.h @@ -17,10 +17,24 @@ #ifndef __LUCI_CIRCLE_SHAPE_INFERENCE_HELPER_H__ #define __LUCI_CIRCLE_SHAPE_INFERENCE_HELPER_H__ +#include <loco/IR/NodeShape.h> #include <loco/IR/TensorShape.h> #include <luci/IR/CircleNodes.h> -#include <luci/IR/CircleShapeSignature.h> + +namespace luci +{ + +// NOTE Functions in this namespace will be removed after new inference +// algorithms are fully implemented. + +// This function is temporary function for deprecating loco::shape_get +loco::NodeShape shape_get(const loco::Node *node); + +// This function is temporary function for deprecating loco::shape_known +bool shape_known(const loco::Node *node); + +} // namespace luci namespace luci { diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp index 38ff619ab..c6d8232c3 100644 --- a/compiler/luci/service/src/CircleShapeInferenceRule.cpp +++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp @@ -17,6 +17,7 @@ #include "luci/Service/CircleShapeInferenceRule.h" #include "Check.h" +#include "CircleShapeInferenceHelper.h" #include "ShapeInfer_StridedSlice.h" #include <luci/IR/CircleNodes.h> @@ -41,7 +42,11 @@ std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape { if (r) os << ","; - os << tensor_shape.dim(r).value(); + + if (tensor_shape.dim(r).known()) + os << tensor_shape.dim(r).value(); + else + os << "?"; } os << "]"; return os; @@ -52,7 +57,15 @@ loco::TensorShape own_shape(const luci::CircleNode *node) loco::TensorShape shape; shape.rank(node->rank()); for (uint32_t r = 0; r < node->rank(); ++r) - shape.dim(r) = loco::Dimension(node->dim(r).value()); + { + // Shape inference rules in this file did not consider unknown dimension. + // If some node has unknown dimension, 0 is inserted and wrong shape + // inference was done as a result. + // To fix this, new shape inference algorithm is being implemented. + // Until new inference algorithm is fully implemented, unknown dimension + // would be represented as 1 along with TFLite expression. + shape.dim(r) = node->dim(r).known() ? node->dim(r).value() : 1; + } return shape; } @@ -135,10 +148,8 @@ loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::Tenso output_shape.rank(rank); for (uint32_t axis = 0; axis < rank; ++axis) { - assert(x.dim(axis).known() && y.dim(axis).known()); - - auto x_dim = x.dim(axis).value(); - auto y_dim = y.dim(axis).value(); + auto x_dim = x.dim(axis).known() ? x.dim(axis).value() : 1; + auto y_dim = y.dim(axis).known() ? y.dim(axis).value() : 1; // each dimension of x and y should be same or one must be 1 if different if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1))) @@ -177,23 +188,29 @@ template <loco::DataType T> std::vector<int64_t> vector_from_constant(luci::Circ template <class CIRCLENODE> loco::NodeShape broadcast_xy(const CIRCLENODE *node) { - auto x_shape = loco::shape_get(node->x()).template as<loco::TensorShape>(); - auto y_shape = loco::shape_get(node->y()).template as<loco::TensorShape>(); + auto x_shape = luci::shape_get(node->x()).template as<loco::TensorShape>(); + auto y_shape = luci::shape_get(node->y()).template as<loco::TensorShape>(); auto output_shape = broadcast_shape(x_shape, y_shape); return loco::NodeShape{output_shape}; } +template <class CIRCLENODE> loco::NodeShape use_inputs(const CIRCLENODE *node) +{ + auto inputs_shape = luci::shape_get(node->inputs()).template as<loco::TensorShape>(); + return loco::NodeShape{inputs_shape}; +} + template <class CIRCLENODE> loco::NodeShape use_x(const CIRCLENODE *node) { - auto x_shape = loco::shape_get(node->x()).template as<loco::TensorShape>(); + auto x_shape = luci::shape_get(node->x()).template as<loco::TensorShape>(); return loco::NodeShape{x_shape}; } template <class CIRCLENODE> loco::NodeShape use_logits(const CIRCLENODE *node) { - auto shape = loco::shape_get(node->logits()).template as<loco::TensorShape>(); + auto shape = luci::shape_get(node->logits()).template as<loco::TensorShape>(); return loco::NodeShape{shape}; } @@ -202,7 +219,7 @@ loco::NodeShape use_paddings(const CIRCLENODE *node, const luci::CircleConst *pa { const loco::DataType S32 = loco::DataType::S32; - auto input_shape = loco::shape_get(node->input()).template as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>(); // TODO support other data type LUCI_ASSERT(paddings->dtype() == S32, "Only support int 32 for now"); @@ -232,11 +249,11 @@ loco::NodeShape use_paddings(const CIRCLENODE *node, const luci::CircleConst *pa loco::NodeShape infer_add_n(const luci::CircleAddN *node) { - auto shape = loco::shape_get(node->inputs(0)).as<loco::TensorShape>(); + auto shape = luci::shape_get(node->inputs(0)).as<loco::TensorShape>(); for (uint32_t idx = 1; idx < node->arity(); ++idx) { - auto shape_idx = loco::shape_get(node->inputs(idx)).as<loco::TensorShape>(); + auto shape_idx = luci::shape_get(node->inputs(idx)).as<loco::TensorShape>(); if (!(shape == shape_idx)) { INTERNAL_EXN_V("ADD_N shape not same as the first input: ", idx); @@ -247,8 +264,8 @@ loco::NodeShape infer_add_n(const luci::CircleAddN *node) loco::NodeShape infer_arg_max(const luci::CircleArgMax *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); - auto dimension_shape = loco::shape_get(node->dimension()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); + auto dimension_shape = luci::shape_get(node->dimension()).as<loco::TensorShape>(); int64_t select_axis = 0; { @@ -286,8 +303,8 @@ loco::NodeShape infer_arg_max(const luci::CircleArgMax *node) loco::NodeShape infer_arg_min(const luci::CircleArgMin *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); - auto dimension_shape = loco::shape_get(node->dimension()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); + auto dimension_shape = luci::shape_get(node->dimension()).as<loco::TensorShape>(); int64_t select_axis = 0; { @@ -326,9 +343,7 @@ loco::NodeShape infer_arg_min(const luci::CircleArgMin *node) // Call this for CircleAvgPool2D and CircleMaxPool2D only template <class Pool2DType> loco::NodeShape infer_pool_2d_shape(const Pool2DType *node) { - LUCI_ASSERT(loco::shape_known(node->value()), "Shape must be known"); - - auto ifm_shape = loco::shape_get(node->value()).template as<loco::TensorShape>(); + auto ifm_shape = luci::shape_get(node->value()).template as<loco::TensorShape>(); assert(ifm_shape.rank() == 4); uint32_t input_height = ifm_shape.dim(1).value(); @@ -372,7 +387,7 @@ loco::NodeShape infer_batch_to_space_nd(const luci::CircleBatchToSpaceND *node) { const loco::DataType S32 = loco::DataType::S32; - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // Support only input rank is 3 and 4 assert(input_shape.rank() == 3 || input_shape.rank() == 4); @@ -384,8 +399,8 @@ loco::NodeShape infer_batch_to_space_nd(const luci::CircleBatchToSpaceND *node) auto const_crops = loco::must_cast<luci::CircleConst *>(node->crops()); LUCI_ASSERT(const_crops->dtype() == loco::DataType::S32, "Only support int32 crops"); - auto const_block_shape_shape = loco::shape_get(const_block_shape).as<loco::TensorShape>(); - auto const_crops_shape = loco::shape_get(const_crops).as<loco::TensorShape>(); + auto const_block_shape_shape = luci::shape_get(const_block_shape).as<loco::TensorShape>(); + auto const_crops_shape = luci::shape_get(const_crops).as<loco::TensorShape>(); assert(const_block_shape_shape.rank() == 1); assert(const_crops_shape.rank() == 2); @@ -423,8 +438,8 @@ struct OutputSize template <class Conv2DType> OutputSize infer_conv2d_type(const Conv2DType *node) { - auto ifm_shape = loco::shape_get(node->input()).template as<loco::TensorShape>(); - auto ker_shape = loco::shape_get(node->filter()).template as<loco::TensorShape>(); + auto ifm_shape = luci::shape_get(node->input()).template as<loco::TensorShape>(); + auto ker_shape = luci::shape_get(node->filter()).template as<loco::TensorShape>(); assert(ifm_shape.rank() == 4); assert(ker_shape.rank() == 4); @@ -496,7 +511,7 @@ loco::NodeShape infer_batchmatmul_shape(const loco::TensorShape &x_shape, loco::Dimension y_lhs = adj_y ? y_shape.dim(y_rank - 1) : y_shape.dim(y_rank - 2); loco::Dimension y_rhs = adj_y ? y_shape.dim(y_rank - 2) : y_shape.dim(y_rank - 1); - if (not(x_rhs == y_lhs)) + if (x_rhs.known() && y_lhs.known() && not(x_rhs == y_lhs)) INTERNAL_EXN("x_rhs and y_lhs should be same"); uint32_t out_rank = output_shape.rank(); @@ -511,7 +526,7 @@ loco::NodeShape infer_concatenation(const luci::CircleConcatenation *node) // TODO Support when CircleConcatenation has 0 input assert(node->numValues() > 0); - auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>(); + auto first_shape = luci::shape_get(node->values(0)).as<loco::TensorShape>(); auto axis = node->axis(); if (axis < 0) axis += first_shape.rank(); @@ -527,14 +542,20 @@ loco::NodeShape infer_concatenation(const luci::CircleConcatenation *node) for (uint32_t i = 1; i < node->numValues(); ++i) { - auto input_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->values(i)).as<loco::TensorShape>(); for (uint32_t j = 0; j < output_shape.rank(); ++j) { if (j == static_cast<uint32_t>(axis)) + { + // If dimension is unknown, value() will return 0. + // This is wrong but until new inference algorithm is implemented, + // this code will not be modified to keep compatibility. output_shape.dim(j) = output_shape.dim(j).value() + input_shape.dim(j).value(); + } else - assert(output_shape.dim(j) == input_shape.dim(j)); + assert(!output_shape.dim(j).known() || !input_shape.dim(j).known() || + output_shape.dim(j) == input_shape.dim(j)); } } @@ -545,8 +566,8 @@ loco::NodeShape infer_conv2d(const luci::CircleConv2D *node) { LOGGER(l); - auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC - auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI + auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC + auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI INFO(l) << "[luci] CircleConv2D ShapeInf ifm(" << ifm_shape.rank() << ") ker(" << ker_shape.rank() << ")" << std::endl; @@ -569,7 +590,7 @@ loco::NodeShape infer_conv2d(const luci::CircleConv2D *node) loco::NodeShape infer_depth_to_space(const luci::CircleDepthToSpace *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); LUCI_ASSERT(input_shape.rank() == 4, "Only input rank 4 is supported"); // Only data format NHWC is supported @@ -601,12 +622,13 @@ loco::NodeShape infer_depth_to_space(const luci::CircleDepthToSpace *node) loco::NodeShape infer_depthwise_conv2d(const luci::CircleDepthwiseConv2D *node) { - auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC - auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM + auto ifm_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC + auto ker_shape = luci::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM assert(ifm_shape.rank() == 4); assert(ker_shape.rank() == 4); assert(ker_shape.dim(0).value() == 1); + assert(ifm_shape.dim(3).value() * node->depthMultiplier() == ker_shape.dim(3).value()); auto os = infer_conv2d_type(node); @@ -623,7 +645,7 @@ loco::NodeShape infer_depthwise_conv2d(const luci::CircleDepthwiseConv2D *node) loco::NodeShape infer_expand_dims(const luci::CircleExpandDims *node) { const loco::DataType S32 = loco::DataType::S32; - auto x_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto x_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); if (x_shape.rank() == 0) { // This maybe for unknown shape. We use shape from the node itself. @@ -637,7 +659,7 @@ loco::NodeShape infer_expand_dims(const luci::CircleExpandDims *node) } int32_t axis = const_axis->at<S32>(0); LUCI_ASSERT((axis <= static_cast<int32_t>(x_shape.rank())) && - (axis >= -1 - static_cast<int32_t>(x_shape.rank())), + (axis >= -1 - static_cast<int32_t>(x_shape.rank())), "Axis has to be between [-(D+1), D], where D is rank of input."); size_t positive_axis = axis < 0 ? x_shape.rank() + axis + 1 : axis; loco::TensorShape output_shape; @@ -684,8 +706,8 @@ loco::NodeShape infer_fill(const luci::CircleFill *node) loco::NodeShape infer_fully_connected(const luci::CircleFullyConnected *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); - auto weights_shape = loco::shape_get(node->weights()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); + auto weights_shape = luci::shape_get(node->weights()).as<loco::TensorShape>(); // Checking shape capability for fully connected layer // Input: a tensor of at least rank 2 [D1, D2, ... Dn] @@ -715,8 +737,8 @@ loco::NodeShape infer_gather(const luci::CircleGather *node) { loco::TensorShape output_shape; - const auto input_shape = loco::shape_get(node->params()).as<loco::TensorShape>(); - const auto positions_shape = loco::shape_get(node->indices()).as<loco::TensorShape>(); + const auto input_shape = luci::shape_get(node->params()).as<loco::TensorShape>(); + const auto positions_shape = luci::shape_get(node->indices()).as<loco::TensorShape>(); int32_t axis = node->axis(); // If CircleGather input has a dynamic shape, it can't inference this shape. So, it returns the @@ -743,8 +765,8 @@ loco::NodeShape infer_gather_nd(const luci::CircleGatherNd *node) { loco::TensorShape output_shape; - const auto params_shape = loco::shape_get(node->params()).as<loco::TensorShape>(); - const auto indices_shape = loco::shape_get(node->indices()).as<loco::TensorShape>(); + const auto params_shape = luci::shape_get(node->params()).as<loco::TensorShape>(); + const auto indices_shape = luci::shape_get(node->indices()).as<loco::TensorShape>(); const auto params_rank = params_shape.rank(); const auto indices_rank = indices_shape.rank(); @@ -791,7 +813,7 @@ loco::NodeShape infer_matrix_diag(const luci::CircleMatrixDiag *node) { loco::TensorShape output_shape; - auto diagonal_shape = loco::shape_get(node->diagonal()).as<loco::TensorShape>(); + auto diagonal_shape = luci::shape_get(node->diagonal()).as<loco::TensorShape>(); auto rank = diagonal_shape.rank(); output_shape.rank(rank + 1); @@ -808,8 +830,8 @@ loco::NodeShape infer_matrix_diag(const luci::CircleMatrixDiag *node) loco::NodeShape infer_matrix_set_diag(const luci::CircleMatrixSetDiag *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); - auto diagonal_shape = loco::shape_get(node->diagonal()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); + auto diagonal_shape = luci::shape_get(node->diagonal()).as<loco::TensorShape>(); auto rank = diagonal_shape.rank(); @@ -831,7 +853,7 @@ loco::TensorShape infer_reducer(const loco::Node *input, const loco::Node *indic { const loco::DataType S32 = loco::DataType::S32; - auto input_shape = loco::shape_get(input).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(input).as<loco::TensorShape>(); auto reduction_indices = loco::must_cast<const luci::CircleConst *>(indices); { // Exceptions @@ -892,7 +914,7 @@ loco::NodeShape infer_mirror_pad(const luci::CircleMirrorPad *node) loco::NodeShape infer_one_hot(const luci::CircleOneHot *node) { const loco::DataType S32 = loco::DataType::S32; - auto indices_shape = loco::shape_get(node->indices()).as<loco::TensorShape>(); + auto indices_shape = luci::shape_get(node->indices()).as<loco::TensorShape>(); // Only support OneHot node's depth() is CircleConst with type S32 // TODO support depth with other types auto depth = loco::must_cast<luci::CircleConst *>(node->depth()); @@ -925,11 +947,11 @@ loco::NodeShape infer_pack(const luci::CirclePack *node) { LUCI_ASSERT(node->values_count() > 0, "Only support one or more inputs"); - auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>(); + auto first_shape = luci::shape_get(node->values(0)).as<loco::TensorShape>(); // Make sure all inputs have the same shape. for (uint32_t i = 1; i < node->values_count(); ++i) { - auto in_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>(); + auto in_shape = luci::shape_get(node->values(i)).as<loco::TensorShape>(); LUCI_ASSERT(loco::NodeShape{first_shape} == loco::NodeShape{in_shape}, "All inputs must have the same shape"); } @@ -985,8 +1007,8 @@ loco::NodeShape infer_pad_v2(const luci::CirclePadV2 *node) loco::NodeShape infer_p_relu(const luci::CirclePRelu *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); - auto alpha_shape = loco::shape_get(node->alpha()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); + auto alpha_shape = luci::shape_get(node->alpha()).as<loco::TensorShape>(); auto output_shape = broadcast_shape(input_shape, alpha_shape); @@ -1087,10 +1109,12 @@ loco::NodeShape infer_reshape(const luci::CircleReshape *node) loco::TensorShape output_shape = shape_by_input; // One of the dimensions can have special value -1, meaning its actual value should be inferred. - const auto input_shape = loco::shape_get(node->tensor()).as<loco::TensorShape>(); - const uint32_t input_element_count = loco::element_count(&input_shape); + const auto input_shape = luci::shape_get(node->tensor()).as<loco::TensorShape>(); + uint32_t input_element_count = 1; uint32_t output_element_count = 1; uint32_t unknown_dim_index = UINT32_MAX; + for (uint32_t i = 0; i < input_shape.rank(); ++i) + input_element_count *= (input_shape.dim(i).known() ? input_shape.dim(i).value() : 1); for (uint32_t dim_index = 0; dim_index < output_shape.rank(); ++dim_index) { const uint32_t dim_value = output_shape.dim(dim_index).value(); @@ -1114,7 +1138,7 @@ loco::NodeShape infer_reshape(const luci::CircleReshape *node) loco::NodeShape infer_resize_bilinear(const luci::CircleResizeBilinear *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); if (input_shape.rank() != 4) INTERNAL_EXN("Expected ResizeBilinear input to have rank 4"); @@ -1142,7 +1166,7 @@ loco::NodeShape infer_resize_bilinear(const luci::CircleResizeBilinear *node) loco::NodeShape infer_resize_nearest_neighbor(const luci::CircleResizeNearestNeighbor *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); if (input_shape.rank() != 4) INTERNAL_EXN("Expected ResizeNearesNeighbor input to have rank 4"); @@ -1195,8 +1219,8 @@ loco::NodeShape infer_scatter_nd(const luci::CircleScatterNd *node) loco::NodeShape infer_segment_sum(const luci::CircleSegmentSum *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); - auto segment_shape = loco::shape_get(node->segment_ids()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); + auto segment_shape = luci::shape_get(node->segment_ids()).as<loco::TensorShape>(); LUCI_ASSERT(segment_shape.rank() == 1, "segment_ids must be 1-D tensor"); LUCI_ASSERT(segment_shape.dim(0).value() == input_shape.dim(0).value(), @@ -1226,11 +1250,11 @@ loco::NodeShape infer_segment_sum(const luci::CircleSegmentSum *node) loco::NodeShape infer_select(const luci::CircleSelect *node) { - auto t_shape = loco::shape_get(node->t()).as<loco::TensorShape>(); - assert(t_shape == loco::shape_get(node->e()).as<loco::TensorShape>()); + auto t_shape = luci::shape_get(node->t()).as<loco::TensorShape>(); + assert(t_shape == luci::shape_get(node->e()).as<loco::TensorShape>()); // condition shape validation - auto c_shape = loco::shape_get(node->condition()).as<loco::TensorShape>(); + auto c_shape = luci::shape_get(node->condition()).as<loco::TensorShape>(); if (c_shape.rank() != t_shape.rank()) { if (c_shape.rank() != 0 && c_shape.rank() != 1) @@ -1248,9 +1272,9 @@ loco::NodeShape infer_select(const luci::CircleSelect *node) loco::NodeShape infer_select_v2(const luci::CircleSelectV2 *node) { - auto c_shape = loco::shape_get(node->condition()).as<loco::TensorShape>(); - auto t_shape = loco::shape_get(node->t()).as<loco::TensorShape>(); - auto e_shape = loco::shape_get(node->e()).as<loco::TensorShape>(); + auto c_shape = luci::shape_get(node->condition()).as<loco::TensorShape>(); + auto t_shape = luci::shape_get(node->t()).as<loco::TensorShape>(); + auto e_shape = luci::shape_get(node->e()).as<loco::TensorShape>(); // validate ability to broadcast shapes to each other auto b_shape = broadcast_shape(broadcast_shape(c_shape, t_shape), e_shape); @@ -1259,7 +1283,7 @@ loco::NodeShape infer_select_v2(const luci::CircleSelectV2 *node) loco::NodeShape infer_shape(const luci::CircleShape *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); loco::TensorShape output_shape; @@ -1274,7 +1298,7 @@ loco::NodeShape infer_slice(const luci::CircleSlice *node) const loco::DataType S32 = loco::DataType::S32; const loco::DataType S64 = loco::DataType::S64; - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); auto const_begin = loco::must_cast<luci::CircleConst *>(node->begin()); auto const_size = loco::must_cast<luci::CircleConst *>(node->size()); @@ -1318,7 +1342,7 @@ loco::NodeShape infer_space_to_batch_nd(const luci::CircleSpaceToBatchND *node) { const loco::DataType S32 = loco::DataType::S32; - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // Support only input rank is 3 and 4 assert(input_shape.rank() == 3 || input_shape.rank() == 4); @@ -1330,8 +1354,8 @@ loco::NodeShape infer_space_to_batch_nd(const luci::CircleSpaceToBatchND *node) auto const_paddings = loco::must_cast<luci::CircleConst *>(node->paddings()); LUCI_ASSERT(const_paddings->dtype() == S32, "Only support int32 paddings"); - auto const_block_shape_shape = loco::shape_get(const_block_shape).as<loco::TensorShape>(); - auto const_paddings_shape = loco::shape_get(const_paddings).as<loco::TensorShape>(); + auto const_block_shape_shape = luci::shape_get(const_block_shape).as<loco::TensorShape>(); + auto const_paddings_shape = luci::shape_get(const_paddings).as<loco::TensorShape>(); assert(const_block_shape_shape.rank() == 1); assert(const_paddings_shape.rank() == 2); @@ -1374,7 +1398,7 @@ loco::NodeShape infer_space_to_batch_nd(const luci::CircleSpaceToBatchND *node) loco::NodeShape infer_space_to_depth(const luci::CircleSpaceToDepth *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); LUCI_ASSERT(input_shape.rank() == 4, "Only input rank 4 is supported"); // Only data format NHWC is supported @@ -1412,19 +1436,33 @@ loco::NodeShape infer_sparse_to_dense(const luci::CircleSparseToDense *node) auto output_shape_node = dynamic_cast<luci::CircleConst *>(node->output_shape()); if (output_shape_node != nullptr) { - // Only support node with S32 - LUCI_ASSERT(output_shape_node->dtype() == loco::DataType::S32, - "Only support int32 CircleConst"); + const auto output_shape_type = output_shape_node->dtype(); if (output_shape_node->rank() != 1) INTERNAL_EXN_V("Only support rank 1 CircleConst", oops::to_uint32(output_shape_node->rank())); - shape.rank(output_shape_node->size<loco::DataType::S32>()); + if (output_shape_type == loco::DataType::S32) + { + shape.rank(output_shape_node->size<loco::DataType::S32>()); - for (uint32_t axis = 0; axis < shape.rank(); ++axis) + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + shape.dim(axis) = output_shape_node->at<loco::DataType::S32>(axis); + } + } + else if (output_shape_type == loco::DataType::S64) { - shape.dim(axis) = output_shape_node->at<loco::DataType::S32>(axis); + shape.rank(output_shape_node->size<loco::DataType::S64>()); + + for (uint32_t axis = 0; axis < shape.rank(); ++axis) + { + shape.dim(axis) = output_shape_node->at<loco::DataType::S64>(axis); + } + } + else + { + INTERNAL_EXN("Output shape of SparseToDense must be either int32 or int64"); } } else @@ -1453,7 +1491,7 @@ loco::NodeShape infer_strided_slice(const luci::CircleStridedSlice *node) loco::NodeShape infer_squeeze(const luci::CircleSqueeze *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); // TODO input shape may be unknown before runtime std::vector<bool> do_squeeze(input_shape.rank(), false); @@ -1508,7 +1546,7 @@ loco::NodeShape infer_tile(const luci::CircleTile *node) { const loco::DataType S32 = loco::DataType::S32; - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); auto multiples = loco::must_cast<luci::CircleConst *>(node->multiples()); // TODO support non-const case @@ -1534,7 +1572,7 @@ loco::NodeShape infer_tile(const luci::CircleTile *node) loco::NodeShape infer_transpose(const luci::CircleTranspose *node) { - auto input_shape = loco::shape_get(node->a()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->a()).as<loco::TensorShape>(); auto perm_node = loco::must_cast<luci::CircleConst *>(node->perm()); @@ -1576,7 +1614,7 @@ loco::NodeShape infer_unpack(const luci::CircleUnpack *node) // CircleUnpack provides list(array) of Tensors which has one less dimension of the input // We'll set shape of CircleUnpack to shape of actual outputs // TODO fix this if any problem rises - auto value_shape = loco::shape_get(node->value()).as<loco::TensorShape>(); + auto value_shape = luci::shape_get(node->value()).as<loco::TensorShape>(); auto axis = node->axis(); auto num = node->num(); @@ -1610,9 +1648,9 @@ loco::NodeShape infer_unpack(const luci::CircleUnpack *node) loco::NodeShape infer_unidirectionalsequencelstm(const luci::CircleUnidirectionalSequenceLSTM *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); auto recurrent_to_output_weights = - loco::shape_get(node->recurrent_to_output_weights()).as<loco::TensorShape>(); + luci::shape_get(node->recurrent_to_output_weights()).as<loco::TensorShape>(); auto rank = input_shape.rank(); loco::TensorShape output_shape; output_shape.rank(rank); @@ -1626,7 +1664,7 @@ loco::NodeShape infer_unidirectionalsequencelstm(const luci::CircleUnidirectiona loco::NodeShape infer_unique(const luci::CircleUnique *node) { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); assert(input_shape.rank() == 1); @@ -1641,7 +1679,7 @@ loco::NodeShape infer_bcq_fully_connected(const luci::CircleBCQFullyConnected *n { loco::TensorShape out_shape; - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); auto weights_clusters = loco::must_cast<luci::CircleConst *>(node->weights_clusters()); LUCI_ASSERT(input_shape.rank() == 2, "Input rank of BCQFullyConnected should be 2"); @@ -1664,8 +1702,8 @@ loco::NodeShape infer_bcq_gather(const luci::CircleBCQGather *node) loco::TensorShape input_shape; loco::TensorShape output_shape; - const auto input_binary_shape = loco::shape_get(node->input_binary()).as<loco::TensorShape>(); - const auto indices_shape = loco::shape_get(node->indices()).as<loco::TensorShape>(); + const auto input_binary_shape = luci::shape_get(node->input_binary()).as<loco::TensorShape>(); + const auto indices_shape = luci::shape_get(node->indices()).as<loco::TensorShape>(); auto axis = node->axis(); auto input_clusters = loco::must_cast<luci::CircleConst *>(node->input_clusters()); @@ -1712,46 +1750,6 @@ loco::NodeShape infer_output(const luci::CircleOutput *node) return loco::NodeShape{*output_shape}; } -loco::NodeShape infer_if_out(const luci::CircleIfOut *node) -{ - /** - * @note IF operator type and shape are that of the "then" and "else" - * Graph Outputs. - */ - auto circle_if = dynamic_cast<const luci::CircleIf *>(node->input()); - if (circle_if == nullptr) - { - INTERNAL_EXN("CircleIf IR is not configured correctly"); - } - - auto index = node->index(); - auto then_graph = circle_if->then_graph(); - auto else_graph = circle_if->else_graph(); - assert(then_graph != nullptr); - assert(else_graph != nullptr); - - // shape and type are assumed to be same - // these are checked at post_import_graph() in Import - auto then_outputs = loco::output_nodes(then_graph); - auto else_outputs = loco::output_nodes(else_graph); - assert(then_outputs.size() == else_outputs.size()); - assert(index < static_cast<int32_t>(then_outputs.size())); - - auto then_out = loco::must_cast<luci::CircleOutput *>(then_outputs.at(index)); - auto else_out = loco::must_cast<luci::CircleOutput *>(else_outputs.at(index)); - - auto then_graph_outputs = then_graph->outputs(); // loco::GraphOutput items - auto else_graph_outputs = else_graph->outputs(); - assert(then_graph_outputs->size() == else_graph_outputs->size()); - - auto then_graph_output = then_graph_outputs->at(then_out->index()); - auto else_graph_output = else_graph_outputs->at(else_out->index()); - (void)else_graph_output; // make compiler happy for unused variable warnings - assert(*then_graph_output->shape() == *else_graph_output->shape()); - - return loco::NodeShape{*then_graph_output->shape()}; -} - loco::NodeShape infer_non_max_suppression_v4_out(const luci::CircleNonMaxSuppressionV4Out *node) { const loco::DataType S32 = loco::DataType::S32; @@ -1818,7 +1816,7 @@ loco::NodeShape infer_split_out(const luci::CircleSplitOut *node) loco::NodeShape unknown; - auto split_shape = loco::shape_get(split).as<loco::TensorShape>(); + auto split_shape = luci::shape_get(split).as<loco::TensorShape>(); auto split_dim = dynamic_cast<const luci::CircleConst *>(split->split_dim()); if (split_dim == nullptr) @@ -1852,7 +1850,7 @@ loco::NodeShape infer_split_v_out(const luci::CircleSplitVOut *node) loco::NodeShape unknown; - auto split_shape = loco::shape_get(split).as<loco::TensorShape>(); + auto split_shape = luci::shape_get(split).as<loco::TensorShape>(); auto size_splits = dynamic_cast<const luci::CircleConst *>(split->size_splits()); if (size_splits == nullptr) @@ -1913,7 +1911,7 @@ loco::NodeShape infer_top_k_v2_out(const luci::CircleTopKV2Out *node) INTERNAL_EXN("CircleSplit IR is not configured correctly"); // shape of topkv2 is same as topkv2->input() - auto input_shape = loco::shape_get(topkv2).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(topkv2).as<loco::TensorShape>(); auto node_k = loco::must_cast<const luci::CircleConst *>(topkv2->k()); LUCI_ASSERT(node_k->dtype() == S32, "Only support Int32"); @@ -1940,7 +1938,7 @@ loco::NodeShape infer_unique_out(const luci::CircleUniqueOut *node) } assert(node->index() == 1); auto unique = loco::must_cast<luci::CircleUnique *>(node->input()); - auto unique_shape = loco::shape_get(unique->input()).as<loco::TensorShape>(); + auto unique_shape = luci::shape_get(unique->input()).as<loco::TensorShape>(); assert(unique_shape.rank() == 1); @@ -1958,7 +1956,7 @@ loco::NodeShape infer_unpack_out(const luci::CircleUnpackOut *node) INTERNAL_EXN("CircleUnpack IR is not configured correctly"); } - auto unpack_shape = loco::shape_get(unpack).as<loco::TensorShape>(); + auto unpack_shape = luci::shape_get(unpack).as<loco::TensorShape>(); return loco::NodeShape{unpack_shape}; } @@ -2025,8 +2023,8 @@ public: loco::NodeShape visit(const luci::CircleBatchMatMul *node) final { - auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>(); - auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>(); + auto x_shape = luci::shape_get(node->x()).as<loco::TensorShape>(); + auto y_shape = luci::shape_get(node->y()).as<loco::TensorShape>(); return infer_batchmatmul_shape(x_shape, y_shape, node->adj_x(), node->adj_y()); } @@ -2065,7 +2063,7 @@ public: loco::NodeShape visit(const luci::CircleDequantize *node) final { - const auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2073,7 +2071,7 @@ public: loco::NodeShape visit(const luci::CircleElu *node) final { - auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2087,6 +2085,8 @@ public: return infer_expand_dims(node); } + loco::NodeShape visit(const luci::CircleFakeQuant *node) final { return use_inputs(node); } + loco::NodeShape visit(const luci::CircleFill *node) final { return infer_fill(node); } loco::NodeShape visit(const luci::CircleFloor *node) final { return use_x(node); } @@ -2112,7 +2112,7 @@ public: { // Shape of CircleIf is not used. Just use input 0 assert(node->input_count() > 0); - const auto input_shape = loco::shape_get(node->input(0)).as<loco::TensorShape>(); + const auto input_shape = luci::shape_get(node->input(0)).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2125,7 +2125,7 @@ public: loco::NodeShape visit(const luci::CircleLeakyRelu *node) final { - const auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>(); + const auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2135,7 +2135,7 @@ public: loco::NodeShape visit(const luci::CircleLocalResponseNormalization *node) final { - const auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2184,13 +2184,13 @@ public: loco::NodeShape visit(const luci::CircleNonMaxSuppressionV4 *node) final { - const auto boxes_shape = loco::shape_get(node->boxes()).as<loco::TensorShape>(); + const auto boxes_shape = luci::shape_get(node->boxes()).as<loco::TensorShape>(); return loco::NodeShape{boxes_shape}; } loco::NodeShape visit(const luci::CircleNonMaxSuppressionV5 *node) final { - const auto boxes_shape = loco::shape_get(node->boxes()).as<loco::TensorShape>(); + const auto boxes_shape = luci::shape_get(node->boxes()).as<loco::TensorShape>(); return loco::NodeShape{boxes_shape}; } @@ -2244,21 +2244,21 @@ public: loco::NodeShape visit(const luci::CircleRelu *node) final { - auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } loco::NodeShape visit(const luci::CircleRelu6 *node) final { - auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } loco::NodeShape visit(const luci::CircleReluN1To1 *node) final { - auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->features()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2284,7 +2284,7 @@ public: loco::NodeShape visit(const luci::CircleReverseSequence *node) final { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2293,9 +2293,9 @@ public: loco::NodeShape visit(const luci::CircleReverseV2 *node) final { - auto input_shape = loco::shape_get(node->tensor()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->tensor()).as<loco::TensorShape>(); - LUCI_ASSERT(loco::shape_get(node->axis()).as<loco::TensorShape>().rank() == 1, + LUCI_ASSERT(luci::shape_get(node->axis()).as<loco::TensorShape>().rank() == 1, "Tensor must be 1-D"); return loco::NodeShape{input_shape}; @@ -2340,14 +2340,14 @@ public: loco::NodeShape visit(const luci::CircleSplit *node) final { // We'll set Split output as same as input so that SplitOut can handle it's own shape - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } loco::NodeShape visit(const luci::CircleSplitV *node) final { // We'll set SplitV output as same as input so that SplitOut can handle it's own shape - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2382,7 +2382,7 @@ public: loco::NodeShape visit(const luci::CircleTopKV2 *node) final { // set shape of this node as same as input - const auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + const auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2408,13 +2408,13 @@ public: { // Shape of CircleWhile is not used. Just use input 0 assert(node->arity() > 0); - const auto input_shape = loco::shape_get(node->input(0)).as<loco::TensorShape>(); + const auto input_shape = luci::shape_get(node->input(0)).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } loco::NodeShape visit(const luci::CircleZerosLike *node) final { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2429,7 +2429,7 @@ public: loco::NodeShape visit(const luci::CircleInstanceNorm *node) final { - auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); + auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>(); return loco::NodeShape{input_shape}; } @@ -2445,8 +2445,6 @@ public: loco::NodeShape visit(const luci::CircleCustomOut *node) final { return use_own(node); } - loco::NodeShape visit(const luci::CircleIfOut *node) final { return infer_if_out(node); } - loco::NodeShape visit(const luci::CircleNonMaxSuppressionV4Out *node) final { return infer_non_max_suppression_v4_out(node); diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp deleted file mode 100644 index ac27db3bd..000000000 --- a/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright (c) 2020 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 "TestGraph.h" -#include "luci/Service/CircleShapeInferenceRule.h" - -#include <luci/IR/CircleNodes.h> -#include <luci/IR/CircleDialect.h> - -#include <loco.h> -#include <loco/IR/CanonicalDialect.h> -#include <loco/Service/ShapeInference.h> -#include <loco/Service/CanonicalShapeInferenceRule.h> -#include <loco/Service/MultiDialectShapeInferenceRule.h> - -#include <oops/InternalExn.h> - -#include <gtest/gtest.h> - -#include <memory> - -namespace -{ - -bool shape_pass(loco::Graph *g) -{ - loco::CanonicalShapeInferenceRule canonical_rule; - luci::CircleShapeInferenceRule circle_rule; - loco::MultiDialectShapeInferenceRule rules; - - rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(luci::CircleDialect::get(), &circle_rule); - - return loco::apply(&rules).to(g); -} - -} // namespace - -TEST(CircleShapeInferenceRuleTest, minimal_with_CircleRelu) -{ - // Create a simple network - luci::test::TestGraph graph; - auto relu_node = graph.append<luci::CircleRelu>(graph.input_node); - graph.complete(relu_node); - - // set shape - { - graph.input_node->rank(2); - graph.input_node->dim(0) = 3; - graph.input_node->dim(1) = 4; - - graph.output_node->rank(2); - graph.output_node->dim(0) = 3; - graph.output_node->dim(1) = 4; - - luci::test::graph_input_shape(graph.input_node); - luci::test::graph_output_shape(graph.output_node); - } - - // pre-check - ASSERT_FALSE(loco::shape_known(relu_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(relu_node)); - ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(relu_node).domain()); - - auto shape = loco::shape_get(relu_node).as<loco::TensorShape>(); - ASSERT_EQ(2, shape.rank()); - ASSERT_EQ(3, shape.dim(0)); - ASSERT_EQ(4, shape.dim(1)); - } -} - -// based on the case shown in -// https://www.corvil.com/kb/what-is-the-difference-between-same-and-valid-padding-in-tf-nn-max-pool-of-tensorflow -TEST(CircleShapeInferenceRuleTest, avgpool2d_valid) -{ - luci::test::TestGraph graph; - auto avg_node = graph.append<luci::CircleAveragePool2D>(graph.input_node); - graph.complete(); - - auto input_node = graph.input_node; - { - input_node->shape({1, 4, 3, 1}); - luci::test::graph_input_shape(input_node); - } - auto output_node = graph.output_node; - { - output_node->shape({1, 2, 1, 1}); - luci::test::graph_output_shape(output_node); - } - // setting CircleAveragePool2D - { - avg_node->filter()->h(2); - avg_node->filter()->w(2); - avg_node->stride()->h(2); - avg_node->stride()->w(2); - avg_node->fusedActivationFunction(luci::FusedActFunc::NONE); - avg_node->padding(luci::Padding::VALID); - } - ASSERT_FALSE(loco::shape_known(avg_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(avg_node)); - ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(avg_node).domain()); - - auto shape = loco::shape_get(avg_node).as<loco::TensorShape>(); - ASSERT_EQ(4, shape.rank()); - ASSERT_EQ(1, shape.dim(0).value()); - ASSERT_EQ(2, shape.dim(1).value()); - ASSERT_EQ(1, shape.dim(2).value()); - ASSERT_EQ(1, shape.dim(3).value()); - } -} - -TEST(CircleShapeInferenceRuleTest, avgpool2d_same) -{ - luci::test::TestGraph graph; - auto avg_node = graph.append<luci::CircleAveragePool2D>(graph.input_node); - graph.complete(); - - auto input_node = graph.input_node; - { - input_node->shape({1, 4, 3, 1}); - luci::test::graph_input_shape(input_node); - } - auto output_node = graph.output_node; - { - output_node->shape({1, 2, 2, 1}); - luci::test::graph_output_shape(output_node); - } - - // setting CircleAveragePool2D - { - avg_node->filter()->h(2); - avg_node->filter()->w(2); - avg_node->stride()->h(2); - avg_node->stride()->w(2); - avg_node->fusedActivationFunction(luci::FusedActFunc::NONE); - avg_node->padding(luci::Padding::SAME); - } - - ASSERT_FALSE(loco::shape_known(avg_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(avg_node)); - ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(avg_node).domain()); - - auto shape = loco::shape_get(avg_node).as<loco::TensorShape>(); - ASSERT_EQ(4, shape.rank()); - ASSERT_EQ(1, shape.dim(0).value()); - ASSERT_EQ(2, shape.dim(1).value()); - ASSERT_EQ(2, shape.dim(2).value()); - ASSERT_EQ(1, shape.dim(3).value()); - } -} - -/** - * @note Function to test: Shape inference of two different input shapes - * - * Rank expansion to higher input side - * x(2,1,5) + y(3,5) --> x(2,1,5) + y(1,3,5) - * Do output shape inference like numpy - * x(2,1,5) + y(1,3,5) --> output(2,3,5) - * For each axis, dim value should be same OR one of them should be 1 - */ -TEST(CircleShapeInferenceRuleTest, TFAdd_shapeinf_different) -{ - auto g = loco::make_graph(); - - auto x_node = g->nodes()->create<luci::CircleInput>(); - { - x_node->rank(3); - x_node->dim(0) = 2; - x_node->dim(1) = 1; - x_node->dim(2) = 5; - } - auto y_node = g->nodes()->create<luci::CircleInput>(); - { - y_node->rank(2); - y_node->dim(0) = 3; - y_node->dim(1) = 5; - } - auto add_node = g->nodes()->create<luci::CircleAdd>(); - { - add_node->x(x_node); - add_node->y(y_node); - } - auto output_node = g->nodes()->create<luci::CircleOutput>(); - { - output_node->from(add_node); - } - - auto x_input = g->inputs()->create(); - { - x_input->name("x"); - luci::link(x_input, x_node); - } - auto y_input = g->inputs()->create(); - { - y_input->name("y"); - luci::link(y_input, y_node); - } - auto output = g->outputs()->create(); - { - output->name("output"); - luci::link(output, output_node); - } - - luci::test::graph_input_shape(x_node); - luci::test::graph_input_shape(y_node); - luci::test::graph_output_shape(output_node); - - // pre-check - ASSERT_FALSE(loco::shape_known(add_node)); - - // shape inference - while (shape_pass(g.get()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(add_node)); - ASSERT_EQ(loco::Domain::Tensor, loco::shape_get(add_node).domain()); - - auto shape = loco::shape_get(add_node).as<loco::TensorShape>(); - ASSERT_EQ(3, shape.rank()); - ASSERT_EQ(2, shape.dim(0)); - ASSERT_EQ(3, shape.dim(1)); - ASSERT_EQ(5, shape.dim(2)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleTranspose_simple) -{ - luci::test::ExampleGraph<luci::test::ExampleGraphType::CircleTranspose> g; - - g.input_node->rank(3); - g.input_node->dim(0) = 3; - g.input_node->dim(1) = 8; - g.input_node->dim(2) = 1; - - g.const_perm->dtype(loco::DataType::S32); - g.const_perm->rank(1); - g.const_perm->dim(0) = 3; - g.const_perm->size<loco::DataType::S32>(3); - g.const_perm->at<loco::DataType::S32>(0) = 1; - g.const_perm->at<loco::DataType::S32>(1) = 2; - g.const_perm->at<loco::DataType::S32>(2) = 0; - - luci::test::graph_input_shape(g.input_node); - luci::test::graph_output_shape(g.output_node); - - // pre-check - ASSERT_FALSE(loco::shape_known(g.transpose_node)); - - // shape inference - while (shape_pass(g.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(g.transpose_node)); - - auto shape = loco::shape_get(g.transpose_node).as<loco::TensorShape>(); - ASSERT_EQ(3, shape.rank()); - ASSERT_EQ(8, shape.dim(0)); - ASSERT_EQ(1, shape.dim(1)); - ASSERT_EQ(3, shape.dim(2)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleSqueeze) -{ - luci::test::TestGraph graph; - auto squeeze_node = graph.append<luci::CircleSqueeze>(graph.input_node); - graph.complete(); - - auto input_node = graph.input_node; - { - input_node->shape({1, 4, 3, 1}); - } - auto output_node = graph.output_node; - { - output_node->shape({4, 3, 1}); - } - - luci::test::graph_input_shape(input_node); - luci::test::graph_output_shape(output_node); - - squeeze_node->squeeze_dims({0}); - - // pre-check - ASSERT_FALSE(loco::shape_known(squeeze_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(squeeze_node)); - - auto shape = loco::shape_get(squeeze_node).as<loco::TensorShape>(); - ASSERT_EQ(3, shape.rank()); - ASSERT_EQ(4, shape.dim(0)); - ASSERT_EQ(3, shape.dim(1)); - ASSERT_EQ(1, shape.dim(2)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleExpandDims) -{ - luci::test::TestGraph graph; - auto axis = graph.append<luci::CircleConst>(); - axis->dtype(loco::DataType::S32); - axis->rank(0); - axis->size<loco::DataType::S32>(1); - axis->at<loco::DataType::S32>(0) = 1; - - auto expand_dims = graph.append<luci::CircleExpandDims>(graph.input_node, axis); - graph.complete(); - - auto input_node = graph.input_node; - { - input_node->shape({4, 3}); - } - - auto output_node = graph.output_node; - { - output_node->from(expand_dims); - } - - luci::test::graph_input_shape(input_node); - luci::test::graph_output_shape(output_node); - - // shape inference - while (shape_pass(graph.graph())) - ; - - // validation - { - ASSERT_TRUE(loco::shape_known(expand_dims)); - - auto shape = loco::shape_get(expand_dims).as<loco::TensorShape>(); - - ASSERT_EQ(3, shape.rank()); - ASSERT_EQ(4, shape.dim(0)); - ASSERT_EQ(1, shape.dim(1)); - ASSERT_EQ(3, shape.dim(2)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleSqueezeAll) -{ - luci::test::TestGraph graph; - auto squeeze_node = graph.append<luci::CircleSqueeze>(graph.input_node); - graph.complete(); - - auto input_node = graph.input_node; - { - input_node->shape({1, 4, 3, 1}); - } - auto output_node = graph.output_node; - { - input_node->shape({4, 3}); - } - - luci::test::graph_input_shape(input_node); - luci::test::graph_output_shape(output_node); - - squeeze_node->squeeze_dims({}); - - // pre-check - ASSERT_FALSE(loco::shape_known(squeeze_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(squeeze_node)); - - auto shape = loco::shape_get(squeeze_node).as<loco::TensorShape>(); - ASSERT_EQ(2, shape.rank()); - ASSERT_EQ(4, shape.dim(0)); - ASSERT_EQ(3, shape.dim(1)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleGatherNd_simple) -{ - luci::test::TestGraph graph; - auto indices_const = graph.append<luci::CircleConst>(); - auto gather_nd_node = graph.append<luci::CircleGatherNd>(graph.input_node, indices_const); - graph.complete(); - - { - auto input_node = graph.input_node; - input_node->shape({1, 4, 4, 3}); - luci::test::graph_input_shape(input_node); - } - { - auto output_node = graph.output_node; - output_node->shape({1, 2, 2, 3}); - luci::test::graph_output_shape(output_node); - } - - { - indices_const->shape({1, 2, 3}); - } - - // pre-check - ASSERT_FALSE(loco::shape_known(gather_nd_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(gather_nd_node)); - - auto shape = loco::shape_get(gather_nd_node).as<loco::TensorShape>(); - ASSERT_EQ(3, shape.rank()); - ASSERT_EQ(1, shape.dim(0)); - ASSERT_EQ(2, shape.dim(1)); - ASSERT_EQ(3, shape.dim(2)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleGatherNd_slices) -{ - luci::test::TestGraph graph; - auto indices_const = graph.append<luci::CircleConst>(); - auto gather_nd_node = graph.append<luci::CircleGatherNd>(graph.input_node, indices_const); - graph.complete(); - - { - auto input_node = graph.input_node; - input_node->shape({1, 4, 4, 3}); - luci::test::graph_input_shape(input_node); - } - { - auto output_node = graph.output_node; - output_node->shape({1, 2, 4, 4, 3}); - luci::test::graph_output_shape(output_node); - } - - { - indices_const->shape({1, 2, 1}); - } - - // pre-check - ASSERT_FALSE(loco::shape_known(gather_nd_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(gather_nd_node)); - - auto shape = loco::shape_get(gather_nd_node).as<loco::TensorShape>(); - ASSERT_EQ(5, shape.rank()); - ASSERT_EQ(1, shape.dim(0)); - ASSERT_EQ(2, shape.dim(1)); - ASSERT_EQ(4, shape.dim(2)); - ASSERT_EQ(4, shape.dim(3)); - ASSERT_EQ(3, shape.dim(4)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleGatherNd_NEG) -{ - luci::test::TestGraph graph; - auto indices_const = graph.append<luci::CircleConst>(); - auto gather_nd_node = graph.append<luci::CircleGatherNd>(graph.input_node, indices_const); - graph.complete(); - - { - auto input_node = graph.input_node; - input_node->shape({1, 4, 4, 3}); - luci::test::graph_input_shape(input_node); - } - { - // Does not matter, because test should fail anyway - auto output_node = graph.output_node; - output_node->shape({0, 0, 0}); - luci::test::graph_output_shape(output_node); - } - - { - indices_const->shape({1, 2, 5}); - } - - // pre-check - ASSERT_FALSE(loco::shape_known(gather_nd_node)); - - // had to pack into lambda to check throw - auto lambda = [&]() { - // shape inference - while (shape_pass(graph.graph()) == true) - ; - }; - - ASSERT_THROW(lambda(), oops::InternalExn); -} - -TEST(CircleShapeInferenceRuleTest, CircleResizeNearestNeighbor) -{ - luci::test::TestGraph graph; - auto size_const = graph.append<luci::CircleConst>(); - size_const->dtype(loco::DataType::S32); - size_const->rank(1); - size_const->dim(0) = 2; - size_const->size<loco::DataType::S32>(2); - size_const->at<loco::DataType::S32>(0) = 16; - size_const->at<loco::DataType::S32>(1) = 16; - auto resize_node = graph.append<luci::CircleResizeNearestNeighbor>(graph.input_node, size_const); - graph.complete(); - - { - auto input_node = graph.input_node; - input_node->shape({1, 4, 4, 3}); - luci::test::graph_input_shape(input_node); - } - { - auto output_node = graph.output_node; - output_node->from(resize_node); - luci::test::graph_output_shape(output_node); - } - - // pre-check - ASSERT_FALSE(loco::shape_known(resize_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(resize_node)); - - auto shape = loco::shape_get(resize_node).as<loco::TensorShape>(); - ASSERT_EQ(4, shape.rank()); - ASSERT_EQ(1, shape.dim(0)); - ASSERT_EQ(16, shape.dim(1)); - ASSERT_EQ(16, shape.dim(2)); - ASSERT_EQ(3, shape.dim(3)); - } -} - -TEST(CircleShapeInferenceRuleTest, CircleResizeBilinear) -{ - luci::test::TestGraph graph; - auto size_const = graph.append<luci::CircleConst>(); - size_const->dtype(loco::DataType::S32); - size_const->rank(1); - size_const->dim(0) = 2; - size_const->size<loco::DataType::S32>(2); - size_const->at<loco::DataType::S32>(0) = 16; - size_const->at<loco::DataType::S32>(1) = 16; - auto resize_node = graph.append<luci::CircleResizeBilinear>(graph.input_node, size_const); - graph.complete(); - - { - auto input_node = graph.input_node; - input_node->shape({1, 4, 4, 3}); - luci::test::graph_input_shape(input_node); - } - { - auto output_node = graph.output_node; - output_node->from(resize_node); - luci::test::graph_output_shape(output_node); - } - - // pre-check - ASSERT_FALSE(loco::shape_known(resize_node)); - - // shape inference - while (shape_pass(graph.graph()) == true) - ; - - // Verify - { - ASSERT_TRUE(loco::shape_known(resize_node)); - - auto shape = loco::shape_get(resize_node).as<loco::TensorShape>(); - ASSERT_EQ(4, shape.rank()); - ASSERT_EQ(1, shape.dim(0)); - ASSERT_EQ(16, shape.dim(1)); - ASSERT_EQ(16, shape.dim(2)); - ASSERT_EQ(3, shape.dim(3)); - } -} diff --git a/compiler/luci/service/src/CircleShapeSignatureInference.cpp b/compiler/luci/service/src/CircleShapeSignatureInference.cpp deleted file mode 100644 index 1ccaa19d5..000000000 --- a/compiler/luci/service/src/CircleShapeSignatureInference.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020 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/CircleShapeSignatureInference.h" - -#include <luci/Log.h> - -namespace -{ - -std::ostream &operator<<(std::ostream &os, const luci::ShapeSignature &shape_signature) -{ - os << "["; - for (uint32_t r = 0; r < shape_signature.rank(); ++r) - { - if (r) - os << ","; - os << shape_signature.dim(r); - } - os << "]"; - return os; -} - -} // namespace - -namespace luci -{ - -namespace ssinf -{ - -bool Rule::infer(const luci::CircleNode *circle_node, ShapeSignature &shape_signature) const -{ - LOGGER(l); - - // There is nothing to check before ShapeSignatureInference. - - Algorithm alg; - - shape_signature = circle_node->accept(&alg); - - VERBOSE(l, 1) << "[luci] Shape Signature( " << circle_node->name() << " )"; - VERBOSE(l, 1) << " before: " << circle_node->shape_signature(); - VERBOSE(l, 1) << " after: " << shape_signature; - - return true; -} - -} // namespace ssinf - -} // namespace luci diff --git a/compiler/luci/service/src/CircleShapeSignatureInferenceHelper.cpp b/compiler/luci/service/src/CircleShapeSignatureInferenceHelper.cpp deleted file mode 100644 index d7d1a24e8..000000000 --- a/compiler/luci/service/src/CircleShapeSignatureInferenceHelper.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2020 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/CircleShapeSignatureInferenceHelper.h" - -#include <loco.h> - -#include <luci/Log.h> - -#include <oops/InternalExn.h> - -namespace luci -{ - -namespace ssinf -{ - -luci::ShapeSignature legalized_signature(const luci::ShapeSignature &signature) -{ - // If shape signature has at least one -1, it is not static. - for (uint32_t i = 0; i < signature.rank(); ++i) - if (signature.dim(i) == -1) - return signature; - - // If all dimensions are static, return empty shape signature. - return luci::ShapeSignature(); -} - -ShapeSignature reduced_signature(const loco::Node *node, const loco::Node *indices, bool keep_dims) -{ - LOGGER(l); - - ShapeSignature input_signature; - ShapeSignature output_signature; - - auto circle_node = loco::must_cast<const luci::CircleNode *>(node); - if (circle_node->shape_signature().rank() > 0) - input_signature = circle_node->shape_signature(); - else - { - input_signature.rank(circle_node->rank()); - for (uint32_t i = 0; i < circle_node->rank(); ++i) - input_signature.dim(i) = circle_node->dim(i).value(); - } - - // If input rank is 0, it means that one of following case is occurred. - // - Input is scalar : result is always scalar - // - Input shape signature is not inferenced : cannot infer output shape signauture - // Therefore, when input signature rank is 0, always return empty signature. - if (input_signature.rank() == 0) - return output_signature; - - // When reduction_indices is not constant - auto reduction_indices = dynamic_cast<const luci::CircleConst *>(indices); - if (reduction_indices == nullptr) - { - if (keep_dims) - { - // If keep_dims is true, rank is not changed. - output_signature.rank(input_signature.rank()); - for (uint32_t i = 0; i < output_signature.rank(); ++i) - output_signature.dim(i) = -1; - } - else - { - // There is no way to inference for this case. - // Do nothing to return empty signature. - INFO(l) << "[CircleShapeSignatureInferenceHelper] " << circle_node->name() << std::endl; - INFO(l) << " reduced_signature : cannot infer because of non-constant node" << std::endl; - } - - return output_signature; - } - - std::vector<int32_t> reduction_values; - if (reduction_indices->dtype() == loco::DataType::S32) - { - auto reduction_size = reduction_indices->size<loco::DataType::S32>(); - for (uint32_t i = 0; i < reduction_size; ++i) - { - int32_t axis = reduction_indices->at<loco::DataType::S32>(i); - if (axis < 0) - axis += input_signature.rank(); - - if (!(0 <= axis && axis < static_cast<int32_t>(input_signature.rank()))) - INTERNAL_EXN_V("Invalid reduction axis for REDUCER", oops::to_uint32(axis)); - - reduction_values.push_back(axis); - } - } - else if (reduction_indices->dtype() == loco::DataType::S64) - { - auto reduction_size = reduction_indices->size<loco::DataType::S64>(); - for (uint32_t i = 0; i < reduction_size; ++i) - { - int32_t axis = static_cast<int32_t>(reduction_indices->at<loco::DataType::S64>(i)); - if (axis < 0) - axis += input_signature.rank(); - - if (!(0 <= axis && axis < static_cast<int32_t>(input_signature.rank()))) - INTERNAL_EXN_V("Invalid reduction axis for REDUCER", oops::to_uint32(axis)); - - reduction_values.push_back(axis); - } - } - else - { - INTERNAL_EXN("Wrong reduction axis type, Only INT32, INT64 supported."); - } - - if (keep_dims) - { - output_signature.rank(input_signature.rank()); - for (uint32_t i = 0; i < input_signature.rank(); ++i) - output_signature.dim(i) = input_signature.dim(i); - for (uint32_t i = 0; i < reduction_values.size(); ++i) - output_signature.dim(reduction_values.at(i)) = 1; - } - else - { - std::vector<bool> check_reduce(input_signature.rank(), false); - for (uint32_t i = 0; i < reduction_values.size(); ++i) - check_reduce.at(reduction_values.at(i)) = true; - - uint32_t reduce_cnt = 0; - for (uint32_t i = 0; i < check_reduce.size(); ++i) - if (check_reduce.at(i)) - ++reduce_cnt; - - output_signature.rank(input_signature.rank() - reduce_cnt); - for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i) - if (check_reduce.at(i) == false) - output_signature.dim(j++) = input_signature.dim(i); - } - - return output_signature; -} - -ShapeSignature input_arg_signature(const luci::CircleNode *node, uint32_t index) -{ - auto circle_input = loco::must_cast<luci::CircleNode *>(node->arg(index)); - return circle_input->shape_signature(); -} - -} // namespace ssinf - -} // namespace luci diff --git a/compiler/luci/service/src/CircleTypeInference.cpp b/compiler/luci/service/src/CircleTypeInference.cpp index b4755b51a..db9a37cb0 100644 --- a/compiler/luci/service/src/CircleTypeInference.cpp +++ b/compiler/luci/service/src/CircleTypeInference.cpp @@ -15,72 +15,23 @@ */ #include "luci/Service/CircleTypeInference.h" +#include "CircleTypeInferenceHelper.h" #include <luci/Log.h> #include <loco.h> -#include <loco/Service/TypeInference.h> - -#include <mio/circle/schema_generated.h> -#include <oops/InternalExn.h> #include <type_traits> namespace { -circle::TensorType translateLocoTypeToCircle(loco::DataType dtype) -{ - switch (dtype) - { - case loco::DataType::U8: - return circle::TensorType_UINT8; - // case loco::DataType::U16: unsupported - // case loco::DataType::U32: unsupported - // case loco::DataType::U64: unsupported - case loco::DataType::S8: - return circle::TensorType_INT8; - case loco::DataType::S16: - return circle::TensorType_INT16; - case loco::DataType::S32: - return circle::TensorType_INT32; - case loco::DataType::S64: - return circle::TensorType_INT64; - case loco::DataType::FLOAT16: - return circle::TensorType_FLOAT16; - case loco::DataType::FLOAT32: - return circle::TensorType_FLOAT32; - // case loco::DataType::FLOAT64: unsupported - case loco::DataType::BOOL: - return circle::TensorType_BOOL; - default: - break; - } - - INTERNAL_EXN_V("Invalid loco dtype", oops::to_uint32(dtype)); -} - -} // namespace - -namespace luci -{ - -circle::TensorType TypeInference::get(loco::Node *node) -{ - assert(loco::dtype_known(node)); - return translateLocoTypeToCircle(loco::dtype_get(node)); -} - -} // namespace luci - -namespace -{ - bool inputs_dtype_ready(const luci::CircleNode *node) { for (uint32_t arity = 0; arity < node->arity(); ++arity) { - if (node->dtype() == loco::DataType::Unknown) + auto input_node = loco::must_cast<luci::CircleNode *>(node->arg(arity)); + if (input_node->dtype() == loco::DataType::Unknown) return false; } diff --git a/compiler/luci/service/src/CircleTypeInferenceHelper.cpp b/compiler/luci/service/src/CircleTypeInferenceHelper.cpp index 75cd9f7b2..06edd70f2 100644 --- a/compiler/luci/service/src/CircleTypeInferenceHelper.cpp +++ b/compiler/luci/service/src/CircleTypeInferenceHelper.cpp @@ -14,7 +14,23 @@ * limitations under the License. */ -#include "luci/Service/CircleTypeInferenceHelper.h" +#include "CircleTypeInferenceHelper.h" + +namespace luci +{ + +loco::DataType dtype_get(const loco::Node *node) +{ + assert(luci::dtype_known(node)); + return loco::must_cast<const luci::CircleNode *>(node)->dtype(); +} + +bool dtype_known(const loco::Node *node) +{ + return loco::must_cast<const luci::CircleNode *>(node)->dtype() != loco::DataType::Unknown; +} + +} // namespace luci namespace luci { diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInferenceHelper.h b/compiler/luci/service/src/CircleTypeInferenceHelper.h index 296f99355..751340cc7 100644 --- a/compiler/luci/service/include/luci/Service/CircleTypeInferenceHelper.h +++ b/compiler/luci/service/src/CircleTypeInferenceHelper.h @@ -23,6 +23,20 @@ namespace luci { + +// NOTE Functions in this namespace will be removed after new inference +// algorithms are fully implemented. + +// This function is temporary function for deprecating loco::dtype_get +loco::DataType dtype_get(const loco::Node *node); + +// This function is temporary function for deprecating loco::dtype_known +bool dtype_known(const loco::Node *node); + +} // namespace luci + +namespace luci +{ namespace tinf // Namespace for Type Inference { diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.cpp index f738ab5a8..0b8d2af9e 100644 --- a/compiler/luci/service/src/CircleTypeInferenceRule.cpp +++ b/compiler/luci/service/src/CircleTypeInferenceRule.cpp @@ -15,6 +15,7 @@ */ #include "luci/Service/CircleTypeInferenceRule.h" +#include "CircleTypeInferenceHelper.h" #include <luci/IR/CircleDialect.h> #include <luci/IR/CircleNodeVisitor.h> @@ -29,24 +30,24 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT { // TODO Given a tensor x of complex numbers, Abs operation returns a tensor of type float32 or // float64. - loco::DataType visit(const luci::CircleAbs *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleAbs *node) final { return luci::dtype_get(node->x()); } - loco::DataType visit(const luci::CircleAdd *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleAdd *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleAddN *node) final { - auto dtype = loco::dtype_get(node->inputs(0)); + auto dtype = luci::dtype_get(node->inputs(0)); for (uint32_t idx = 1; idx < node->arity(); ++idx) { - auto dtype_idx = loco::dtype_get(node->inputs(idx)); + auto dtype_idx = luci::dtype_get(node->inputs(idx)); if (dtype != dtype_idx) { INTERNAL_EXN_V("ADD_N dtype not same as the first input: ", idx); } } - return loco::dtype_get(node->inputs(0)); + return luci::dtype_get(node->inputs(0)); } loco::DataType visit(const luci::CircleArgMax *node) final { return node->output_type(); } @@ -55,22 +56,22 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CircleAveragePool2D *node) final { - return loco::dtype_get(node->value()); + return luci::dtype_get(node->value()); } loco::DataType visit(const luci::CircleBatchMatMul *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleBatchToSpaceND *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleCast *node) final { return node->dtype(); } - loco::DataType visit(const luci::CircleCeil *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleCeil *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleConcatenation *node) final { @@ -78,87 +79,92 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT assert(node->numValues() > 0); for (uint32_t i = 1; i < node->numValues(); ++i) - assert(loco::dtype_get(node->values(i - 1)) == loco::dtype_get(node->values(i))); + assert(luci::dtype_get(node->values(i - 1)) == luci::dtype_get(node->values(i))); - return loco::dtype_get(node->values(0)); + return luci::dtype_get(node->values(0)); } loco::DataType visit(const luci::CircleConst *node) final { return node->dtype(); } loco::DataType visit(const luci::CircleConv2D *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleCos *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleCos *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleCustom *node) final { if (node->custom_code() == "BatchMatMulV2") { - return loco::dtype_get(node->inputs(0)); + return luci::dtype_get(node->inputs(0)); } return node->dtype(); } loco::DataType visit(const luci::CircleDepthToSpace *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleDepthwiseConv2D *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleDequantize *) final { return loco::DataType::FLOAT32; } - loco::DataType visit(const luci::CircleDiv *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleDiv *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleElu *node) final { - return loco::dtype_get(node->features()); + return luci::dtype_get(node->features()); } loco::DataType visit(const luci::CircleEqual *) final { return loco::DataType::BOOL; } - loco::DataType visit(const luci::CircleExp *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleExp *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleExpandDims *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); + } + + loco::DataType visit(const luci::CircleFakeQuant *node) final + { + return luci::dtype_get(node->inputs()); } loco::DataType visit(const luci::CircleFill *node) final { - return loco::dtype_get(node->value()); + return luci::dtype_get(node->value()); } - loco::DataType visit(const luci::CircleFloor *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleFloor *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleFloorDiv *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleFloorMod *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleFullyConnected *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleGather *node) final { - return loco::dtype_get(node->params()); + return luci::dtype_get(node->params()); } loco::DataType visit(const luci::CircleGatherNd *node) final { - return loco::dtype_get(node->params()); + return luci::dtype_get(node->params()); } loco::DataType visit(const luci::CircleGreater *) final { return loco::DataType::BOOL; } @@ -169,22 +175,22 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT { // Type of If is not used. Just use input 0 assert(node->input_count() > 0); - return loco::dtype_get(node->input(0)); + return luci::dtype_get(node->input(0)); } loco::DataType visit(const luci::CircleL2Normalize *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleL2Pool2D *node) final { - return loco::dtype_get(node->value()); + return luci::dtype_get(node->value()); } loco::DataType visit(const luci::CircleLeakyRelu *node) final { - return loco::dtype_get(node->features()); + return luci::dtype_get(node->features()); } loco::DataType visit(const luci::CircleLess *) final { return loco::DataType::BOOL; } @@ -193,75 +199,75 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CircleLocalResponseNormalization *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleLog *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleLog *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleLogicalAnd *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleLogicalNot *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleLogicalOr *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleLogistic *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleLogSoftmax *node) final { - return loco::dtype_get(node->logits()); + return luci::dtype_get(node->logits()); } loco::DataType visit(const luci::CircleMatrixDiag *node) final { - return loco::dtype_get(node->diagonal()); + return luci::dtype_get(node->diagonal()); } loco::DataType visit(const luci::CircleMatrixSetDiag *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleMaximum *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleMaximum *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleMaxPool2D *node) final { - return loco::dtype_get(node->value()); + return luci::dtype_get(node->value()); } loco::DataType visit(const luci::CircleMean *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleMinimum *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleMinimum *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleMirrorPad *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleNeg *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleNeg *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleNonMaxSuppressionV4 *node) final { - return loco::dtype_get(node->boxes()); + return luci::dtype_get(node->boxes()); } loco::DataType visit(const luci::CircleNonMaxSuppressionV5 *node) final { - return loco::dtype_get(node->boxes()); + return luci::dtype_get(node->boxes()); } loco::DataType visit(const luci::CircleNotEqual *) final { return loco::DataType::BOOL; } @@ -271,25 +277,25 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT // Only support CirclePack with one or more inputs assert(node->values_count() > 0); - auto first_value_type = loco::dtype_get(node->values(0)); + auto first_value_type = luci::dtype_get(node->values(0)); for (uint32_t i = 1; i < node->values_count(); ++i) - assert(first_value_type == loco::dtype_get(node->values(i))); + assert(first_value_type == luci::dtype_get(node->values(i))); return first_value_type; } - loco::DataType visit(const luci::CirclePad *node) final { return loco::dtype_get(node->input()); } + loco::DataType visit(const luci::CirclePad *node) final { return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CirclePadV2 *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CirclePow *node) final { // TODO make sure types cannot differ - auto x_type = loco::dtype_get(node->x()); - auto y_type = loco::dtype_get(node->y()); + auto x_type = luci::dtype_get(node->x()); + auto y_type = luci::dtype_get(node->y()); if (x_type != y_type) INTERNAL_EXN("Different datatype for x and y are not supported"); @@ -299,8 +305,8 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CirclePRelu *node) final { - auto input_type = loco::dtype_get(node->input()); - auto alpha_type = loco::dtype_get(node->alpha()); + auto input_type = luci::dtype_get(node->input()); + auto alpha_type = luci::dtype_get(node->alpha()); if (input_type != alpha_type) INTERNAL_EXN("Different datatype for input and alpha are not supported"); @@ -310,201 +316,201 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CircleRange *node) final { - return loco::dtype_get(node->start()); + return luci::dtype_get(node->start()); } loco::DataType visit(const luci::CircleRank *) final { return loco::DataType::S32; } - loco::DataType visit(const luci::CircleMul *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleMul *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleOneHot *node) final { - return loco::dtype_get(node->on_value()); + return luci::dtype_get(node->on_value()); } loco::DataType visit(const luci::CircleReduceAny *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleReduceMax *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleReduceMin *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleReduceProd *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleRelu *node) final { - return loco::dtype_get(node->features()); + return luci::dtype_get(node->features()); } loco::DataType visit(const luci::CircleRelu6 *node) final { - return loco::dtype_get(node->features()); + return luci::dtype_get(node->features()); } loco::DataType visit(const luci::CircleReluN1To1 *node) final { - return loco::dtype_get(node->features()); + return luci::dtype_get(node->features()); } loco::DataType visit(const luci::CircleReshape *node) final { - return loco::dtype_get(node->tensor()); + return luci::dtype_get(node->tensor()); } loco::DataType visit(const luci::CircleResizeBilinear *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleResizeNearestNeighbor *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleReverseSequence *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleReverseV2 *node) final { - return loco::dtype_get(node->tensor()); + return luci::dtype_get(node->tensor()); } - loco::DataType visit(const luci::CircleRound *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleRound *node) final { return luci::dtype_get(node->x()); } - loco::DataType visit(const luci::CircleRsqrt *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleRsqrt *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleScatterNd *node) final { - return loco::dtype_get(node->updates()); + return luci::dtype_get(node->updates()); } loco::DataType visit(const luci::CircleSegmentSum *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleSelect *node) final { - assert(loco::dtype_get(node->t()) == loco::dtype_get(node->e())); - return loco::dtype_get(node->t()); + assert(luci::dtype_get(node->t()) == luci::dtype_get(node->e())); + return luci::dtype_get(node->t()); } loco::DataType visit(const luci::CircleSelectV2 *node) final { - assert(loco::dtype_get(node->t()) == loco::dtype_get(node->e())); - return loco::dtype_get(node->t()); + assert(luci::dtype_get(node->t()) == luci::dtype_get(node->e())); + return luci::dtype_get(node->t()); } loco::DataType visit(const luci::CircleShape *node) final { return node->out_type(); } - loco::DataType visit(const luci::CircleSin *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleSin *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleSlice *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleSoftmax *node) final { - return loco::dtype_get(node->logits()); + return luci::dtype_get(node->logits()); } loco::DataType visit(const luci::CircleSpaceToBatchND *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleSpaceToDepth *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleSparseToDense *node) final { - return loco::dtype_get(node->values()); + return luci::dtype_get(node->values()); } loco::DataType visit(const luci::CircleSplit *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleSplitV *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleSqrt *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleSqrt *node) final { return luci::dtype_get(node->x()); } - loco::DataType visit(const luci::CircleSquare *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleSquare *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleSquaredDifference *node) final { - return loco::dtype_get(node->x()); + return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleSqueeze *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleStridedSlice *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleSub *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleSub *node) final { return luci::dtype_get(node->x()); } - loco::DataType visit(const luci::CircleSum *node) final { return loco::dtype_get(node->input()); } + loco::DataType visit(const luci::CircleSum *node) final { return luci::dtype_get(node->input()); } - loco::DataType visit(const luci::CircleTanh *node) final { return loco::dtype_get(node->x()); } + loco::DataType visit(const luci::CircleTanh *node) final { return luci::dtype_get(node->x()); } loco::DataType visit(const luci::CircleTile *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleTopKV2 *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleTranspose *node) final { - return loco::dtype_get(node->a()); + return luci::dtype_get(node->a()); } loco::DataType visit(const luci::CircleTransposeConv *node) final { - return loco::dtype_get(node->outBackprop()); + return luci::dtype_get(node->outBackprop()); } loco::DataType visit(const luci::CircleUnidirectionalSequenceLSTM *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleUnique *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleUnpack *node) final { - return loco::dtype_get(node->value()); + return luci::dtype_get(node->value()); } loco::DataType visit(const luci::CircleWhere *) final { return loco::DataType::S64; } @@ -513,12 +519,12 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT { // Type of While is not used. Just use input 0 assert(node->input_count() > 0); - return loco::dtype_get(node->input(0)); + return luci::dtype_get(node->input(0)); } loco::DataType visit(const luci::CircleZerosLike *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } // Circle Only @@ -531,7 +537,7 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CircleInstanceNorm *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } // Virtual @@ -548,7 +554,7 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT { // We don't care for the type if from() is CircleOutputDummy or CircleOutputExclude // from() type should match that of CircleOutput - assert(output_dtype == loco::dtype_get(node->from())); + assert(output_dtype == luci::dtype_get(node->from())); } return output_dtype; } @@ -559,46 +565,6 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CircleCustomOut *node) final { return node->dtype(); } - loco::DataType visit(const luci::CircleIfOut *node) final - { - /** - * @note IF operator type and shape are that of the "then" and "else" - * Graph Outputs. - */ - auto circle_if = dynamic_cast<const luci::CircleIf *>(node->input()); - if (circle_if == nullptr) - { - INTERNAL_EXN("CircleIf IR is not configured correctly"); - } - - auto index = node->index(); - auto then_graph = circle_if->then_graph(); - auto else_graph = circle_if->else_graph(); - assert(then_graph != nullptr); - assert(else_graph != nullptr); - - // shape and type are assumed to be same - // these are checked at post_import_graph() in Import - auto then_outputs = loco::output_nodes(then_graph); - auto else_outputs = loco::output_nodes(else_graph); - assert(then_outputs.size() == else_outputs.size()); - assert(index < static_cast<int32_t>(then_outputs.size())); - - auto then_out = loco::must_cast<luci::CircleOutput *>(then_outputs.at(index)); - auto else_out = loco::must_cast<luci::CircleOutput *>(else_outputs.at(index)); - - auto then_graph_outputs = then_graph->outputs(); // loco::GraphOutput items - auto else_graph_outputs = else_graph->outputs(); - assert(then_graph_outputs->size() == else_graph_outputs->size()); - - auto then_graph_output = then_graph_outputs->at(then_out->index()); - auto else_graph_output = else_graph_outputs->at(else_out->index()); - (void)else_graph_output; // make compiler happy for unused variable warnings - assert(then_graph_output->dtype() == else_graph_output->dtype()); - - return then_graph_output->dtype(); - } - loco::DataType visit(const luci::CircleNonMaxSuppressionV4Out *node) final { (void)node; @@ -619,19 +585,19 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CircleSplitOut *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleSplitVOut *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleTopKV2Out *node) final { // First output is same as input if (node->index() == 0) - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); // Second outout is always S32 assert(node->index() == 1); return loco::DataType::S32; @@ -641,7 +607,7 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT { if (node->index() == 0) { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } assert(node->index() == 1); auto unique = loco::must_cast<luci::CircleUnique *>(node->input()); @@ -650,7 +616,7 @@ struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataT loco::DataType visit(const luci::CircleUnpackOut *node) final { - return loco::dtype_get(node->input()); + return luci::dtype_get(node->input()); } loco::DataType visit(const luci::CircleWhileOut *node) final diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp deleted file mode 100644 index 711a489af..000000000 --- a/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2020 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 "TestGraph.h" -#include <luci/Service/CircleTypeInferenceRule.h> - -#include <luci/IR/CircleNodes.h> -#include <luci/IR/CircleDialect.h> - -#include <loco.h> -#include <loco/IR/CanonicalDialect.h> -#include <loco/Service/TypeInference.h> - -#include <gtest/gtest.h> - -#include <memory> - -TEST(CircleTypeInferenceRuleTest, minimal_with_CircleRelu) -{ - // Create a simple network - luci::test::TestGraph graph; - auto relu_node = graph.append<luci::CircleRelu>(graph.input_node); - graph.complete(relu_node); - - // set dtype for nodes; like setting them in import - graph.input_node->dtype(loco::DataType::S32); - relu_node->dtype(loco::DataType::S32); - graph.output_node->dtype(loco::DataType::S32); - - luci::test::graph_input_dtype(graph.input_node); - luci::test::graph_output_dtype(graph.output_node); - - // pre-check - ASSERT_FALSE(loco::dtype_known(relu_node)); - - // type inference - luci::CircleTypeInferenceRule circle_rule; - loco::CanonicalTypeInferenceRule canon_rule; - loco::MultiDialectTypeInferenceRule rules; - - rules.bind(loco::CanonicalDialect::get(), &canon_rule); - rules.bind(luci::CircleDialect::get(), &circle_rule); - - loco::apply(&rules).to(graph.g.get()); - - // Verify - ASSERT_TRUE(loco::dtype_known(relu_node)); - auto type = loco::dtype_get(relu_node); - ASSERT_EQ(loco::DataType::S32, type); -} diff --git a/compiler/luci/service/src/Nodes/CircleAbs.cpp b/compiler/luci/service/src/Nodes/CircleAbs.cpp new file mode 100644 index 000000000..132760957 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleAbs.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleAbs *) +{ + return _graph->nodes()->create<luci::CircleAbs>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleAbs.test.cpp b/compiler/luci/service/src/Nodes/CircleAbs.test.cpp new file mode 100644 index 000000000..885b395b8 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleAbs.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Abs) +{ + auto g = loco::make_graph(); + auto node_abs = g->nodes()->create<luci::CircleAbs>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_abs, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_abs = dynamic_cast<luci::CircleAbs *>(cloned); + ASSERT_NE(nullptr, cloned_abs); +} diff --git a/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h b/compiler/luci/service/src/Nodes/CircleAdd.cpp index e21ab4cce..08634320e 100644 --- a/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h +++ b/compiler/luci/service/src/Nodes/CircleAdd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,29 +14,20 @@ * limitations under the License. */ -#ifndef __LUCI_SHAPE_INFERENCE_PASS_H__ -#define __LUCI_SHAPE_INFERENCE_PASS_H__ - -#include <loco.h> - -#include <luci/ModulePass.h> +#include "CircleCloneNode.h" namespace luci { -/** - * @brief Pass to infer shape of nodes - */ -class ShapeInferencePass : public luci::Pass +luci::CircleNode *CloneNode::visit(const luci::CircleAdd *node) { -public: - virtual const char *name(void) const { return "luci::ShapeInferencePass"; } + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; -public: - bool run(luci::Module *m); - bool run(loco::Graph *graph); -}; + auto *cloned = _graph->nodes()->create<luci::CircleAdd>(); + if (cloned != nullptr) + cloned->fusedActivationFunction(node->fusedActivationFunction()); + return cloned; +} } // namespace luci - -#endif //__LUCI_SHAPE_INFERENCE_PASS_H__ diff --git a/compiler/luci/service/src/Nodes/CircleAdd.test.cpp b/compiler/luci/service/src/Nodes/CircleAdd.test.cpp new file mode 100644 index 000000000..41a818b0a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleAdd.test.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +/** + * @note Function to test: Shape inference of two different input shapes + * + * Rank expansion to higher input side + * x(2,1,5) + y(3,5) --> x(2,1,5) + y(1,3,5) + * Do output shape inference like numpy + * x(2,1,5) + y(1,3,5) --> output(2,3,5) + * For each axis, dim value should be same OR one of them should be 1 + */ +TEST(ShapeRuleTest, different_input_shapes_add) +{ + luci::CircleInput input1; + luci::CircleInput input2; + luci::CircleAdd add; + + input1.shape({2, 1, 5}); + input1.shape_status(luci::ShapeStatus::VALID); + input2.shape({3, 5}); + input2.shape_status(luci::ShapeStatus::VALID); + + add.x(&input1); + add.y(&input2); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&add, shape)); + ASSERT_EQ(3, shape.rank()); + ASSERT_EQ(2, shape.dim(0).value()); + ASSERT_EQ(3, shape.dim(1).value()); + ASSERT_EQ(5, shape.dim(2).value()); +} + +TEST(CloneNodeTest, clone_Add) +{ + auto g = loco::make_graph(); + auto node_add = g->nodes()->create<luci::CircleAdd>(); + node_add->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_add, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_add = dynamic_cast<luci::CircleAdd *>(cloned); + ASSERT_NE(nullptr, cloned_add); + ASSERT_EQ(node_add->fusedActivationFunction(), cloned_add->fusedActivationFunction()); +} + +TEST(CloneNodeTest, clone_Add_NEG) +{ + auto g = loco::make_graph(); + auto node_add = g->nodes()->create<luci::CircleAdd>(); + node_add->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_add, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleAddN.cpp b/compiler/luci/service/src/Nodes/CircleAddN.cpp new file mode 100644 index 000000000..e536e54bb --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleAddN.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleAddN *node) +{ + auto arity = node->arity(); + return _graph->nodes()->create<luci::CircleAddN>(arity); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleAddN.test.cpp b/compiler/luci/service/src/Nodes/CircleAddN.test.cpp new file mode 100644 index 000000000..5d5b82247 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleAddN.test.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_AddN) +{ + auto g = loco::make_graph(); + auto node_addn = g->nodes()->create<luci::CircleAddN>(3); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_addn, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_addn = dynamic_cast<luci::CircleAddN *>(cloned); + ASSERT_NE(nullptr, cloned_addn); + ASSERT_EQ(node_addn->arity(), cloned_addn->arity()); +} diff --git a/compiler/luci/service/src/Nodes/CircleArgMax.cpp b/compiler/luci/service/src/Nodes/CircleArgMax.cpp new file mode 100644 index 000000000..1b3bafa86 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleArgMax.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleArgMax *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleArgMax>(); + if (cloned != nullptr) + cloned->output_type(node->output_type()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleArgMax.test.cpp b/compiler/luci/service/src/Nodes/CircleArgMax.test.cpp new file mode 100644 index 000000000..bb7588403 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleArgMax.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ArgMax) +{ + auto g = loco::make_graph(); + auto node_argmax = g->nodes()->create<luci::CircleArgMax>(); + node_argmax->output_type(loco::DataType::FLOAT32); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_argmax, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_argmax = dynamic_cast<luci::CircleArgMax *>(cloned); + ASSERT_NE(nullptr, cloned_argmax); + ASSERT_EQ(node_argmax->output_type(), cloned_argmax->output_type()); +} diff --git a/compiler/luci/service/src/Nodes/CircleArgMin.cpp b/compiler/luci/service/src/Nodes/CircleArgMin.cpp new file mode 100644 index 000000000..fa54f7b76 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleArgMin.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleArgMin *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleArgMin>(); + if (cloned != nullptr) + cloned->output_type(node->output_type()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleArgMin.test.cpp b/compiler/luci/service/src/Nodes/CircleArgMin.test.cpp new file mode 100644 index 000000000..ca57946f9 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleArgMin.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ArgMin) +{ + auto g = loco::make_graph(); + auto node_argmin = g->nodes()->create<luci::CircleArgMin>(); + node_argmin->output_type(loco::DataType::FLOAT32); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_argmin, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_argmin = dynamic_cast<luci::CircleArgMin *>(cloned); + ASSERT_NE(nullptr, cloned_argmin); + ASSERT_EQ(node_argmin->output_type(), cloned_argmin->output_type()); +} diff --git a/compiler/luci/service/src/Nodes/CircleAveragePool2D.cpp b/compiler/luci/service/src/Nodes/CircleAveragePool2D.cpp new file mode 100644 index 000000000..4d2791833 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleAveragePool2D.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleAveragePool2D *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + if (node->padding() == luci::Padding::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleAveragePool2D>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->padding(node->padding()); + cloned->filter()->h(node->filter()->h()); + cloned->filter()->w(node->filter()->w()); + cloned->stride()->h(node->stride()->h()); + cloned->stride()->w(node->stride()->w()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleAveragePool2D.test.cpp b/compiler/luci/service/src/Nodes/CircleAveragePool2D.test.cpp new file mode 100644 index 000000000..d048d1426 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleAveragePool2D.test.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, simple_valid_pad_avgpool2d) +{ + luci::CircleInput input; + luci::CircleAveragePool2D avgpool_2d; + + input.shape({1, 4, 3, 1}); + input.shape_status(luci::ShapeStatus::VALID); + + avgpool_2d.value(&input); + avgpool_2d.filter()->h(2); + avgpool_2d.filter()->w(2); + avgpool_2d.stride()->h(2); + avgpool_2d.stride()->w(2); + avgpool_2d.fusedActivationFunction(luci::FusedActFunc::NONE); + avgpool_2d.padding(luci::Padding::VALID); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&avgpool_2d, shape)); + ASSERT_EQ(4, shape.rank()); + ASSERT_EQ(1, shape.dim(0).value()); + ASSERT_EQ(2, shape.dim(1).value()); + ASSERT_EQ(1, shape.dim(2).value()); + ASSERT_EQ(1, shape.dim(3).value()); +} + +TEST(ShapeRuleTest, simple_same_pad_avgpool2d) +{ + luci::CircleInput input; + luci::CircleAveragePool2D avgpool_2d; + + input.shape({1, 4, 3, 1}); + input.shape_status(luci::ShapeStatus::VALID); + + avgpool_2d.value(&input); + avgpool_2d.filter()->h(2); + avgpool_2d.filter()->w(2); + avgpool_2d.stride()->h(2); + avgpool_2d.stride()->w(2); + avgpool_2d.fusedActivationFunction(luci::FusedActFunc::NONE); + avgpool_2d.padding(luci::Padding::SAME); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&avgpool_2d, shape)); + ASSERT_EQ(4, shape.rank()); + ASSERT_EQ(1, shape.dim(0).value()); + ASSERT_EQ(2, shape.dim(1).value()); + ASSERT_EQ(2, shape.dim(2).value()); + ASSERT_EQ(1, shape.dim(3).value()); +} + +TEST(CloneNodeTest, clone_AveragePool2D) +{ + auto g = loco::make_graph(); + auto node_avgpool2d = g->nodes()->create<luci::CircleAveragePool2D>(); + node_avgpool2d->fusedActivationFunction(luci::FusedActFunc::RELU); + node_avgpool2d->padding(luci::Padding::SAME); + node_avgpool2d->filter()->h(1); + node_avgpool2d->filter()->w(2); + node_avgpool2d->stride()->h(3); + node_avgpool2d->stride()->w(4); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_avgpool2d, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_avgpool2d = dynamic_cast<luci::CircleAveragePool2D *>(cloned); + ASSERT_NE(nullptr, cloned_avgpool2d); + ASSERT_EQ(node_avgpool2d->fusedActivationFunction(), cloned_avgpool2d->fusedActivationFunction()); + ASSERT_EQ(node_avgpool2d->padding(), cloned_avgpool2d->padding()); + ASSERT_EQ(node_avgpool2d->filter()->h(), cloned_avgpool2d->filter()->h()); + ASSERT_EQ(node_avgpool2d->filter()->w(), cloned_avgpool2d->filter()->w()); + ASSERT_EQ(node_avgpool2d->stride()->h(), cloned_avgpool2d->stride()->h()); + ASSERT_EQ(node_avgpool2d->stride()->w(), cloned_avgpool2d->stride()->w()); +} + +TEST(CloneNodeTest, clone_AveragePool2D_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_avgpool2d = g->nodes()->create<luci::CircleAveragePool2D>(); + node_avgpool2d->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + node_avgpool2d->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_avgpool2d, gc.get()); + ASSERT_EQ(nullptr, cloned); +} + +TEST(CloneNodeTest, clone_AveragePool2D_padding_NEG) +{ + auto g = loco::make_graph(); + auto node_avgpool2d = g->nodes()->create<luci::CircleAveragePool2D>(); + node_avgpool2d->fusedActivationFunction(luci::FusedActFunc::RELU); + node_avgpool2d->padding(luci::Padding::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_avgpool2d, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/pass/include/luci/Pass/ShapeSignatureInferencePass.h b/compiler/luci/service/src/Nodes/CircleBCQFullyConnected.cpp index 2c6ffcf4e..3edc06ab8 100644 --- a/compiler/luci/pass/include/luci/Pass/ShapeSignatureInferencePass.h +++ b/compiler/luci/service/src/Nodes/CircleBCQFullyConnected.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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. @@ -14,29 +14,23 @@ * limitations under the License. */ -#ifndef __LUCI_SHAPE_SIGNATURE_INFERENCE_PASS_H__ -#define __LUCI_SHAPE_SIGNATURE_INFERENCE_PASS_H__ - -#include <loco.h> - -#include <luci/ModulePass.h> +#include "CircleCloneNode.h" namespace luci { -/** - * @brief Pass to infer shape_signature of nodes - */ -class ShapeSignatureInferencePass : public luci::Pass +luci::CircleNode *CloneNode::visit(const luci::CircleBCQFullyConnected *node) { -public: - virtual const char *name(void) const { return "luci::ShapeSignatureInferencePass"; } - -public: - bool run(luci::Module *m); - bool run(loco::Graph *graph); -}; + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleBCQFullyConnected>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->weights_hidden_size(node->weights_hidden_size()); + } + return cloned; +} } // namespace luci - -#endif //__LUCI_SHAPE_SIGNATURE_INFERENCE_PASS_H__ diff --git a/compiler/luci/service/src/Nodes/CircleBCQFullyConnected.test.cpp b/compiler/luci/service/src/Nodes/CircleBCQFullyConnected.test.cpp new file mode 100644 index 000000000..90c192e07 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleBCQFullyConnected.test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_BCQFullyConnected) +{ + auto g = loco::make_graph(); + auto node_fc = g->nodes()->create<luci::CircleBCQFullyConnected>(); + node_fc->fusedActivationFunction(luci::FusedActFunc::RELU); + node_fc->weights_hidden_size(3); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fc, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_fc = dynamic_cast<luci::CircleBCQFullyConnected *>(cloned); + ASSERT_NE(nullptr, cloned_fc); + ASSERT_EQ(node_fc->fusedActivationFunction(), cloned_fc->fusedActivationFunction()); + ASSERT_EQ(node_fc->weights_hidden_size(), cloned_fc->weights_hidden_size()); +} + +TEST(CloneNodeTest, clone_BCQFullyConnected_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_fc = g->nodes()->create<luci::CircleBCQFullyConnected>(); + node_fc->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fc, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleBCQGather.cpp b/compiler/luci/service/src/Nodes/CircleBCQGather.cpp new file mode 100644 index 000000000..35b6be744 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleBCQGather.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleBCQGather *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleBCQGather>(); + if (cloned != nullptr) + { + cloned->axis(node->axis()); + cloned->input_hidden_size(node->input_hidden_size()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleBCQGather.test.cpp b/compiler/luci/service/src/Nodes/CircleBCQGather.test.cpp new file mode 100644 index 000000000..a3f9e8850 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleBCQGather.test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_BCQGather) +{ + auto g = loco::make_graph(); + auto node_gat = g->nodes()->create<luci::CircleBCQGather>(); + node_gat->axis(3); + node_gat->input_hidden_size(5); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_gat, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_gat = dynamic_cast<luci::CircleBCQGather *>(cloned); + ASSERT_NE(nullptr, cloned_gat); + ASSERT_EQ(node_gat->axis(), cloned_gat->axis()); + ASSERT_EQ(node_gat->input_hidden_size(), cloned_gat->input_hidden_size()); +} diff --git a/compiler/luci/service/src/Nodes/CircleBatchMatMul.cpp b/compiler/luci/service/src/Nodes/CircleBatchMatMul.cpp new file mode 100644 index 000000000..c7a8bbd52 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleBatchMatMul.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleBatchMatMul *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleBatchMatMul>(); + if (cloned != nullptr) + { + cloned->adj_x(node->adj_x()); + cloned->adj_y(node->adj_y()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleBatchMatMul.test.cpp b/compiler/luci/service/src/Nodes/CircleBatchMatMul.test.cpp new file mode 100644 index 000000000..e013feae8 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleBatchMatMul.test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_BatchMatMul) +{ + auto g = loco::make_graph(); + auto node_bmm = g->nodes()->create<luci::CircleBatchMatMul>(); + node_bmm->adj_x(true); + node_bmm->adj_y(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_bmm, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_bmm = dynamic_cast<luci::CircleBatchMatMul *>(cloned); + ASSERT_NE(nullptr, cloned_bmm); + ASSERT_EQ(node_bmm->adj_x(), cloned_bmm->adj_x()); + ASSERT_EQ(node_bmm->adj_y(), cloned_bmm->adj_y()); +} diff --git a/compiler/luci/service/src/Nodes/CircleBatchToSpaceND.cpp b/compiler/luci/service/src/Nodes/CircleBatchToSpaceND.cpp new file mode 100644 index 000000000..70aa05f72 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleBatchToSpaceND.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleBatchToSpaceND *) +{ + return _graph->nodes()->create<luci::CircleBatchToSpaceND>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleBatchToSpaceND.test.cpp b/compiler/luci/service/src/Nodes/CircleBatchToSpaceND.test.cpp new file mode 100644 index 000000000..a45039fc7 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleBatchToSpaceND.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_BatchToSpaceND) +{ + auto g = loco::make_graph(); + auto node_b2s = g->nodes()->create<luci::CircleBatchToSpaceND>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_b2s, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_b2s = dynamic_cast<luci::CircleBatchToSpaceND *>(cloned); + ASSERT_NE(nullptr, cloned_b2s); +} diff --git a/compiler/luci/service/src/Nodes/CircleCast.cpp b/compiler/luci/service/src/Nodes/CircleCast.cpp new file mode 100644 index 000000000..75f15f9de --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCast.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleCast *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleCast>(); + if (cloned != nullptr) + { + cloned->in_data_type(node->in_data_type()); + cloned->out_data_type(node->out_data_type()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleCast.test.cpp b/compiler/luci/service/src/Nodes/CircleCast.test.cpp new file mode 100644 index 000000000..1c4bacb73 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCast.test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Cast) +{ + auto g = loco::make_graph(); + auto node_cast = g->nodes()->create<luci::CircleCast>(); + node_cast->in_data_type(loco::DataType::U16); + node_cast->out_data_type(loco::DataType::S32); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_cast, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_cast = dynamic_cast<luci::CircleCast *>(cloned); + ASSERT_NE(nullptr, cloned_cast); + ASSERT_EQ(node_cast->in_data_type(), cloned_cast->in_data_type()); + ASSERT_EQ(node_cast->out_data_type(), cloned_cast->out_data_type()); +} diff --git a/compiler/luci/service/src/Nodes/CircleCeil.cpp b/compiler/luci/service/src/Nodes/CircleCeil.cpp new file mode 100644 index 000000000..92d039a7d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCeil.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleCeil *) +{ + return _graph->nodes()->create<luci::CircleCeil>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleCeil.test.cpp b/compiler/luci/service/src/Nodes/CircleCeil.test.cpp new file mode 100644 index 000000000..b182127d9 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCeil.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Ceil) +{ + auto g = loco::make_graph(); + auto node_ceil = g->nodes()->create<luci::CircleCeil>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_ceil, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_ceil = dynamic_cast<luci::CircleCeil *>(cloned); + ASSERT_NE(nullptr, cloned_ceil); +} diff --git a/compiler/luci/service/src/Nodes/CircleConcatenation.cpp b/compiler/luci/service/src/Nodes/CircleConcatenation.cpp new file mode 100644 index 000000000..75d6a53e6 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleConcatenation.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleConcatenation *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleConcatenation>(node->numValues()); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->axis(node->axis()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleConcatenation.test.cpp b/compiler/luci/service/src/Nodes/CircleConcatenation.test.cpp new file mode 100644 index 000000000..270068cf0 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleConcatenation.test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Concatenation) +{ + auto g = loco::make_graph(); + auto node_concat = g->nodes()->create<luci::CircleConcatenation>(3); + node_concat->fusedActivationFunction(luci::FusedActFunc::RELU); + node_concat->axis(7); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_concat, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_concat = dynamic_cast<luci::CircleConcatenation *>(cloned); + ASSERT_NE(nullptr, cloned_concat); + ASSERT_EQ(node_concat->numValues(), cloned_concat->numValues()); + ASSERT_EQ(node_concat->fusedActivationFunction(), cloned_concat->fusedActivationFunction()); + ASSERT_EQ(node_concat->axis(), cloned_concat->axis()); +} + +TEST(CloneNodeTest, clone_Concatenation_NEG) +{ + auto g = loco::make_graph(); + auto node_concat = g->nodes()->create<luci::CircleConcatenation>(3); + node_concat->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_concat, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleConst.cpp b/compiler/luci/service/src/Nodes/CircleConst.cpp new file mode 100644 index 000000000..0306ef4eb --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleConst.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +#include "luci/Service/CircleNodeClone.h" + +#include <luci/IR/Nodes/CircleConst.h> + +#include <loco.h> +#include <loco/IR/Graph.h> + +#include <oops/UserExn.h> + +#include <cassert> + +namespace +{ + +template <loco::DataType T> +void copy_values(const luci::CircleConst *node, luci::CircleConst *cloned) +{ + assert(T == node->dtype()); + assert(T == cloned->dtype()); + + const auto size = node->size<T>(); + cloned->size<T>(size); + for (uint32_t i = 0; i < size; i++) + cloned->at<T>(i) = node->at<T>(i); +} + +luci::CircleConst *clone_circleconst(const luci::CircleConst *node, loco::Graph *graph) +{ + auto cloned = graph->nodes()->create<luci::CircleConst>(); + + if (cloned != nullptr) + { + // dtype/shape + cloned->dtype(node->dtype()); + cloned->rank(node->rank()); + + // values + switch (node->dtype()) + { + case loco::DataType::FLOAT32: + copy_values<loco::DataType::FLOAT32>(node, cloned); + break; + + case loco::DataType::U8: + copy_values<loco::DataType::U8>(node, cloned); + break; + + case loco::DataType::S8: + copy_values<loco::DataType::S8>(node, cloned); + break; + + case loco::DataType::S16: + copy_values<loco::DataType::S16>(node, cloned); + break; + + case loco::DataType::S32: + copy_values<loco::DataType::S32>(node, cloned); + break; + + case loco::DataType::S64: + copy_values<loco::DataType::S64>(node, cloned); + break; + + case loco::DataType::BOOL: + copy_values<loco::DataType::BOOL>(node, cloned); + break; + + default: + throw oops::UserExn("Unsupported tensor dtype"); + } + } + + return cloned; +} + +} // namespace + +namespace luci +{ + +luci::CircleConst *clone(luci::CircleConst *node) +{ + auto *cloned = clone_circleconst(node, node->graph()); + + copy_common_attributes(node, cloned); + + return cloned; +} + +} // namespace luci + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleConst *node) +{ + return clone_circleconst(node, _graph); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleConst.test.cpp b/compiler/luci/service/src/Nodes/CircleConst.test.cpp new file mode 100644 index 000000000..5d94798f4 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleConst.test.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021 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/Nodes/CircleConst.h" +#include "luci/Service/CircleNodeClone.h" + +#include <loco.h> +#include <loco/IR/Graph.h> + +#include <gtest/gtest.h> + +namespace +{ + +luci::CircleConst *new_const_s32(loco::Graph *g) +{ + // prepare source CircleConst + auto circle_const = g->nodes()->create<luci::CircleConst>(); + + const auto size = 2; + + circle_const->dtype(loco::DataType::S32); + circle_const->rank(1); + circle_const->dim(0).set(size); + circle_const->shape_status(luci::ShapeStatus::VALID); + + circle_const->size<loco::DataType::S32>(size); + for (uint32_t i = 0; i < size; i++) + circle_const->at<loco::DataType::S32>(i) = i; + + // quantparam + auto quantparam = std::make_unique<luci::CircleQuantParam>(); + quantparam->scale = {1.0}; + quantparam->zerop = {0}; + quantparam->min = {-127.0}; + quantparam->max = {127.0}; + quantparam->quantized_dimension = 1; + circle_const->quantparam(std::move(quantparam)); + + // sparsityparam + auto sparam = std::make_unique<luci::SparsityParam>(); + sparam->traversal_order = {1}; + sparam->block_map = {1}; + sparam->dim_metadata = {}; + circle_const->sparsityparam(std::move(sparam)); + + return circle_const; +} + +template <loco::DataType DT> luci::CircleConst *new_empty_const(loco::Graph *g) +{ + auto circle_const = g->nodes()->create<luci::CircleConst>(); + + const auto size = 0; + + circle_const->dtype(DT); + circle_const->rank(1); + circle_const->dim(0).set(size); + circle_const->shape_status(luci::ShapeStatus::VALID); + circle_const->size<DT>(size); + + return circle_const; +} + +} // namespace + +TEST(CircleConstTest, clone) +{ + auto g = loco::make_graph(); + + // prepare source CircleConst + auto circle_const = new_const_s32(g.get()); + + // make a clone + auto const_cloned = luci::clone(circle_const); + + // check attributes + ASSERT_EQ(loco::DataType::S32, const_cloned->dtype()); + ASSERT_EQ(1, const_cloned->rank()); + ASSERT_EQ(2, const_cloned->dim(0).value()); + ASSERT_EQ(2, const_cloned->size<loco::DataType::S32>()); + ASSERT_EQ(0, const_cloned->at<loco::DataType::S32>(0)); + ASSERT_EQ(1, const_cloned->at<loco::DataType::S32>(1)); + ASSERT_NE(nullptr, const_cloned->quantparam()); + ASSERT_NE(nullptr, const_cloned->sparsityparam()); +} + +TEST(CircleConstTest, clone_U8) +{ + auto g = loco::make_graph(); + + // prepare source CircleConst + auto circle_const = new_empty_const<loco::DataType::U8>(g.get()); + + // make a clone + auto const_cloned = luci::clone(circle_const); + + // check attributes + ASSERT_EQ(loco::DataType::U8, const_cloned->dtype()); +} + +TEST(CircleConstTest, clone_S8) +{ + auto g = loco::make_graph(); + + // prepare source CircleConst + auto circle_const = new_empty_const<loco::DataType::S8>(g.get()); + + // make a clone + auto const_cloned = luci::clone(circle_const); + + // check attributes + ASSERT_EQ(loco::DataType::S8, const_cloned->dtype()); +} + +TEST(CircleConstTest, clone_S64) +{ + auto g = loco::make_graph(); + + // prepare source CircleConst + auto circle_const = new_empty_const<loco::DataType::S64>(g.get()); + + // make a clone + auto const_cloned = luci::clone(circle_const); + + // check attributes + ASSERT_EQ(loco::DataType::S64, const_cloned->dtype()); +} + +TEST(CircleConstTest, clone_BOOL) +{ + auto g = loco::make_graph(); + + // prepare source CircleConst + auto circle_const = new_empty_const<loco::DataType::BOOL>(g.get()); + + // make a clone + auto const_cloned = luci::clone(circle_const); + + // check attributes + ASSERT_EQ(loco::DataType::BOOL, const_cloned->dtype()); +} + +TEST(CloneNodeTest, clone_Const) +{ + auto g = loco::make_graph(); + auto node_const = new_const_s32(g.get()); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_const, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_const = dynamic_cast<luci::CircleConst *>(cloned); + ASSERT_NE(nullptr, cloned_const); + ASSERT_EQ(loco::DataType::S32, cloned_const->dtype()); + ASSERT_EQ(1, cloned_const->rank()); + ASSERT_EQ(2, cloned_const->dim(0).value()); + ASSERT_EQ(2, cloned_const->size<loco::DataType::S32>()); + ASSERT_EQ(0, cloned_const->at<loco::DataType::S32>(0)); + ASSERT_EQ(1, cloned_const->at<loco::DataType::S32>(1)); + ASSERT_NE(nullptr, cloned_const->quantparam()); + ASSERT_NE(nullptr, cloned_const->sparsityparam()); +} diff --git a/compiler/luci/service/src/Nodes/CircleConv2D.cpp b/compiler/luci/service/src/Nodes/CircleConv2D.cpp new file mode 100644 index 000000000..08cd87ef7 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleConv2D.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleConv2D *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + if (node->padding() == luci::Padding::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleConv2D>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->padding(node->padding()); + cloned->stride()->h(node->stride()->h()); + cloned->stride()->w(node->stride()->w()); + cloned->dilation()->h(node->dilation()->h()); + cloned->dilation()->w(node->dilation()->w()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleConv2D.test.cpp b/compiler/luci/service/src/Nodes/CircleConv2D.test.cpp new file mode 100644 index 000000000..c265d6cd1 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleConv2D.test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Conv2D) +{ + auto g = loco::make_graph(); + auto node_conv2d = g->nodes()->create<luci::CircleConv2D>(); + node_conv2d->fusedActivationFunction(luci::FusedActFunc::RELU); + node_conv2d->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_conv2d, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_conv2d = dynamic_cast<luci::CircleConv2D *>(cloned); + ASSERT_NE(nullptr, cloned_conv2d); + ASSERT_EQ(node_conv2d->fusedActivationFunction(), cloned_conv2d->fusedActivationFunction()); + ASSERT_EQ(node_conv2d->padding(), cloned_conv2d->padding()); +} + +TEST(CloneNodeTest, clone_Conv2D_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_conv2d = g->nodes()->create<luci::CircleConv2D>(); + node_conv2d->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + node_conv2d->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_conv2d, gc.get()); + ASSERT_EQ(nullptr, cloned); +} + +TEST(CloneNodeTest, clone_Conv2D_padding_NEG) +{ + auto g = loco::make_graph(); + auto node_conv2d = g->nodes()->create<luci::CircleConv2D>(); + node_conv2d->fusedActivationFunction(luci::FusedActFunc::RELU); + node_conv2d->padding(luci::Padding::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_conv2d, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleCos.cpp b/compiler/luci/service/src/Nodes/CircleCos.cpp new file mode 100644 index 000000000..c46e3741b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCos.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleCos *) +{ + return _graph->nodes()->create<luci::CircleCos>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleCos.test.cpp b/compiler/luci/service/src/Nodes/CircleCos.test.cpp new file mode 100644 index 000000000..a25943b98 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCos.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Cos) +{ + auto g = loco::make_graph(); + auto node_cos = g->nodes()->create<luci::CircleCos>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_cos, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_cos = dynamic_cast<luci::CircleCos *>(cloned); + ASSERT_NE(nullptr, cloned_cos); +} diff --git a/compiler/luci/service/src/Nodes/CircleCustom.cpp b/compiler/luci/service/src/Nodes/CircleCustom.cpp new file mode 100644 index 000000000..a9764c373 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCustom.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleCustom *node) +{ + uint32_t num_in = node->numInputs(); + uint32_t num_out = node->numOutputs(); + auto *cloned = _graph->nodes()->create<luci::CircleCustom>(num_in, num_out); + if (cloned != nullptr) + { + cloned->custom_options(node->custom_options()); + cloned->custom_code(node->custom_code()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleCustom.test.cpp b/compiler/luci/service/src/Nodes/CircleCustom.test.cpp new file mode 100644 index 000000000..6fee68e71 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCustom.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +#include <string> +#include <vector> + +TEST(CloneNodeTest, clone_Custom) +{ + auto g = loco::make_graph(); + auto node_custom = g->nodes()->create<luci::CircleCustom>(2, 3); + std::vector<uint8_t> options({0x55, 0x56, 0x57}); + std::string code = "hello"; + node_custom->custom_options(options); + node_custom->custom_code(code); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_custom, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_custom = dynamic_cast<luci::CircleCustom *>(cloned); + ASSERT_NE(nullptr, cloned_custom); + auto cloned_options = cloned_custom->custom_options(); + ASSERT_EQ(options.size(), cloned_options.size()); + auto size = options.size(); + for (size_t s = 0; s < size; ++s) + ASSERT_EQ(options.at(s), cloned_options.at(s)); + ASSERT_TRUE(node_custom->custom_code() == cloned_custom->custom_code()); +} diff --git a/compiler/luci/service/src/Nodes/CircleCustomOut.cpp b/compiler/luci/service/src/Nodes/CircleCustomOut.cpp new file mode 100644 index 000000000..84577f529 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCustomOut.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleCustomOut *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleCustomOut>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleCustomOut.test.cpp b/compiler/luci/service/src/Nodes/CircleCustomOut.test.cpp new file mode 100644 index 000000000..15121bab6 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleCustomOut.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_CustomOut) +{ + auto g = loco::make_graph(); + auto node_cout = g->nodes()->create<luci::CircleCustomOut>(); + node_cout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_cout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_cout = dynamic_cast<luci::CircleCustomOut *>(cloned); + ASSERT_NE(nullptr, cloned_cout); + ASSERT_EQ(node_cout->index(), cloned_cout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleDepthToSpace.cpp b/compiler/luci/service/src/Nodes/CircleDepthToSpace.cpp new file mode 100644 index 000000000..7e0bc7d74 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDepthToSpace.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleDepthToSpace *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleDepthToSpace>(); + if (cloned != nullptr) + cloned->block_size(node->block_size()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleDepthToSpace.test.cpp b/compiler/luci/service/src/Nodes/CircleDepthToSpace.test.cpp new file mode 100644 index 000000000..192b10b90 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDepthToSpace.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_DepthToSpace) +{ + auto g = loco::make_graph(); + auto node_d2s = g->nodes()->create<luci::CircleDepthToSpace>(); + node_d2s->block_size(32); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_d2s, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_d2s = dynamic_cast<luci::CircleDepthToSpace *>(cloned); + ASSERT_NE(nullptr, cloned_d2s); + ASSERT_EQ(node_d2s->block_size(), cloned_d2s->block_size()); +} diff --git a/compiler/luci/service/src/Nodes/CircleDepthwiseConv2D.cpp b/compiler/luci/service/src/Nodes/CircleDepthwiseConv2D.cpp new file mode 100644 index 000000000..8e0b23d94 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDepthwiseConv2D.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleDepthwiseConv2D *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + if (node->padding() == luci::Padding::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleDepthwiseConv2D>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->padding(node->padding()); + cloned->stride()->h(node->stride()->h()); + cloned->stride()->w(node->stride()->w()); + cloned->depthMultiplier(node->depthMultiplier()); + cloned->dilation()->h(node->dilation()->h()); + cloned->dilation()->w(node->dilation()->w()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleDepthwiseConv2D.test.cpp b/compiler/luci/service/src/Nodes/CircleDepthwiseConv2D.test.cpp new file mode 100644 index 000000000..8657464bc --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDepthwiseConv2D.test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_DepthwiseConv2D) +{ + auto g = loco::make_graph(); + auto node_dwconv2d = g->nodes()->create<luci::CircleDepthwiseConv2D>(); + node_dwconv2d->fusedActivationFunction(luci::FusedActFunc::RELU); + node_dwconv2d->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_dwconv2d, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_dwconv2d = dynamic_cast<luci::CircleDepthwiseConv2D *>(cloned); + ASSERT_NE(nullptr, cloned_dwconv2d); + ASSERT_EQ(node_dwconv2d->fusedActivationFunction(), cloned_dwconv2d->fusedActivationFunction()); + ASSERT_EQ(node_dwconv2d->padding(), cloned_dwconv2d->padding()); +} + +TEST(CloneNodeTest, clone_DepthwiseConv2D_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_dwconv2d = g->nodes()->create<luci::CircleDepthwiseConv2D>(); + node_dwconv2d->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + node_dwconv2d->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_dwconv2d, gc.get()); + ASSERT_EQ(nullptr, cloned); +} + +TEST(CloneNodeTest, clone_DepthwiseConv2D_padding_NEG) +{ + auto g = loco::make_graph(); + auto node_dwconv2d = g->nodes()->create<luci::CircleDepthwiseConv2D>(); + node_dwconv2d->fusedActivationFunction(luci::FusedActFunc::RELU); + node_dwconv2d->padding(luci::Padding::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_dwconv2d, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleDequantize.cpp b/compiler/luci/service/src/Nodes/CircleDequantize.cpp new file mode 100644 index 000000000..79983e4d3 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDequantize.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleDequantize *) +{ + return _graph->nodes()->create<luci::CircleDequantize>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleDequantize.test.cpp b/compiler/luci/service/src/Nodes/CircleDequantize.test.cpp new file mode 100644 index 000000000..e1c563acf --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDequantize.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Dequantize) +{ + auto g = loco::make_graph(); + auto node_dq = g->nodes()->create<luci::CircleDequantize>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_dq, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_dq = dynamic_cast<luci::CircleDequantize *>(cloned); + ASSERT_NE(nullptr, cloned_dq); +} diff --git a/compiler/luci/service/src/Nodes/CircleDiv.cpp b/compiler/luci/service/src/Nodes/CircleDiv.cpp new file mode 100644 index 000000000..7c48d8b76 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDiv.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleDiv *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleDiv>(); + if (cloned != nullptr) + cloned->fusedActivationFunction(node->fusedActivationFunction()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleDiv.test.cpp b/compiler/luci/service/src/Nodes/CircleDiv.test.cpp new file mode 100644 index 000000000..5182ac908 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleDiv.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Div) +{ + auto g = loco::make_graph(); + auto node_div = g->nodes()->create<luci::CircleDiv>(); + node_div->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_div, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_div = dynamic_cast<luci::CircleDiv *>(cloned); + ASSERT_NE(nullptr, cloned_div); + ASSERT_EQ(node_div->fusedActivationFunction(), cloned_div->fusedActivationFunction()); +} + +TEST(CloneNodeTest, clone_Div_NEG) +{ + auto g = loco::make_graph(); + auto node_div = g->nodes()->create<luci::CircleDiv>(); + node_div->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_div, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleElu.cpp b/compiler/luci/service/src/Nodes/CircleElu.cpp new file mode 100644 index 000000000..e2df30285 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleElu.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleElu *) +{ + return _graph->nodes()->create<luci::CircleElu>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleElu.test.cpp b/compiler/luci/service/src/Nodes/CircleElu.test.cpp new file mode 100644 index 000000000..e75b3bcb1 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleElu.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Elu) +{ + auto g = loco::make_graph(); + auto node_elu = g->nodes()->create<luci::CircleElu>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_elu, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_elu = dynamic_cast<luci::CircleElu *>(cloned); + ASSERT_NE(nullptr, cloned_elu); +} diff --git a/compiler/luci/service/src/Nodes/CircleEqual.cpp b/compiler/luci/service/src/Nodes/CircleEqual.cpp new file mode 100644 index 000000000..5dd382d0b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleEqual.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleEqual *) +{ + return _graph->nodes()->create<luci::CircleEqual>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleEqual.test.cpp b/compiler/luci/service/src/Nodes/CircleEqual.test.cpp new file mode 100644 index 000000000..99a5535fc --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleEqual.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Equal) +{ + auto g = loco::make_graph(); + auto node_eq = g->nodes()->create<luci::CircleEqual>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_eq, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_eq = dynamic_cast<luci::CircleEqual *>(cloned); + ASSERT_NE(nullptr, cloned_eq); +} diff --git a/compiler/luci/service/src/Nodes/CircleExp.cpp b/compiler/luci/service/src/Nodes/CircleExp.cpp new file mode 100644 index 000000000..3d4918320 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleExp.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleExp *) +{ + return _graph->nodes()->create<luci::CircleExp>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleExp.test.cpp b/compiler/luci/service/src/Nodes/CircleExp.test.cpp new file mode 100644 index 000000000..ff2bb65db --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleExp.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Exp) +{ + auto g = loco::make_graph(); + auto node_exp = g->nodes()->create<luci::CircleExp>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_exp, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_exp = dynamic_cast<luci::CircleExp *>(cloned); + ASSERT_NE(nullptr, cloned_exp); +} diff --git a/compiler/luci/service/src/Nodes/CircleExpandDims.cpp b/compiler/luci/service/src/Nodes/CircleExpandDims.cpp new file mode 100644 index 000000000..4dd1cec86 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleExpandDims.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleExpandDims *) +{ + return _graph->nodes()->create<luci::CircleExpandDims>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleExpandDims.test.cpp b/compiler/luci/service/src/Nodes/CircleExpandDims.test.cpp new file mode 100644 index 000000000..e3481bccd --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleExpandDims.test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, simple_expand_dims) +{ + luci::CircleInput input; + luci::CircleConst axis; + luci::CircleExpandDims expand_dims; + + input.shape({4, 3}); + input.shape_status(luci::ShapeStatus::VALID); + + axis.dtype(loco::DataType::S32); + axis.rank(0); + axis.size<loco::DataType::S32>(1); + axis.at<loco::DataType::S32>(0) = 1; + axis.shape_status(luci::ShapeStatus::VALID); + + expand_dims.input(&input); + expand_dims.axis(&axis); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&expand_dims, shape)); + ASSERT_EQ(3, shape.rank()); + ASSERT_EQ(4, shape.dim(0).value()); + ASSERT_EQ(1, shape.dim(1).value()); + ASSERT_EQ(3, shape.dim(2).value()); +} + +TEST(CloneNodeTest, clone_ExpandDims) +{ + auto g = loco::make_graph(); + auto node_ed = g->nodes()->create<luci::CircleExpandDims>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_ed, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_ed = dynamic_cast<luci::CircleExpandDims *>(cloned); + ASSERT_NE(nullptr, cloned_ed); +} diff --git a/compiler/luci/service/src/Nodes/CircleFakeQuant.cpp b/compiler/luci/service/src/Nodes/CircleFakeQuant.cpp new file mode 100644 index 000000000..7abaca685 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFakeQuant.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleFakeQuant *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleFakeQuant>(); + if (cloned != nullptr) + { + cloned->min(node->min()); + cloned->max(node->max()); + cloned->num_bits(node->num_bits()); + cloned->narrow_range(node->narrow_range()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleFakeQuant.test.cpp b/compiler/luci/service/src/Nodes/CircleFakeQuant.test.cpp new file mode 100644 index 000000000..2c4e3b836 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFakeQuant.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_FakeQuant) +{ + auto g = loco::make_graph(); + auto node_fq = g->nodes()->create<luci::CircleFakeQuant>(); + node_fq->min(1.0f); + node_fq->max(2.0f); + node_fq->num_bits(8); + node_fq->narrow_range(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fq, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_fq = dynamic_cast<luci::CircleFakeQuant *>(cloned); + ASSERT_NE(nullptr, cloned_fq); + ASSERT_EQ(node_fq->min(), cloned_fq->min()); + ASSERT_EQ(node_fq->max(), cloned_fq->max()); + ASSERT_EQ(node_fq->num_bits(), cloned_fq->num_bits()); + ASSERT_EQ(node_fq->narrow_range(), cloned_fq->narrow_range()); +} diff --git a/compiler/luci/service/src/Nodes/CircleFill.cpp b/compiler/luci/service/src/Nodes/CircleFill.cpp new file mode 100644 index 000000000..d9b74c63a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFill.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleFill *) +{ + return _graph->nodes()->create<luci::CircleFill>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleFill.test.cpp b/compiler/luci/service/src/Nodes/CircleFill.test.cpp new file mode 100644 index 000000000..56c807585 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFill.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Fill) +{ + auto g = loco::make_graph(); + auto node_fill = g->nodes()->create<luci::CircleFill>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fill, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_fill = dynamic_cast<luci::CircleFill *>(cloned); + ASSERT_NE(nullptr, cloned_fill); +} diff --git a/compiler/luci/service/src/Nodes/CircleFloor.cpp b/compiler/luci/service/src/Nodes/CircleFloor.cpp new file mode 100644 index 000000000..532808bc8 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFloor.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleFloor *) +{ + return _graph->nodes()->create<luci::CircleFloor>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleFloor.test.cpp b/compiler/luci/service/src/Nodes/CircleFloor.test.cpp new file mode 100644 index 000000000..3d53fd2c3 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFloor.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Floor) +{ + auto g = loco::make_graph(); + auto node_floor = g->nodes()->create<luci::CircleFloor>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_floor, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_floor = dynamic_cast<luci::CircleFloor *>(cloned); + ASSERT_NE(nullptr, cloned_floor); +} diff --git a/compiler/luci/service/src/Nodes/CircleFloorDiv.cpp b/compiler/luci/service/src/Nodes/CircleFloorDiv.cpp new file mode 100644 index 000000000..65be3e868 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFloorDiv.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleFloorDiv *) +{ + return _graph->nodes()->create<luci::CircleFloorDiv>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleFloorDiv.test.cpp b/compiler/luci/service/src/Nodes/CircleFloorDiv.test.cpp new file mode 100644 index 000000000..6365ccd3b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFloorDiv.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_FloorDiv) +{ + auto g = loco::make_graph(); + auto node_floordiv = g->nodes()->create<luci::CircleFloorDiv>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_floordiv, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_floordiv = dynamic_cast<luci::CircleFloorDiv *>(cloned); + ASSERT_NE(nullptr, cloned_floordiv); +} diff --git a/compiler/luci/service/src/Nodes/CircleFloorMod.cpp b/compiler/luci/service/src/Nodes/CircleFloorMod.cpp new file mode 100644 index 000000000..00e6a0499 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFloorMod.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleFloorMod *) +{ + return _graph->nodes()->create<luci::CircleFloorMod>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleFloorMod.test.cpp b/compiler/luci/service/src/Nodes/CircleFloorMod.test.cpp new file mode 100644 index 000000000..ce91d5881 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFloorMod.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_FloorMod) +{ + auto g = loco::make_graph(); + auto node_floormod = g->nodes()->create<luci::CircleFloorMod>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_floormod, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_floormod = dynamic_cast<luci::CircleFloorMod *>(cloned); + ASSERT_NE(nullptr, cloned_floormod); +} diff --git a/compiler/luci/service/src/Nodes/CircleFullyConnected.cpp b/compiler/luci/service/src/Nodes/CircleFullyConnected.cpp new file mode 100644 index 000000000..8acb35cbf --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFullyConnected.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleFullyConnected *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + if (node->weights_format() == luci::CircleFullyConnected::WeightsFormat::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleFullyConnected>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->weights_format(node->weights_format()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleFullyConnected.test.cpp b/compiler/luci/service/src/Nodes/CircleFullyConnected.test.cpp new file mode 100644 index 000000000..965b59130 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleFullyConnected.test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_FullyConnected) +{ + auto g = loco::make_graph(); + auto node_fc = g->nodes()->create<luci::CircleFullyConnected>(); + node_fc->fusedActivationFunction(luci::FusedActFunc::RELU); + node_fc->weights_format(luci::CircleFullyConnected::WeightsFormat::DEFAULT); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fc, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_fc = dynamic_cast<luci::CircleFullyConnected *>(cloned); + ASSERT_NE(nullptr, cloned_fc); + ASSERT_EQ(node_fc->fusedActivationFunction(), cloned_fc->fusedActivationFunction()); + ASSERT_EQ(node_fc->weights_format(), cloned_fc->weights_format()); +} + +TEST(CloneNodeTest, clone_FullyConnected_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_fc = g->nodes()->create<luci::CircleFullyConnected>(); + node_fc->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + node_fc->weights_format(luci::CircleFullyConnected::WeightsFormat::DEFAULT); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fc, gc.get()); + ASSERT_EQ(nullptr, cloned); +} + +TEST(CloneNodeTest, clone_FullyConnected_wf_NEG) +{ + auto g = loco::make_graph(); + auto node_fc = g->nodes()->create<luci::CircleFullyConnected>(); + node_fc->fusedActivationFunction(luci::FusedActFunc::RELU); + node_fc->weights_format(luci::CircleFullyConnected::WeightsFormat::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fc, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleGather.cpp b/compiler/luci/service/src/Nodes/CircleGather.cpp new file mode 100644 index 000000000..072bdeabc --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGather.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleGather *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleGather>(); + if (cloned != nullptr) + cloned->axis(node->axis()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleGather.test.cpp b/compiler/luci/service/src/Nodes/CircleGather.test.cpp new file mode 100644 index 000000000..f48dbdb67 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGather.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Gather) +{ + auto g = loco::make_graph(); + auto node_gat = g->nodes()->create<luci::CircleGather>(); + node_gat->axis(3); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_gat, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_gat = dynamic_cast<luci::CircleGather *>(cloned); + ASSERT_NE(nullptr, cloned_gat); + ASSERT_EQ(node_gat->axis(), cloned_gat->axis()); +} diff --git a/compiler/luci/service/src/Nodes/CircleGatherNd.cpp b/compiler/luci/service/src/Nodes/CircleGatherNd.cpp new file mode 100644 index 000000000..df7ae6e79 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGatherNd.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleGatherNd *) +{ + return _graph->nodes()->create<luci::CircleGatherNd>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleGatherNd.test.cpp b/compiler/luci/service/src/Nodes/CircleGatherNd.test.cpp new file mode 100644 index 000000000..3a705710c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGatherNd.test.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <oops/InternalExn.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, gather_nd_simple) +{ + luci::CircleInput input; + luci::CircleConst indices_const; + luci::CircleGatherNd gather_nd; + + input.shape({1, 4, 4, 3}); + indices_const.shape({1, 2, 3}); + + input.shape_status(luci::ShapeStatus::VALID); + indices_const.shape_status(luci::ShapeStatus::VALID); + + gather_nd.params(&input); + gather_nd.indices(&indices_const); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&gather_nd, shape)); + ASSERT_EQ(3, shape.rank()); + ASSERT_EQ(1, shape.dim(0).value()); + ASSERT_EQ(2, shape.dim(1).value()); + ASSERT_EQ(3, shape.dim(2).value()); +} + +TEST(ShapeRuleTest, gather_nd_slices) +{ + luci::CircleInput input; + luci::CircleConst indices_const; + luci::CircleGatherNd gather_nd; + + input.shape({1, 4, 4, 3}); + indices_const.shape({1, 2, 1}); + + input.shape_status(luci::ShapeStatus::VALID); + indices_const.shape_status(luci::ShapeStatus::VALID); + + gather_nd.params(&input); + gather_nd.indices(&indices_const); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&gather_nd, shape)); + ASSERT_EQ(5, shape.rank()); + ASSERT_EQ(1, shape.dim(0).value()); + ASSERT_EQ(2, shape.dim(1).value()); + ASSERT_EQ(4, shape.dim(2).value()); + ASSERT_EQ(4, shape.dim(3).value()); + ASSERT_EQ(3, shape.dim(4).value()); +} + +TEST(ShapeRuleTest, gather_nd_NEG) +{ + luci::CircleInput input; + luci::CircleConst indices_const; + luci::CircleGatherNd gather_nd; + + input.shape({1, 4, 4, 3}); + indices_const.shape({1, 2, 5}); + + input.shape_status(luci::ShapeStatus::VALID); + indices_const.shape_status(luci::ShapeStatus::VALID); + + gather_nd.params(&input); + gather_nd.indices(&indices_const); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_THROW(shape_inf_rule.infer(&gather_nd, shape), oops::InternalExn); +} + +TEST(CloneNodeTest, clone_GatherNd) +{ + auto g = loco::make_graph(); + auto node_gtnd = g->nodes()->create<luci::CircleGatherNd>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_gtnd, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_gtnd = dynamic_cast<luci::CircleGatherNd *>(cloned); + ASSERT_NE(nullptr, cloned_gtnd); +} diff --git a/compiler/luci/service/src/Nodes/CircleGreater.cpp b/compiler/luci/service/src/Nodes/CircleGreater.cpp new file mode 100644 index 000000000..366d955bf --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGreater.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleGreater *) +{ + return _graph->nodes()->create<luci::CircleGreater>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleGreater.test.cpp b/compiler/luci/service/src/Nodes/CircleGreater.test.cpp new file mode 100644 index 000000000..6d2df61f0 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGreater.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Greater) +{ + auto g = loco::make_graph(); + auto node_gt = g->nodes()->create<luci::CircleGreater>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_gt, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_gt = dynamic_cast<luci::CircleGreater *>(cloned); + ASSERT_NE(nullptr, cloned_gt); +} diff --git a/compiler/luci/service/src/Nodes/CircleGreaterEqual.cpp b/compiler/luci/service/src/Nodes/CircleGreaterEqual.cpp new file mode 100644 index 000000000..9705bbe1e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGreaterEqual.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleGreaterEqual *) +{ + return _graph->nodes()->create<luci::CircleGreaterEqual>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleGreaterEqual.test.cpp b/compiler/luci/service/src/Nodes/CircleGreaterEqual.test.cpp new file mode 100644 index 000000000..10387df3a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleGreaterEqual.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_GreaterEqual) +{ + auto g = loco::make_graph(); + auto node_ge = g->nodes()->create<luci::CircleGreaterEqual>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_ge, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_ge = dynamic_cast<luci::CircleGreaterEqual *>(cloned); + ASSERT_NE(nullptr, cloned_ge); +} diff --git a/compiler/luci/service/src/Nodes/CircleIfOut.cpp b/compiler/luci/service/src/Nodes/CircleIfOut.cpp new file mode 100644 index 000000000..31ad7203f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleIfOut.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021 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 <luci/Service/CircleTypeInference.h> + +namespace +{ + +struct CircleIfOutGraphs +{ + loco::GraphOutput *then_graph_output; + loco::GraphOutput *else_graph_output; +}; + +} // namespace + +namespace +{ + +CircleIfOutGraphs get_out_graphs(const luci::CircleIfOut *node) +{ + CircleIfOutGraphs ret_out; + + /** + * @note IF operator type and shape are that of the "then" and "else" + * Graph Outputs. + */ + auto circle_if = loco::must_cast<const luci::CircleIf *>(node->input()); + + auto index = node->index(); + auto then_graph = circle_if->then_graph(); + auto else_graph = circle_if->else_graph(); + assert(then_graph != nullptr); + assert(else_graph != nullptr); + + // shape and type are assumed to be same + // these are checked at post_import_graph() in Import + auto then_outputs = loco::output_nodes(then_graph); + auto else_outputs = loco::output_nodes(else_graph); + assert(then_outputs.size() == else_outputs.size()); + assert(index < static_cast<int32_t>(then_outputs.size())); + + auto then_out = loco::must_cast<luci::CircleOutput *>(then_outputs.at(index)); + auto else_out = loco::must_cast<luci::CircleOutput *>(else_outputs.at(index)); + + auto then_graph_outputs = then_graph->outputs(); // loco::GraphOutput items + auto else_graph_outputs = else_graph->outputs(); + assert(then_graph_outputs->size() == else_graph_outputs->size()); + + ret_out.then_graph_output = then_graph_outputs->at(then_out->index()); + ret_out.else_graph_output = else_graph_outputs->at(else_out->index()); + + return ret_out; +} + +} // namespace + +namespace luci +{ + +loco::TensorShape sinf::Algorithm::visit(const luci::CircleIfOut *node) +{ + auto graphs = get_out_graphs(node); + assert(*graphs.then_graph_output->shape() == *graphs.else_graph_output->shape()); + return *graphs.then_graph_output->shape(); +} + +loco::DataType tinf::Algorithm::visit(const luci::CircleIfOut *node) +{ + auto graphs = get_out_graphs(node); + assert(graphs.then_graph_output->dtype() == graphs.else_graph_output->dtype()); + return graphs.then_graph_output->dtype(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleInstanceNorm.cpp b/compiler/luci/service/src/Nodes/CircleInstanceNorm.cpp new file mode 100644 index 000000000..d9e49d8ed --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleInstanceNorm.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleInstanceNorm *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleInstanceNorm>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->epsilon(node->epsilon()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleInstanceNorm.test.cpp b/compiler/luci/service/src/Nodes/CircleInstanceNorm.test.cpp new file mode 100644 index 000000000..bae92b1ae --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleInstanceNorm.test.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_InstanceNorm) +{ + auto g = loco::make_graph(); + auto node_fc = g->nodes()->create<luci::CircleInstanceNorm>(); + node_fc->fusedActivationFunction(luci::FusedActFunc::RELU); + node_fc->epsilon(3); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fc, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_fc = dynamic_cast<luci::CircleInstanceNorm *>(cloned); + ASSERT_NE(nullptr, cloned_fc); + ASSERT_EQ(node_fc->fusedActivationFunction(), cloned_fc->fusedActivationFunction()); + ASSERT_EQ(node_fc->epsilon(), cloned_fc->epsilon()); +} + +TEST(CloneNodeTest, clone_InstanceNorm_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_fc = g->nodes()->create<luci::CircleInstanceNorm>(); + node_fc->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_fc, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleL2Normalize.cpp b/compiler/luci/service/src/Nodes/CircleL2Normalize.cpp new file mode 100644 index 000000000..afa2a6acb --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleL2Normalize.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleL2Normalize *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleL2Normalize>(); + if (cloned != nullptr) + cloned->fusedActivationFunction(node->fusedActivationFunction()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleL2Normalize.test.cpp b/compiler/luci/service/src/Nodes/CircleL2Normalize.test.cpp new file mode 100644 index 000000000..0f148797e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleL2Normalize.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_L2Normalize) +{ + auto g = loco::make_graph(); + auto node_l2n = g->nodes()->create<luci::CircleL2Normalize>(); + node_l2n->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_l2n, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_l2n = dynamic_cast<luci::CircleL2Normalize *>(cloned); + ASSERT_NE(nullptr, cloned_l2n); + ASSERT_EQ(node_l2n->fusedActivationFunction(), cloned_l2n->fusedActivationFunction()); +} + +TEST(CloneNodeTest, clone_L2Normalize_NEG) +{ + auto g = loco::make_graph(); + auto node_l2n = g->nodes()->create<luci::CircleL2Normalize>(); + node_l2n->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_l2n, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleL2Pool2D.cpp b/compiler/luci/service/src/Nodes/CircleL2Pool2D.cpp new file mode 100644 index 000000000..2d876c5bc --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleL2Pool2D.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleL2Pool2D *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + if (node->padding() == luci::Padding::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleL2Pool2D>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->padding(node->padding()); + cloned->filter()->h(node->filter()->h()); + cloned->filter()->w(node->filter()->w()); + cloned->stride()->h(node->stride()->h()); + cloned->stride()->w(node->stride()->w()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleL2Pool2D.test.cpp b/compiler/luci/service/src/Nodes/CircleL2Pool2D.test.cpp new file mode 100644 index 000000000..37344fd9a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleL2Pool2D.test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_L2Pool2D) +{ + auto g = loco::make_graph(); + auto node_l2n = g->nodes()->create<luci::CircleL2Pool2D>(); + node_l2n->fusedActivationFunction(luci::FusedActFunc::RELU); + node_l2n->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_l2n, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_l2n = dynamic_cast<luci::CircleL2Pool2D *>(cloned); + ASSERT_NE(nullptr, cloned_l2n); + ASSERT_EQ(node_l2n->fusedActivationFunction(), cloned_l2n->fusedActivationFunction()); + ASSERT_EQ(node_l2n->padding(), cloned_l2n->padding()); +} + +TEST(CloneNodeTest, clone_L2Normalize_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_l2n = g->nodes()->create<luci::CircleL2Pool2D>(); + node_l2n->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + node_l2n->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_l2n, gc.get()); + ASSERT_EQ(nullptr, cloned); +} + +TEST(CloneNodeTest, clone_L2Normalize_padding_NEG) +{ + auto g = loco::make_graph(); + auto node_l2n = g->nodes()->create<luci::CircleL2Pool2D>(); + node_l2n->fusedActivationFunction(luci::FusedActFunc::RELU); + node_l2n->padding(luci::Padding::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_l2n, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleLeakyRelu.cpp b/compiler/luci/service/src/Nodes/CircleLeakyRelu.cpp new file mode 100644 index 000000000..91030618c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLeakyRelu.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLeakyRelu *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleLeakyRelu>(); + if (cloned != nullptr) + cloned->alpha(node->alpha()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLeakyRelu.test.cpp b/compiler/luci/service/src/Nodes/CircleLeakyRelu.test.cpp new file mode 100644 index 000000000..17fc1442a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLeakyRelu.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_LeakyRelu) +{ + auto g = loco::make_graph(); + auto node_lr = g->nodes()->create<luci::CircleLeakyRelu>(); + node_lr->alpha(1.2f); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_lr, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_lr = dynamic_cast<luci::CircleLeakyRelu *>(cloned); + ASSERT_NE(nullptr, cloned_lr); + ASSERT_EQ(node_lr->alpha(), cloned_lr->alpha()); +} diff --git a/compiler/luci/service/src/Nodes/CircleLess.cpp b/compiler/luci/service/src/Nodes/CircleLess.cpp new file mode 100644 index 000000000..33b70b735 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLess.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLess *) +{ + return _graph->nodes()->create<luci::CircleLess>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLess.test.cpp b/compiler/luci/service/src/Nodes/CircleLess.test.cpp new file mode 100644 index 000000000..43248948d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLess.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Less) +{ + auto g = loco::make_graph(); + auto node_less = g->nodes()->create<luci::CircleLess>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_less, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_less = dynamic_cast<luci::CircleLess *>(cloned); + ASSERT_NE(nullptr, cloned_less); +} diff --git a/compiler/luci/service/src/Nodes/CircleLessEqual.cpp b/compiler/luci/service/src/Nodes/CircleLessEqual.cpp new file mode 100644 index 000000000..22491365a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLessEqual.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLessEqual *) +{ + return _graph->nodes()->create<luci::CircleLessEqual>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLessEqual.test.cpp b/compiler/luci/service/src/Nodes/CircleLessEqual.test.cpp new file mode 100644 index 000000000..0a87daf5d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLessEqual.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_LessEqual) +{ + auto g = loco::make_graph(); + auto node_le = g->nodes()->create<luci::CircleLessEqual>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_le, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_le = dynamic_cast<luci::CircleLessEqual *>(cloned); + ASSERT_NE(nullptr, cloned_le); +} diff --git a/compiler/luci/service/src/Nodes/CircleLocalResponseNormalization.cpp b/compiler/luci/service/src/Nodes/CircleLocalResponseNormalization.cpp new file mode 100644 index 000000000..bf69b5ef5 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLocalResponseNormalization.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLocalResponseNormalization *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleLocalResponseNormalization>(); + if (cloned != nullptr) + { + cloned->radius(node->radius()); + cloned->bias(node->bias()); + cloned->alpha(node->alpha()); + cloned->beta(node->beta()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLocalResponseNormalization.test.cpp b/compiler/luci/service/src/Nodes/CircleLocalResponseNormalization.test.cpp new file mode 100644 index 000000000..262b119bb --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLocalResponseNormalization.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_LocalResponseNormalization) +{ + auto g = loco::make_graph(); + auto node_lrn = g->nodes()->create<luci::CircleLocalResponseNormalization>(); + node_lrn->radius(32); + node_lrn->bias(1.2f); + node_lrn->alpha(3.4f); + node_lrn->beta(5.7f); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_lrn, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_lrn = dynamic_cast<luci::CircleLocalResponseNormalization *>(cloned); + ASSERT_NE(nullptr, cloned_lrn); + ASSERT_EQ(node_lrn->radius(), cloned_lrn->radius()); + ASSERT_EQ(node_lrn->bias(), cloned_lrn->bias()); + ASSERT_EQ(node_lrn->alpha(), cloned_lrn->alpha()); + ASSERT_EQ(node_lrn->beta(), cloned_lrn->beta()); +} diff --git a/compiler/luci/service/src/Nodes/CircleLog.cpp b/compiler/luci/service/src/Nodes/CircleLog.cpp new file mode 100644 index 000000000..5788f129f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLog.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLog *) +{ + return _graph->nodes()->create<luci::CircleLog>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLog.test.cpp b/compiler/luci/service/src/Nodes/CircleLog.test.cpp new file mode 100644 index 000000000..d1ee1428e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLog.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Log) +{ + auto g = loco::make_graph(); + auto node_log = g->nodes()->create<luci::CircleLog>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_log, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_log = dynamic_cast<luci::CircleLog *>(cloned); + ASSERT_NE(nullptr, cloned_log); +} diff --git a/compiler/luci/service/src/Nodes/CircleLogSoftmax.cpp b/compiler/luci/service/src/Nodes/CircleLogSoftmax.cpp new file mode 100644 index 000000000..352160aff --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogSoftmax.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLogSoftmax *) +{ + return _graph->nodes()->create<luci::CircleLogSoftmax>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLogSoftmax.test.cpp b/compiler/luci/service/src/Nodes/CircleLogSoftmax.test.cpp new file mode 100644 index 000000000..feebb79cb --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogSoftmax.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_LogSoftmax) +{ + auto g = loco::make_graph(); + auto node_logs = g->nodes()->create<luci::CircleLogSoftmax>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_logs, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_logs = dynamic_cast<luci::CircleLogSoftmax *>(cloned); + ASSERT_NE(nullptr, cloned_logs); +} diff --git a/compiler/luci/service/src/Nodes/CircleLogicalAnd.cpp b/compiler/luci/service/src/Nodes/CircleLogicalAnd.cpp new file mode 100644 index 000000000..5df62b951 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogicalAnd.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLogicalAnd *) +{ + return _graph->nodes()->create<luci::CircleLogicalAnd>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLogicalAnd.test.cpp b/compiler/luci/service/src/Nodes/CircleLogicalAnd.test.cpp new file mode 100644 index 000000000..aa811edfa --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogicalAnd.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_LogicalAnd) +{ + auto g = loco::make_graph(); + auto node_logand = g->nodes()->create<luci::CircleLogicalAnd>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_logand, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_logand = dynamic_cast<luci::CircleLogicalAnd *>(cloned); + ASSERT_NE(nullptr, cloned_logand); +} diff --git a/compiler/luci/service/src/Nodes/CircleLogicalNot.cpp b/compiler/luci/service/src/Nodes/CircleLogicalNot.cpp new file mode 100644 index 000000000..ac982829d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogicalNot.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLogicalNot *) +{ + return _graph->nodes()->create<luci::CircleLogicalNot>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLogicalNot.test.cpp b/compiler/luci/service/src/Nodes/CircleLogicalNot.test.cpp new file mode 100644 index 000000000..9e55be944 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogicalNot.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_LogicalNot) +{ + auto g = loco::make_graph(); + auto node_lognot = g->nodes()->create<luci::CircleLogicalNot>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_lognot, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_lognot = dynamic_cast<luci::CircleLogicalNot *>(cloned); + ASSERT_NE(nullptr, cloned_lognot); +} diff --git a/compiler/luci/service/src/Nodes/CircleLogicalOr.cpp b/compiler/luci/service/src/Nodes/CircleLogicalOr.cpp new file mode 100644 index 000000000..1201d6f34 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogicalOr.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLogicalOr *) +{ + return _graph->nodes()->create<luci::CircleLogicalOr>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLogicalOr.test.cpp b/compiler/luci/service/src/Nodes/CircleLogicalOr.test.cpp new file mode 100644 index 000000000..19b706dcd --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogicalOr.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_LogicalOr) +{ + auto g = loco::make_graph(); + auto node_logor = g->nodes()->create<luci::CircleLogicalOr>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_logor, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_logor = dynamic_cast<luci::CircleLogicalOr *>(cloned); + ASSERT_NE(nullptr, cloned_logor); +} diff --git a/compiler/luci/service/src/Nodes/CircleLogistic.cpp b/compiler/luci/service/src/Nodes/CircleLogistic.cpp new file mode 100644 index 000000000..b21b187e9 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogistic.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleLogistic *) +{ + return _graph->nodes()->create<luci::CircleLogistic>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleLogistic.test.cpp b/compiler/luci/service/src/Nodes/CircleLogistic.test.cpp new file mode 100644 index 000000000..05dbe46e4 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleLogistic.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Logistic) +{ + auto g = loco::make_graph(); + auto node_log = g->nodes()->create<luci::CircleLogistic>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_log, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_log = dynamic_cast<luci::CircleLogistic *>(cloned); + ASSERT_NE(nullptr, cloned_log); +} diff --git a/compiler/luci/service/src/Nodes/CircleMatrixDiag.cpp b/compiler/luci/service/src/Nodes/CircleMatrixDiag.cpp new file mode 100644 index 000000000..2bffa07b1 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMatrixDiag.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleMatrixDiag *) +{ + return _graph->nodes()->create<luci::CircleMatrixDiag>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMatrixDiag.test.cpp b/compiler/luci/service/src/Nodes/CircleMatrixDiag.test.cpp new file mode 100644 index 000000000..c08c4cb94 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMatrixDiag.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_MatrixDiag) +{ + auto g = loco::make_graph(); + auto node_md = g->nodes()->create<luci::CircleMatrixDiag>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_md, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_md = dynamic_cast<luci::CircleMatrixDiag *>(cloned); + ASSERT_NE(nullptr, cloned_md); +} diff --git a/compiler/luci/service/src/Nodes/CircleMatrixSetDiag.cpp b/compiler/luci/service/src/Nodes/CircleMatrixSetDiag.cpp new file mode 100644 index 000000000..5ea2a5339 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMatrixSetDiag.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleMatrixSetDiag *) +{ + return _graph->nodes()->create<luci::CircleMatrixSetDiag>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMatrixSetDiag.test.cpp b/compiler/luci/service/src/Nodes/CircleMatrixSetDiag.test.cpp new file mode 100644 index 000000000..5ea77ba75 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMatrixSetDiag.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_MatrixSetDiag) +{ + auto g = loco::make_graph(); + auto node_msd = g->nodes()->create<luci::CircleMatrixSetDiag>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_msd, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_msd = dynamic_cast<luci::CircleMatrixSetDiag *>(cloned); + ASSERT_NE(nullptr, cloned_msd); +} diff --git a/compiler/luci/service/src/Nodes/CircleMaxPool2D.cpp b/compiler/luci/service/src/Nodes/CircleMaxPool2D.cpp new file mode 100644 index 000000000..b21610c7f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMaxPool2D.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleMaxPool2D *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + if (node->padding() == luci::Padding::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleMaxPool2D>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->padding(node->padding()); + cloned->filter()->h(node->filter()->h()); + cloned->filter()->w(node->filter()->w()); + cloned->stride()->h(node->stride()->h()); + cloned->stride()->w(node->stride()->w()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMaxPool2D.test.cpp b/compiler/luci/service/src/Nodes/CircleMaxPool2D.test.cpp new file mode 100644 index 000000000..415cf7c44 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMaxPool2D.test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_MaxPool2D) +{ + auto g = loco::make_graph(); + auto node_mp = g->nodes()->create<luci::CircleMaxPool2D>(); + node_mp->fusedActivationFunction(luci::FusedActFunc::RELU); + node_mp->padding(luci::Padding::SAME); + node_mp->filter()->h(1); + node_mp->filter()->w(2); + node_mp->stride()->h(3); + node_mp->stride()->w(4); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mp, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_mp = dynamic_cast<luci::CircleMaxPool2D *>(cloned); + ASSERT_NE(nullptr, cloned_mp); + ASSERT_EQ(node_mp->fusedActivationFunction(), cloned_mp->fusedActivationFunction()); + ASSERT_EQ(node_mp->padding(), cloned_mp->padding()); + ASSERT_EQ(node_mp->filter()->h(), cloned_mp->filter()->h()); + ASSERT_EQ(node_mp->filter()->w(), cloned_mp->filter()->w()); + ASSERT_EQ(node_mp->stride()->h(), cloned_mp->stride()->h()); + ASSERT_EQ(node_mp->stride()->w(), cloned_mp->stride()->w()); +} + +TEST(CloneNodeTest, clone_MaxPool2D_fusedact_NEG) +{ + auto g = loco::make_graph(); + auto node_mp = g->nodes()->create<luci::CircleMaxPool2D>(); + node_mp->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + node_mp->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mp, gc.get()); + ASSERT_EQ(nullptr, cloned); +} + +TEST(CloneNodeTest, clone_MaxPool2D_padding_NEG) +{ + auto g = loco::make_graph(); + auto node_mp = g->nodes()->create<luci::CircleMaxPool2D>(); + node_mp->fusedActivationFunction(luci::FusedActFunc::RELU); + node_mp->padding(luci::Padding::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mp, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleMaximum.cpp b/compiler/luci/service/src/Nodes/CircleMaximum.cpp new file mode 100644 index 000000000..545f4ca21 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMaximum.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleMaximum *) +{ + return _graph->nodes()->create<luci::CircleMaximum>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMaximum.test.cpp b/compiler/luci/service/src/Nodes/CircleMaximum.test.cpp new file mode 100644 index 000000000..6f1ada060 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMaximum.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Maximum) +{ + auto g = loco::make_graph(); + auto node_max = g->nodes()->create<luci::CircleMaximum>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_max, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_max = dynamic_cast<luci::CircleMaximum *>(cloned); + ASSERT_NE(nullptr, cloned_max); +} diff --git a/compiler/luci/service/src/Nodes/CircleMean.cpp b/compiler/luci/service/src/Nodes/CircleMean.cpp index a78713698..95bc54532 100644 --- a/compiler/luci/service/src/Nodes/CircleMean.cpp +++ b/compiler/luci/service/src/Nodes/CircleMean.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,15 +14,17 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleMean *node) +luci::CircleNode *CloneNode::visit(const luci::CircleMean *node) { - return legalized_signature( - reduced_signature(node->input(), node->reduction_indices(), node->keep_dims())); + auto *cloned = _graph->nodes()->create<luci::CircleMean>(); + if (cloned != nullptr) + cloned->keep_dims(node->keep_dims()); + return cloned; } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMean.test.cpp b/compiler/luci/service/src/Nodes/CircleMean.test.cpp new file mode 100644 index 000000000..aa1b88f13 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMean.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Mean) +{ + auto g = loco::make_graph(); + auto node_mean = g->nodes()->create<luci::CircleMean>(); + node_mean->keep_dims(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mean, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_mean = dynamic_cast<luci::CircleMean *>(cloned); + ASSERT_NE(nullptr, cloned_mean); + ASSERT_EQ(node_mean->keep_dims(), cloned_mean->keep_dims()); +} diff --git a/compiler/luci/service/src/Nodes/CircleMinimum.cpp b/compiler/luci/service/src/Nodes/CircleMinimum.cpp new file mode 100644 index 000000000..2c2755c55 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMinimum.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleMinimum *) +{ + return _graph->nodes()->create<luci::CircleMinimum>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMinimum.test.cpp b/compiler/luci/service/src/Nodes/CircleMinimum.test.cpp new file mode 100644 index 000000000..0a54be71c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMinimum.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Minimum) +{ + auto g = loco::make_graph(); + auto node_min = g->nodes()->create<luci::CircleMinimum>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_min, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_min = dynamic_cast<luci::CircleMinimum *>(cloned); + ASSERT_NE(nullptr, cloned_min); +} diff --git a/compiler/luci/service/src/Nodes/CircleMirrorPad.cpp b/compiler/luci/service/src/Nodes/CircleMirrorPad.cpp new file mode 100644 index 000000000..919221a0b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMirrorPad.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleMirrorPad *node) +{ + if (node->mode() == luci::MirrorPadMode::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleMirrorPad>(); + if (cloned != nullptr) + cloned->mode(node->mode()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMirrorPad.test.cpp b/compiler/luci/service/src/Nodes/CircleMirrorPad.test.cpp new file mode 100644 index 000000000..911cf6d3b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMirrorPad.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_MirrorPad) +{ + auto g = loco::make_graph(); + auto node_mp = g->nodes()->create<luci::CircleMirrorPad>(); + node_mp->mode(luci::MirrorPadMode::REFLECT); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mp, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_mp = dynamic_cast<luci::CircleMirrorPad *>(cloned); + ASSERT_NE(nullptr, cloned_mp); + ASSERT_EQ(node_mp->mode(), cloned_mp->mode()); +} + +TEST(CloneNodeTest, clone_MirrorPad_mode_NEG) +{ + auto g = loco::make_graph(); + auto node_mp = g->nodes()->create<luci::CircleMirrorPad>(); + node_mp->mode(luci::MirrorPadMode::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mp, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleMul.cpp b/compiler/luci/service/src/Nodes/CircleMul.cpp new file mode 100644 index 000000000..096aed196 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMul.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleMul *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleMul>(); + if (cloned != nullptr) + cloned->fusedActivationFunction(node->fusedActivationFunction()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleMul.test.cpp b/compiler/luci/service/src/Nodes/CircleMul.test.cpp new file mode 100644 index 000000000..dc5565f11 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleMul.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Mul) +{ + auto g = loco::make_graph(); + auto node_mul = g->nodes()->create<luci::CircleMul>(); + node_mul->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mul, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_mul = dynamic_cast<luci::CircleMul *>(cloned); + ASSERT_NE(nullptr, cloned_mul); + ASSERT_EQ(node_mul->fusedActivationFunction(), cloned_mul->fusedActivationFunction()); +} + +TEST(CloneNodeTest, clone_Mul_NEG) +{ + auto g = loco::make_graph(); + auto node_mul = g->nodes()->create<luci::CircleMul>(); + node_mul->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_mul, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleNeg.cpp b/compiler/luci/service/src/Nodes/CircleNeg.cpp new file mode 100644 index 000000000..312189e77 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNeg.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleNeg *) +{ + return _graph->nodes()->create<luci::CircleNeg>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleNeg.test.cpp b/compiler/luci/service/src/Nodes/CircleNeg.test.cpp new file mode 100644 index 000000000..8c2880324 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNeg.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Neg) +{ + auto g = loco::make_graph(); + auto node_neg = g->nodes()->create<luci::CircleNeg>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_neg, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_neg = dynamic_cast<luci::CircleNeg *>(cloned); + ASSERT_NE(nullptr, cloned_neg); +} diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4.cpp new file mode 100644 index 000000000..4757e8314 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleNonMaxSuppressionV4 *) +{ + return _graph->nodes()->create<luci::CircleNonMaxSuppressionV4>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4.test.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4.test.cpp new file mode 100644 index 000000000..34f5b0325 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_NonMaxSuppressionV4) +{ + auto g = loco::make_graph(); + auto node_nms = g->nodes()->create<luci::CircleNonMaxSuppressionV4>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_nms, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_nms = dynamic_cast<luci::CircleNonMaxSuppressionV4 *>(cloned); + ASSERT_NE(nullptr, cloned_nms); +} diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4Out.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4Out.cpp new file mode 100644 index 000000000..2a12f2a45 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4Out.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleNonMaxSuppressionV4Out *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleNonMaxSuppressionV4Out>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp new file mode 100644 index 000000000..ed9e0e019 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV4Out.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_NonMaxSuppressionV4Out) +{ + auto g = loco::make_graph(); + auto node_nout = g->nodes()->create<luci::CircleNonMaxSuppressionV4Out>(); + node_nout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_nout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_nout = dynamic_cast<luci::CircleNonMaxSuppressionV4Out *>(cloned); + ASSERT_NE(nullptr, cloned_nout); + ASSERT_EQ(node_nout->index(), cloned_nout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5.cpp new file mode 100644 index 000000000..34d128072 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleNonMaxSuppressionV5 *) +{ + return _graph->nodes()->create<luci::CircleNonMaxSuppressionV5>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5.test.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5.test.cpp new file mode 100644 index 000000000..faaee969e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_NonMaxSuppressionV5) +{ + auto g = loco::make_graph(); + auto node_nms = g->nodes()->create<luci::CircleNonMaxSuppressionV5>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_nms, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_nms = dynamic_cast<luci::CircleNonMaxSuppressionV5 *>(cloned); + ASSERT_NE(nullptr, cloned_nms); +} diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5Out.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5Out.cpp new file mode 100644 index 000000000..e1d7875e7 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5Out.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleNonMaxSuppressionV5Out *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleNonMaxSuppressionV5Out>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp new file mode 100644 index 000000000..ef0f766b9 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNonMaxSuppressionV5Out.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_NonMaxSuppressionV5Out) +{ + auto g = loco::make_graph(); + auto node_nout = g->nodes()->create<luci::CircleNonMaxSuppressionV5Out>(); + node_nout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_nout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_nout = dynamic_cast<luci::CircleNonMaxSuppressionV5Out *>(cloned); + ASSERT_NE(nullptr, cloned_nout); + ASSERT_EQ(node_nout->index(), cloned_nout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleNotEqual.cpp b/compiler/luci/service/src/Nodes/CircleNotEqual.cpp new file mode 100644 index 000000000..4cb5320e8 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNotEqual.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleNotEqual *) +{ + return _graph->nodes()->create<luci::CircleNotEqual>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleNotEqual.test.cpp b/compiler/luci/service/src/Nodes/CircleNotEqual.test.cpp new file mode 100644 index 000000000..20f7dbc4b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleNotEqual.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_NotEqual) +{ + auto g = loco::make_graph(); + auto node_ne = g->nodes()->create<luci::CircleNotEqual>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_ne, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_ne = dynamic_cast<luci::CircleNotEqual *>(cloned); + ASSERT_NE(nullptr, cloned_ne); +} diff --git a/compiler/luci/service/src/Nodes/CircleOneHot.cpp b/compiler/luci/service/src/Nodes/CircleOneHot.cpp new file mode 100644 index 000000000..a33c8ff26 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleOneHot.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleOneHot *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleOneHot>(); + if (cloned != nullptr) + cloned->axis(node->axis()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleOneHot.test.cpp b/compiler/luci/service/src/Nodes/CircleOneHot.test.cpp new file mode 100644 index 000000000..dea927d1b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleOneHot.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_OneHot) +{ + auto g = loco::make_graph(); + auto node_oh = g->nodes()->create<luci::CircleOneHot>(); + node_oh->axis(3); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_oh, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_oh = dynamic_cast<luci::CircleOneHot *>(cloned); + ASSERT_NE(nullptr, cloned_oh); + ASSERT_EQ(node_oh->axis(), cloned_oh->axis()); +} diff --git a/compiler/luci/service/src/Nodes/CircleOutputDummy.cpp b/compiler/luci/service/src/Nodes/CircleOutputDummy.cpp index e0f13c439..ce94dff94 100644 --- a/compiler/luci/service/src/Nodes/CircleOutputDummy.cpp +++ b/compiler/luci/service/src/Nodes/CircleOutputDummy.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,11 +14,14 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleOutputDummy *) { return ShapeSignature(); } +luci::CircleNode *CloneNode::visit(const luci::CircleOutputDummy *) +{ + return _graph->nodes()->create<luci::CircleOutputDummy>(); +} } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleOutputDummy.test.cpp b/compiler/luci/service/src/Nodes/CircleOutputDummy.test.cpp new file mode 100644 index 000000000..6170c7c41 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleOutputDummy.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_OutputDummy) +{ + auto g = loco::make_graph(); + auto node_dummy = g->nodes()->create<luci::CircleOutputDummy>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_dummy, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_dummy = dynamic_cast<luci::CircleOutputDummy *>(cloned); + ASSERT_NE(nullptr, cloned_dummy); +} diff --git a/compiler/luci/service/src/Nodes/CircleOutputExclude.cpp b/compiler/luci/service/src/Nodes/CircleOutputExclude.cpp index 75bbbb3c0..1b0f919c3 100644 --- a/compiler/luci/service/src/Nodes/CircleOutputExclude.cpp +++ b/compiler/luci/service/src/Nodes/CircleOutputExclude.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,14 +14,14 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleOutputExclude *) +luci::CircleNode *CloneNode::visit(const luci::CircleOutputExclude *) { - return ShapeSignature(); + return _graph->nodes()->create<luci::CircleOutputExclude>(); } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleOutputExclude.test.cpp b/compiler/luci/service/src/Nodes/CircleOutputExclude.test.cpp new file mode 100644 index 000000000..120ffe86b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleOutputExclude.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_OutputExclude) +{ + auto g = loco::make_graph(); + auto node_outex = g->nodes()->create<luci::CircleOutputExclude>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_outex, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_outex = dynamic_cast<luci::CircleOutputExclude *>(cloned); + ASSERT_NE(nullptr, cloned_outex); +} diff --git a/compiler/luci/service/src/Nodes/CirclePRelu.cpp b/compiler/luci/service/src/Nodes/CirclePRelu.cpp new file mode 100644 index 000000000..8a34e507e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePRelu.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CirclePRelu *) +{ + return _graph->nodes()->create<luci::CirclePRelu>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CirclePRelu.test.cpp b/compiler/luci/service/src/Nodes/CirclePRelu.test.cpp new file mode 100644 index 000000000..1150e3fa4 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePRelu.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_PRelu) +{ + auto g = loco::make_graph(); + auto node_pr = g->nodes()->create<luci::CirclePRelu>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_pr, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_pr = dynamic_cast<luci::CirclePRelu *>(cloned); + ASSERT_NE(nullptr, cloned_pr); +} diff --git a/compiler/luci/service/src/Nodes/CirclePack.cpp b/compiler/luci/service/src/Nodes/CirclePack.cpp new file mode 100644 index 000000000..a3cee0bfd --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePack.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CirclePack *node) +{ + auto *cloned = _graph->nodes()->create<luci::CirclePack>(node->values_count()); + if (cloned != nullptr) + cloned->axis(node->axis()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CirclePack.test.cpp b/compiler/luci/service/src/Nodes/CirclePack.test.cpp new file mode 100644 index 000000000..b808956dc --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePack.test.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Pack) +{ + auto g = loco::make_graph(); + auto node_pack = g->nodes()->create<luci::CirclePack>(3); + node_pack->axis(7); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_pack, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_pack = dynamic_cast<luci::CirclePack *>(cloned); + ASSERT_NE(nullptr, cloned_pack); + ASSERT_EQ(node_pack->values_count(), cloned_pack->values_count()); + ASSERT_EQ(node_pack->axis(), cloned_pack->axis()); +} diff --git a/compiler/luci/service/src/Nodes/CirclePad.cpp b/compiler/luci/service/src/Nodes/CirclePad.cpp new file mode 100644 index 000000000..425bdce4d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePad.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CirclePad *) +{ + return _graph->nodes()->create<luci::CirclePad>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CirclePad.test.cpp b/compiler/luci/service/src/Nodes/CirclePad.test.cpp new file mode 100644 index 000000000..1d5f8375e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePad.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Pad) +{ + auto g = loco::make_graph(); + auto node_pad = g->nodes()->create<luci::CirclePad>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_pad, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_pad = dynamic_cast<luci::CirclePad *>(cloned); + ASSERT_NE(nullptr, cloned_pad); +} diff --git a/compiler/luci/service/src/Nodes/CirclePadV2.cpp b/compiler/luci/service/src/Nodes/CirclePadV2.cpp new file mode 100644 index 000000000..0e93869b6 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePadV2.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CirclePadV2 *) +{ + return _graph->nodes()->create<luci::CirclePadV2>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CirclePadV2.test.cpp b/compiler/luci/service/src/Nodes/CirclePadV2.test.cpp new file mode 100644 index 000000000..d011f69f8 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePadV2.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_PadV2) +{ + auto g = loco::make_graph(); + auto node_pad = g->nodes()->create<luci::CirclePadV2>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_pad, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_pad = dynamic_cast<luci::CirclePadV2 *>(cloned); + ASSERT_NE(nullptr, cloned_pad); +} diff --git a/compiler/luci/service/src/Nodes/CirclePow.cpp b/compiler/luci/service/src/Nodes/CirclePow.cpp new file mode 100644 index 000000000..bf9388913 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePow.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CirclePow *) +{ + return _graph->nodes()->create<luci::CirclePow>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CirclePow.test.cpp b/compiler/luci/service/src/Nodes/CirclePow.test.cpp new file mode 100644 index 000000000..946298932 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CirclePow.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Pow) +{ + auto g = loco::make_graph(); + auto node_pow = g->nodes()->create<luci::CirclePow>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_pow, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_pow = dynamic_cast<luci::CirclePow *>(cloned); + ASSERT_NE(nullptr, cloned_pow); +} diff --git a/compiler/luci/service/src/Nodes/CircleRange.cpp b/compiler/luci/service/src/Nodes/CircleRange.cpp new file mode 100644 index 000000000..9c6f7b494 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRange.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleRange *) +{ + return _graph->nodes()->create<luci::CircleRange>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleRange.test.cpp b/compiler/luci/service/src/Nodes/CircleRange.test.cpp new file mode 100644 index 000000000..b2fb29617 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRange.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Range) +{ + auto g = loco::make_graph(); + auto node_range = g->nodes()->create<luci::CircleRange>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_range, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_range = dynamic_cast<luci::CircleRange *>(cloned); + ASSERT_NE(nullptr, cloned_range); +} diff --git a/compiler/luci/service/src/Nodes/CircleRank.cpp b/compiler/luci/service/src/Nodes/CircleRank.cpp new file mode 100644 index 000000000..db8171c51 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRank.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleRank *) +{ + return _graph->nodes()->create<luci::CircleRank>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleRank.test.cpp b/compiler/luci/service/src/Nodes/CircleRank.test.cpp new file mode 100644 index 000000000..0e81fb254 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRank.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Rank) +{ + auto g = loco::make_graph(); + auto node_rank = g->nodes()->create<luci::CircleRank>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rank, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rank = dynamic_cast<luci::CircleRank *>(cloned); + ASSERT_NE(nullptr, cloned_rank); +} diff --git a/compiler/luci/service/src/Nodes/CircleReduceAny.cpp b/compiler/luci/service/src/Nodes/CircleReduceAny.cpp index 27da81466..3ab0b3b59 100644 --- a/compiler/luci/service/src/Nodes/CircleReduceAny.cpp +++ b/compiler/luci/service/src/Nodes/CircleReduceAny.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,15 +14,17 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleReduceAny *node) +luci::CircleNode *CloneNode::visit(const luci::CircleReduceAny *node) { - return legalized_signature( - reduced_signature(node->input(), node->reduction_indices(), node->keep_dims())); + auto *cloned = _graph->nodes()->create<luci::CircleReduceAny>(); + if (cloned != nullptr) + cloned->keep_dims(node->keep_dims()); + return cloned; } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReduceAny.test.cpp b/compiler/luci/service/src/Nodes/CircleReduceAny.test.cpp new file mode 100644 index 000000000..904b5a139 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReduceAny.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ReduceAny) +{ + auto g = loco::make_graph(); + auto node_ra = g->nodes()->create<luci::CircleReduceAny>(); + node_ra->keep_dims(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_ra, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_ra = dynamic_cast<luci::CircleReduceAny *>(cloned); + ASSERT_NE(nullptr, cloned_ra); + ASSERT_EQ(node_ra->keep_dims(), cloned_ra->keep_dims()); +} diff --git a/compiler/luci/service/src/Nodes/CircleReduceMax.cpp b/compiler/luci/service/src/Nodes/CircleReduceMax.cpp index 48d9cb970..c026905ca 100644 --- a/compiler/luci/service/src/Nodes/CircleReduceMax.cpp +++ b/compiler/luci/service/src/Nodes/CircleReduceMax.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,15 +14,17 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleReduceMax *node) +luci::CircleNode *CloneNode::visit(const luci::CircleReduceMax *node) { - return legalized_signature( - reduced_signature(node->input(), node->reduction_indices(), node->keep_dims())); + auto *cloned = _graph->nodes()->create<luci::CircleReduceMax>(); + if (cloned != nullptr) + cloned->keep_dims(node->keep_dims()); + return cloned; } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReduceMax.test.cpp b/compiler/luci/service/src/Nodes/CircleReduceMax.test.cpp new file mode 100644 index 000000000..b3f3c881e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReduceMax.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ReduceMax) +{ + auto g = loco::make_graph(); + auto node_rmax = g->nodes()->create<luci::CircleReduceMax>(); + node_rmax->keep_dims(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rmax, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rmax = dynamic_cast<luci::CircleReduceMax *>(cloned); + ASSERT_NE(nullptr, cloned_rmax); + ASSERT_EQ(node_rmax->keep_dims(), cloned_rmax->keep_dims()); +} diff --git a/compiler/luci/service/src/Nodes/CircleReduceMin.cpp b/compiler/luci/service/src/Nodes/CircleReduceMin.cpp index 9a9997118..3dfa19680 100644 --- a/compiler/luci/service/src/Nodes/CircleReduceMin.cpp +++ b/compiler/luci/service/src/Nodes/CircleReduceMin.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,15 +14,17 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleReduceMin *node) +luci::CircleNode *CloneNode::visit(const luci::CircleReduceMin *node) { - return legalized_signature( - reduced_signature(node->input(), node->reduction_indices(), node->keep_dims())); + auto *cloned = _graph->nodes()->create<luci::CircleReduceMin>(); + if (cloned != nullptr) + cloned->keep_dims(node->keep_dims()); + return cloned; } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReduceMin.test.cpp b/compiler/luci/service/src/Nodes/CircleReduceMin.test.cpp new file mode 100644 index 000000000..b3faa68da --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReduceMin.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ReduceMin) +{ + auto g = loco::make_graph(); + auto node_rmin = g->nodes()->create<luci::CircleReduceMin>(); + node_rmin->keep_dims(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rmin, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rmin = dynamic_cast<luci::CircleReduceMin *>(cloned); + ASSERT_NE(nullptr, cloned_rmin); + ASSERT_EQ(node_rmin->keep_dims(), cloned_rmin->keep_dims()); +} diff --git a/compiler/luci/service/src/Nodes/CircleReduceProd.cpp b/compiler/luci/service/src/Nodes/CircleReduceProd.cpp index a9d381a74..418a8ce32 100644 --- a/compiler/luci/service/src/Nodes/CircleReduceProd.cpp +++ b/compiler/luci/service/src/Nodes/CircleReduceProd.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,15 +14,17 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleReduceProd *node) +luci::CircleNode *CloneNode::visit(const luci::CircleReduceProd *node) { - return legalized_signature( - reduced_signature(node->input(), node->reduction_indices(), node->keep_dims())); + auto *cloned = _graph->nodes()->create<luci::CircleReduceProd>(); + if (cloned != nullptr) + cloned->keep_dims(node->keep_dims()); + return cloned; } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReduceProd.test.cpp b/compiler/luci/service/src/Nodes/CircleReduceProd.test.cpp new file mode 100644 index 000000000..8caf8e91f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReduceProd.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ReduceProd) +{ + auto g = loco::make_graph(); + auto node_rp = g->nodes()->create<luci::CircleReduceProd>(); + node_rp->keep_dims(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rp, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rp = dynamic_cast<luci::CircleReduceProd *>(cloned); + ASSERT_NE(nullptr, cloned_rp); + ASSERT_EQ(node_rp->keep_dims(), cloned_rp->keep_dims()); +} diff --git a/compiler/luci/service/src/Nodes/CircleRelu.cpp b/compiler/luci/service/src/Nodes/CircleRelu.cpp index a7a7f6f0a..7447eea0c 100644 --- a/compiler/luci/service/src/Nodes/CircleRelu.cpp +++ b/compiler/luci/service/src/Nodes/CircleRelu.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,14 +14,14 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleRelu *node) +luci::CircleNode *CloneNode::visit(const luci::CircleRelu *) { - return input_arg_signature(node, 0); + return _graph->nodes()->create<luci::CircleRelu>(); } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleRelu.test.cpp b/compiler/luci/service/src/Nodes/CircleRelu.test.cpp new file mode 100644 index 000000000..6154376ba --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRelu.test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> +#include <luci/Service/CircleTypeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, simple_relu) +{ + luci::CircleInput input; + luci::CircleRelu relu; + + input.shape({3, 4}); + input.shape_status(luci::ShapeStatus::VALID); + + relu.features(&input); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&relu, shape)); + ASSERT_EQ(2, shape.rank()); + ASSERT_EQ(3, shape.dim(0).value()); + ASSERT_EQ(4, shape.dim(1).value()); +} + +TEST(DataTypeRuleTest, simple_relu) +{ + luci::CircleInput input; + luci::CircleRelu relu; + + input.dtype(loco::DataType::S32); + + relu.features(&input); + + loco::DataType dtype; + luci::tinf::Rule type_inf_rule; + + ASSERT_TRUE(type_inf_rule.infer(&relu, dtype)); + ASSERT_EQ(loco::DataType::S32, dtype); +} + +TEST(CloneNodeTest, clone_Relu) +{ + auto g = loco::make_graph(); + auto node_relu = g->nodes()->create<luci::CircleRelu>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_relu, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_relu = dynamic_cast<luci::CircleRelu *>(cloned); + ASSERT_NE(nullptr, cloned_relu); +} diff --git a/compiler/luci/service/src/Nodes/CircleRelu6.cpp b/compiler/luci/service/src/Nodes/CircleRelu6.cpp index 92a596d08..7b98311ed 100644 --- a/compiler/luci/service/src/Nodes/CircleRelu6.cpp +++ b/compiler/luci/service/src/Nodes/CircleRelu6.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,14 +14,14 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleRelu6 *node) +luci::CircleNode *CloneNode::visit(const luci::CircleRelu6 *) { - return input_arg_signature(node, 0); + return _graph->nodes()->create<luci::CircleRelu6>(); } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleRelu6.test.cpp b/compiler/luci/service/src/Nodes/CircleRelu6.test.cpp new file mode 100644 index 000000000..213dbcb09 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRelu6.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Relu6) +{ + auto g = loco::make_graph(); + auto node_relu6 = g->nodes()->create<luci::CircleRelu6>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_relu6, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_relu6 = dynamic_cast<luci::CircleRelu6 *>(cloned); + ASSERT_NE(nullptr, cloned_relu6); +} diff --git a/compiler/luci/service/src/Nodes/CircleReluN1To1.cpp b/compiler/luci/service/src/Nodes/CircleReluN1To1.cpp index 1e8d9971d..4efedb9fc 100644 --- a/compiler/luci/service/src/Nodes/CircleReluN1To1.cpp +++ b/compiler/luci/service/src/Nodes/CircleReluN1To1.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,14 +14,14 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleReluN1To1 *node) +luci::CircleNode *CloneNode::visit(const luci::CircleReluN1To1 *) { - return input_arg_signature(node, 0); + return _graph->nodes()->create<luci::CircleReluN1To1>(); } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReluN1To1.test.cpp b/compiler/luci/service/src/Nodes/CircleReluN1To1.test.cpp new file mode 100644 index 000000000..b828e795c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReluN1To1.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ReluN1To1) +{ + auto g = loco::make_graph(); + auto node_relun1 = g->nodes()->create<luci::CircleReluN1To1>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_relun1, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_relun1 = dynamic_cast<luci::CircleReluN1To1 *>(cloned); + ASSERT_NE(nullptr, cloned_relun1); +} diff --git a/compiler/luci/service/src/Nodes/CircleReshape.cpp b/compiler/luci/service/src/Nodes/CircleReshape.cpp new file mode 100644 index 000000000..07a81b306 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReshape.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleReshape *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleReshape>(); + if (cloned != nullptr) + { + uint32_t rank = node->newShape()->rank(); + cloned->newShape()->rank(rank); + for (uint32_t r = 0; r < rank; ++r) + { + cloned->newShape()->dim(r) = node->newShape()->dim(r); + } + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReshape.test.cpp b/compiler/luci/service/src/Nodes/CircleReshape.test.cpp new file mode 100644 index 000000000..ca92b717d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReshape.test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Reshape) +{ + auto g = loco::make_graph(); + auto node_reshape = g->nodes()->create<luci::CircleReshape>(); + node_reshape->newShape()->rank(2); + node_reshape->newShape()->dim(0) = 3; + node_reshape->newShape()->dim(1) = 4; + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_reshape, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_reshape = dynamic_cast<luci::CircleReshape *>(cloned); + ASSERT_NE(nullptr, cloned_reshape); + ASSERT_EQ(node_reshape->newShape()->rank(), cloned_reshape->newShape()->rank()); + ASSERT_EQ(node_reshape->newShape()->dim(0), cloned_reshape->newShape()->dim(0)); + ASSERT_EQ(node_reshape->newShape()->dim(1), cloned_reshape->newShape()->dim(1)); +} diff --git a/compiler/luci/service/src/Nodes/CircleResizeBilinear.cpp b/compiler/luci/service/src/Nodes/CircleResizeBilinear.cpp new file mode 100644 index 000000000..55d21af45 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleResizeBilinear.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleResizeBilinear *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleResizeBilinear>(); + if (cloned != nullptr) + { + cloned->align_corners(node->align_corners()); + cloned->half_pixel_centers(node->half_pixel_centers()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleResizeBilinear.test.cpp b/compiler/luci/service/src/Nodes/CircleResizeBilinear.test.cpp new file mode 100644 index 000000000..bff71261d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleResizeBilinear.test.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, resize_bilinear_simple) +{ + luci::CircleInput input; + luci::CircleConst rb_size; + luci::CircleResizeBilinear rb; + + input.shape({1, 4, 4, 3}); + input.shape_status(luci::ShapeStatus::VALID); + + rb_size.dtype(loco::DataType::S32); + rb_size.rank(1); + rb_size.dim(0).set(2); + rb_size.size<loco::DataType::S32>(2); + rb_size.at<loco::DataType::S32>(0) = 16; + rb_size.at<loco::DataType::S32>(1) = 16; + rb_size.shape_status(luci::ShapeStatus::VALID); + + rb.input(&input); + rb.size(&rb_size); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&rb, shape)); + ASSERT_EQ(4, shape.rank()); + ASSERT_EQ(1, shape.dim(0).value()); + ASSERT_EQ(16, shape.dim(1).value()); + ASSERT_EQ(16, shape.dim(2).value()); + ASSERT_EQ(3, shape.dim(3).value()); +} + +TEST(CloneNodeTest, clone_ResizeBilinear) +{ + auto g = loco::make_graph(); + auto node_rb = g->nodes()->create<luci::CircleResizeBilinear>(); + node_rb->align_corners(true); + node_rb->half_pixel_centers(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rb, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rb = dynamic_cast<luci::CircleResizeBilinear *>(cloned); + ASSERT_NE(nullptr, cloned_rb); + ASSERT_EQ(node_rb->align_corners(), cloned_rb->align_corners()); + ASSERT_EQ(node_rb->half_pixel_centers(), cloned_rb->half_pixel_centers()); +} diff --git a/compiler/luci/service/src/Nodes/CircleResizeNearestNeighbor.cpp b/compiler/luci/service/src/Nodes/CircleResizeNearestNeighbor.cpp new file mode 100644 index 000000000..5727786a7 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleResizeNearestNeighbor.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleResizeNearestNeighbor *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleResizeNearestNeighbor>(); + if (cloned != nullptr) + cloned->align_corners(node->align_corners()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleResizeNearestNeighbor.test.cpp b/compiler/luci/service/src/Nodes/CircleResizeNearestNeighbor.test.cpp new file mode 100644 index 000000000..a1d781c65 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleResizeNearestNeighbor.test.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, resize_nearest_neighbor_simple) +{ + luci::CircleInput input; + luci::CircleConst rnn_size; + luci::CircleResizeNearestNeighbor rnn; + + input.shape({1, 4, 4, 3}); + input.shape_status(luci::ShapeStatus::VALID); + + rnn_size.dtype(loco::DataType::S32); + rnn_size.rank(1); + rnn_size.dim(0).set(2); + rnn_size.size<loco::DataType::S32>(2); + rnn_size.at<loco::DataType::S32>(0) = 16; + rnn_size.at<loco::DataType::S32>(1) = 16; + rnn_size.shape_status(luci::ShapeStatus::VALID); + + rnn.input(&input); + rnn.size(&rnn_size); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&rnn, shape)); + ASSERT_EQ(4, shape.rank()); + ASSERT_EQ(1, shape.dim(0).value()); + ASSERT_EQ(16, shape.dim(1).value()); + ASSERT_EQ(16, shape.dim(2).value()); + ASSERT_EQ(3, shape.dim(3).value()); +} + +TEST(CloneNodeTest, clone_ResizeNearestNeighbor) +{ + auto g = loco::make_graph(); + auto node_rnn = g->nodes()->create<luci::CircleResizeNearestNeighbor>(); + node_rnn->align_corners(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rnn, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rnn = dynamic_cast<luci::CircleResizeNearestNeighbor *>(cloned); + ASSERT_NE(nullptr, cloned_rnn); + ASSERT_EQ(node_rnn->align_corners(), cloned_rnn->align_corners()); +} diff --git a/compiler/luci/service/src/Nodes/CircleReverseSequence.cpp b/compiler/luci/service/src/Nodes/CircleReverseSequence.cpp new file mode 100644 index 000000000..6e6919b0c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReverseSequence.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleReverseSequence *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleReverseSequence>(); + if (cloned != nullptr) + { + cloned->seq_axis(node->seq_axis()); + cloned->batch_axis(node->batch_axis()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReverseSequence.test.cpp b/compiler/luci/service/src/Nodes/CircleReverseSequence.test.cpp new file mode 100644 index 000000000..a7a8e3949 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReverseSequence.test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ReverseSequence) +{ + auto g = loco::make_graph(); + auto node_rs = g->nodes()->create<luci::CircleReverseSequence>(); + node_rs->seq_axis(1); + node_rs->batch_axis(2); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rs, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rs = dynamic_cast<luci::CircleReverseSequence *>(cloned); + ASSERT_NE(nullptr, cloned_rs); + ASSERT_EQ(node_rs->seq_axis(), cloned_rs->seq_axis()); + ASSERT_EQ(node_rs->batch_axis(), cloned_rs->batch_axis()); +} diff --git a/compiler/luci/service/src/Nodes/CircleReverseV2.cpp b/compiler/luci/service/src/Nodes/CircleReverseV2.cpp new file mode 100644 index 000000000..e8fee6c3e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReverseV2.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleReverseV2 *) +{ + return _graph->nodes()->create<luci::CircleReverseV2>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleReverseV2.test.cpp b/compiler/luci/service/src/Nodes/CircleReverseV2.test.cpp new file mode 100644 index 000000000..0e5ff933c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleReverseV2.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ReverseV2) +{ + auto g = loco::make_graph(); + auto node_rev = g->nodes()->create<luci::CircleReverseV2>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rev, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rev = dynamic_cast<luci::CircleReverseV2 *>(cloned); + ASSERT_NE(nullptr, cloned_rev); +} diff --git a/compiler/luci/service/src/Nodes/CircleRound.cpp b/compiler/luci/service/src/Nodes/CircleRound.cpp new file mode 100644 index 000000000..2c23f2df6 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRound.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleRound *) +{ + return _graph->nodes()->create<luci::CircleRound>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleRound.test.cpp b/compiler/luci/service/src/Nodes/CircleRound.test.cpp new file mode 100644 index 000000000..2c2c3a9d0 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRound.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Round) +{ + auto g = loco::make_graph(); + auto node_rnd = g->nodes()->create<luci::CircleRound>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rnd, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rnd = dynamic_cast<luci::CircleRound *>(cloned); + ASSERT_NE(nullptr, cloned_rnd); +} diff --git a/compiler/luci/service/src/Nodes/CircleRsqrt.cpp b/compiler/luci/service/src/Nodes/CircleRsqrt.cpp new file mode 100644 index 000000000..aca702fe1 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRsqrt.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleRsqrt *) +{ + return _graph->nodes()->create<luci::CircleRsqrt>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleRsqrt.test.cpp b/compiler/luci/service/src/Nodes/CircleRsqrt.test.cpp new file mode 100644 index 000000000..3e4ced562 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleRsqrt.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Rsqrt) +{ + auto g = loco::make_graph(); + auto node_rsqrt = g->nodes()->create<luci::CircleRsqrt>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_rsqrt, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_rsqrt = dynamic_cast<luci::CircleRsqrt *>(cloned); + ASSERT_NE(nullptr, cloned_rsqrt); +} diff --git a/compiler/luci/service/src/Nodes/CircleScatterNd.cpp b/compiler/luci/service/src/Nodes/CircleScatterNd.cpp new file mode 100644 index 000000000..6c477a598 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleScatterNd.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleScatterNd *) +{ + return _graph->nodes()->create<luci::CircleScatterNd>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleScatterNd.test.cpp b/compiler/luci/service/src/Nodes/CircleScatterNd.test.cpp new file mode 100644 index 000000000..ce63603cc --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleScatterNd.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ScatterNd) +{ + auto g = loco::make_graph(); + auto node_snd = g->nodes()->create<luci::CircleScatterNd>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_snd, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_snd = dynamic_cast<luci::CircleScatterNd *>(cloned); + ASSERT_NE(nullptr, cloned_snd); +} diff --git a/compiler/luci/service/src/Nodes/CircleSegmentSum.cpp b/compiler/luci/service/src/Nodes/CircleSegmentSum.cpp new file mode 100644 index 000000000..aa4001f57 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSegmentSum.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSegmentSum *) +{ + return _graph->nodes()->create<luci::CircleSegmentSum>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSegmentSum.test.cpp b/compiler/luci/service/src/Nodes/CircleSegmentSum.test.cpp new file mode 100644 index 000000000..ff17b0745 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSegmentSum.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SegmentSum) +{ + auto g = loco::make_graph(); + auto node_ss = g->nodes()->create<luci::CircleSegmentSum>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_ss, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_ss = dynamic_cast<luci::CircleSegmentSum *>(cloned); + ASSERT_NE(nullptr, cloned_ss); +} diff --git a/compiler/luci/service/src/Nodes/CircleSelect.cpp b/compiler/luci/service/src/Nodes/CircleSelect.cpp new file mode 100644 index 000000000..71b31d33f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSelect.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSelect *) +{ + return _graph->nodes()->create<luci::CircleSelect>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSelect.test.cpp b/compiler/luci/service/src/Nodes/CircleSelect.test.cpp new file mode 100644 index 000000000..e8d631618 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSelect.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Select) +{ + auto g = loco::make_graph(); + auto node_sel = g->nodes()->create<luci::CircleSelect>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sel, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sel = dynamic_cast<luci::CircleSelect *>(cloned); + ASSERT_NE(nullptr, cloned_sel); +} diff --git a/compiler/luci/service/src/Nodes/CircleSelectV2.cpp b/compiler/luci/service/src/Nodes/CircleSelectV2.cpp new file mode 100644 index 000000000..07af40c40 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSelectV2.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSelectV2 *) +{ + return _graph->nodes()->create<luci::CircleSelectV2>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSelectV2.test.cpp b/compiler/luci/service/src/Nodes/CircleSelectV2.test.cpp new file mode 100644 index 000000000..253dba555 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSelectV2.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SelectV2) +{ + auto g = loco::make_graph(); + auto node_sel = g->nodes()->create<luci::CircleSelectV2>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sel, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sel = dynamic_cast<luci::CircleSelectV2 *>(cloned); + ASSERT_NE(nullptr, cloned_sel); +} diff --git a/compiler/luci/service/src/Nodes/CircleShape.cpp b/compiler/luci/service/src/Nodes/CircleShape.cpp new file mode 100644 index 000000000..e5b5fa28f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleShape.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleShape *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleShape>(); + if (cloned != nullptr) + cloned->out_type(node->out_type()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleShape.test.cpp b/compiler/luci/service/src/Nodes/CircleShape.test.cpp new file mode 100644 index 000000000..ec057bd05 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleShape.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Shape) +{ + auto g = loco::make_graph(); + auto node_shape = g->nodes()->create<luci::CircleShape>(); + node_shape->out_type(loco::DataType::S32); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_shape, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_shape = dynamic_cast<luci::CircleShape *>(cloned); + ASSERT_NE(nullptr, cloned_shape); + ASSERT_EQ(node_shape->out_type(), cloned_shape->out_type()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSin.cpp b/compiler/luci/service/src/Nodes/CircleSin.cpp new file mode 100644 index 000000000..46a07d21d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSin.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSin *) +{ + return _graph->nodes()->create<luci::CircleSin>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSin.test.cpp b/compiler/luci/service/src/Nodes/CircleSin.test.cpp new file mode 100644 index 000000000..b072e7e2c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSin.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Sin) +{ + auto g = loco::make_graph(); + auto node_sin = g->nodes()->create<luci::CircleSin>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sin, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sin = dynamic_cast<luci::CircleSin *>(cloned); + ASSERT_NE(nullptr, cloned_sin); +} diff --git a/compiler/luci/service/src/Nodes/CircleSlice.cpp b/compiler/luci/service/src/Nodes/CircleSlice.cpp new file mode 100644 index 000000000..6b2f4a591 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSlice.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSlice *) +{ + return _graph->nodes()->create<luci::CircleSlice>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSlice.test.cpp b/compiler/luci/service/src/Nodes/CircleSlice.test.cpp new file mode 100644 index 000000000..48ec20304 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSlice.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Slice) +{ + auto g = loco::make_graph(); + auto node_slice = g->nodes()->create<luci::CircleSlice>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_slice, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_slice = dynamic_cast<luci::CircleSlice *>(cloned); + ASSERT_NE(nullptr, cloned_slice); +} diff --git a/compiler/luci/service/src/Nodes/CircleSoftmax.cpp b/compiler/luci/service/src/Nodes/CircleSoftmax.cpp new file mode 100644 index 000000000..359d1000c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSoftmax.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSoftmax *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSoftmax>(); + if (cloned != nullptr) + cloned->beta(node->beta()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSoftmax.test.cpp b/compiler/luci/service/src/Nodes/CircleSoftmax.test.cpp new file mode 100644 index 000000000..c80b44d69 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSoftmax.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Softmax) +{ + auto g = loco::make_graph(); + auto node_sm = g->nodes()->create<luci::CircleSoftmax>(); + node_sm->beta(2.3f); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sm, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sm = dynamic_cast<luci::CircleSoftmax *>(cloned); + ASSERT_NE(nullptr, cloned_sm); + ASSERT_EQ(node_sm->beta(), cloned_sm->beta()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSpaceToBatchND.cpp b/compiler/luci/service/src/Nodes/CircleSpaceToBatchND.cpp new file mode 100644 index 000000000..feb4f3e37 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSpaceToBatchND.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSpaceToBatchND *) +{ + return _graph->nodes()->create<luci::CircleSpaceToBatchND>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSpaceToBatchND.test.cpp b/compiler/luci/service/src/Nodes/CircleSpaceToBatchND.test.cpp new file mode 100644 index 000000000..eb743795d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSpaceToBatchND.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SpaceToBatchND) +{ + auto g = loco::make_graph(); + auto node_s2bnd = g->nodes()->create<luci::CircleSpaceToBatchND>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_s2bnd, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_s2bnd = dynamic_cast<luci::CircleSpaceToBatchND *>(cloned); + ASSERT_NE(nullptr, cloned_s2bnd); +} diff --git a/compiler/luci/service/src/Nodes/CircleSpaceToDepth.cpp b/compiler/luci/service/src/Nodes/CircleSpaceToDepth.cpp new file mode 100644 index 000000000..3a82f5c7a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSpaceToDepth.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSpaceToDepth *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSpaceToDepth>(); + if (cloned != nullptr) + cloned->block_size(node->block_size()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSpaceToDepth.test.cpp b/compiler/luci/service/src/Nodes/CircleSpaceToDepth.test.cpp new file mode 100644 index 000000000..fb544e6d7 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSpaceToDepth.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SpaceToDepth) +{ + auto g = loco::make_graph(); + auto node_s2d = g->nodes()->create<luci::CircleSpaceToDepth>(); + node_s2d->block_size(32); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_s2d, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_s2d = dynamic_cast<luci::CircleSpaceToDepth *>(cloned); + ASSERT_NE(nullptr, cloned_s2d); + ASSERT_EQ(node_s2d->block_size(), cloned_s2d->block_size()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSparseToDense.cpp b/compiler/luci/service/src/Nodes/CircleSparseToDense.cpp new file mode 100644 index 000000000..3dba1a542 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSparseToDense.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSparseToDense *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSparseToDense>(); + if (cloned != nullptr) + cloned->validate_indices(node->validate_indices()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSparseToDense.test.cpp b/compiler/luci/service/src/Nodes/CircleSparseToDense.test.cpp new file mode 100644 index 000000000..177a469cd --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSparseToDense.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SparseToDense) +{ + auto g = loco::make_graph(); + auto node_s2d = g->nodes()->create<luci::CircleSparseToDense>(); + node_s2d->validate_indices(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_s2d, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_s2d = dynamic_cast<luci::CircleSparseToDense *>(cloned); + ASSERT_NE(nullptr, cloned_s2d); + ASSERT_EQ(node_s2d->validate_indices(), cloned_s2d->validate_indices()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSplit.cpp b/compiler/luci/service/src/Nodes/CircleSplit.cpp new file mode 100644 index 000000000..e68a24a1f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplit.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSplit *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSplit>(); + if (cloned != nullptr) + cloned->num_split(node->num_split()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSplit.test.cpp b/compiler/luci/service/src/Nodes/CircleSplit.test.cpp new file mode 100644 index 000000000..9ee26b425 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplit.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Split) +{ + auto g = loco::make_graph(); + auto node_split = g->nodes()->create<luci::CircleSplit>(); + node_split->num_split(5); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_split, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_split = dynamic_cast<luci::CircleSplit *>(cloned); + ASSERT_NE(nullptr, cloned_split); + ASSERT_EQ(node_split->num_split(), cloned_split->num_split()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSplitOut.cpp b/compiler/luci/service/src/Nodes/CircleSplitOut.cpp new file mode 100644 index 000000000..024598892 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplitOut.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSplitOut *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSplitOut>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSplitOut.test.cpp b/compiler/luci/service/src/Nodes/CircleSplitOut.test.cpp new file mode 100644 index 000000000..deec08804 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplitOut.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SplitOut) +{ + auto g = loco::make_graph(); + auto node_sout = g->nodes()->create<luci::CircleSplitOut>(); + node_sout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sout = dynamic_cast<luci::CircleSplitOut *>(cloned); + ASSERT_NE(nullptr, cloned_sout); + ASSERT_EQ(node_sout->index(), cloned_sout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSplitV.cpp b/compiler/luci/service/src/Nodes/CircleSplitV.cpp new file mode 100644 index 000000000..de6c6cce6 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplitV.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSplitV *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSplitV>(); + if (cloned != nullptr) + cloned->num_split(node->num_split()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSplitV.test.cpp b/compiler/luci/service/src/Nodes/CircleSplitV.test.cpp new file mode 100644 index 000000000..d109a64aa --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplitV.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SplitV) +{ + auto g = loco::make_graph(); + auto node_split = g->nodes()->create<luci::CircleSplitV>(); + node_split->num_split(5); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_split, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_split = dynamic_cast<luci::CircleSplitV *>(cloned); + ASSERT_NE(nullptr, cloned_split); + ASSERT_EQ(node_split->num_split(), cloned_split->num_split()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSplitVOut.cpp b/compiler/luci/service/src/Nodes/CircleSplitVOut.cpp new file mode 100644 index 000000000..f40eb0a47 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplitVOut.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSplitVOut *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSplitVOut>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSplitVOut.test.cpp b/compiler/luci/service/src/Nodes/CircleSplitVOut.test.cpp new file mode 100644 index 000000000..ab5e9d6be --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSplitVOut.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SplitVOut) +{ + auto g = loco::make_graph(); + auto node_sout = g->nodes()->create<luci::CircleSplitVOut>(); + node_sout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sout = dynamic_cast<luci::CircleSplitVOut *>(cloned); + ASSERT_NE(nullptr, cloned_sout); + ASSERT_EQ(node_sout->index(), cloned_sout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSqrt.cpp b/compiler/luci/service/src/Nodes/CircleSqrt.cpp new file mode 100644 index 000000000..a3e63684b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSqrt.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSqrt *) +{ + return _graph->nodes()->create<luci::CircleSqrt>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSqrt.test.cpp b/compiler/luci/service/src/Nodes/CircleSqrt.test.cpp new file mode 100644 index 000000000..dbef839d6 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSqrt.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Sqrt) +{ + auto g = loco::make_graph(); + auto node_sqrt = g->nodes()->create<luci::CircleSqrt>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sqrt, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sqrt = dynamic_cast<luci::CircleSqrt *>(cloned); + ASSERT_NE(nullptr, cloned_sqrt); +} diff --git a/compiler/luci/service/src/Nodes/CircleSquare.cpp b/compiler/luci/service/src/Nodes/CircleSquare.cpp new file mode 100644 index 000000000..88bbed76c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSquare.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSquare *) +{ + return _graph->nodes()->create<luci::CircleSquare>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSquare.test.cpp b/compiler/luci/service/src/Nodes/CircleSquare.test.cpp new file mode 100644 index 000000000..67ac21210 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSquare.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Square) +{ + auto g = loco::make_graph(); + auto node_squ = g->nodes()->create<luci::CircleSquare>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_squ, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_squ = dynamic_cast<luci::CircleSquare *>(cloned); + ASSERT_NE(nullptr, cloned_squ); +} diff --git a/compiler/luci/service/src/Nodes/CircleSquaredDifference.cpp b/compiler/luci/service/src/Nodes/CircleSquaredDifference.cpp new file mode 100644 index 000000000..6becdf1c9 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSquaredDifference.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSquaredDifference *) +{ + return _graph->nodes()->create<luci::CircleSquaredDifference>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSquaredDifference.test.cpp b/compiler/luci/service/src/Nodes/CircleSquaredDifference.test.cpp new file mode 100644 index 000000000..26099612b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSquaredDifference.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_SquaredDifference) +{ + auto g = loco::make_graph(); + auto node_sd = g->nodes()->create<luci::CircleSquaredDifference>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sd, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sd = dynamic_cast<luci::CircleSquaredDifference *>(cloned); + ASSERT_NE(nullptr, cloned_sd); +} diff --git a/compiler/luci/service/src/Nodes/CircleSqueeze.cpp b/compiler/luci/service/src/Nodes/CircleSqueeze.cpp new file mode 100644 index 000000000..02ba5020c --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSqueeze.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSqueeze *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleSqueeze>(); + if (cloned != nullptr) + cloned->squeeze_dims(node->squeeze_dims()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSqueeze.test.cpp b/compiler/luci/service/src/Nodes/CircleSqueeze.test.cpp new file mode 100644 index 000000000..bc73eafa7 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSqueeze.test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, squeeze_simple) +{ + luci::CircleInput input; + luci::CircleSqueeze squeeze; + + input.shape({1, 4, 3, 1}); + input.shape_status(luci::ShapeStatus::VALID); + + squeeze.input(&input); + squeeze.squeeze_dims({0}); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&squeeze, shape)); + ASSERT_EQ(3, shape.rank()); + ASSERT_EQ(4, shape.dim(0).value()); + ASSERT_EQ(3, shape.dim(1).value()); + ASSERT_EQ(1, shape.dim(2).value()); +} + +TEST(ShapeRuleTest, squeeze_all) +{ + luci::CircleInput input; + luci::CircleSqueeze squeeze; + + input.shape({1, 4, 3, 1}); + input.shape_status(luci::ShapeStatus::VALID); + + squeeze.input(&input); + squeeze.squeeze_dims({}); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&squeeze, shape)); + ASSERT_EQ(2, shape.rank()); + ASSERT_EQ(4, shape.dim(0).value()); + ASSERT_EQ(3, shape.dim(1).value()); +} + +TEST(CloneNodeTest, clone_Squeeze) +{ + auto g = loco::make_graph(); + auto node_squ = g->nodes()->create<luci::CircleSqueeze>(); + node_squ->squeeze_dims({2, 3}); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_squ, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_squ = dynamic_cast<luci::CircleSqueeze *>(cloned); + ASSERT_NE(nullptr, cloned_squ); + ASSERT_EQ(node_squ->squeeze_dims().size(), cloned_squ->squeeze_dims().size()); + for (size_t s = 0; s < node_squ->squeeze_dims().size(); ++s) + ASSERT_EQ(node_squ->squeeze_dims().at(s), cloned_squ->squeeze_dims().at(s)); +} diff --git a/compiler/luci/service/src/Nodes/CircleStridedSlice.cpp b/compiler/luci/service/src/Nodes/CircleStridedSlice.cpp new file mode 100644 index 000000000..c4d199316 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleStridedSlice.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleStridedSlice *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleStridedSlice>(); + if (cloned != nullptr) + { + cloned->begin_mask(node->begin_mask()); + cloned->end_mask(node->end_mask()); + cloned->ellipsis_mask(node->ellipsis_mask()); + cloned->new_axis_mask(node->new_axis_mask()); + cloned->shrink_axis_mask(node->shrink_axis_mask()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleStridedSlice.test.cpp b/compiler/luci/service/src/Nodes/CircleStridedSlice.test.cpp new file mode 100644 index 000000000..d633f3022 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleStridedSlice.test.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_StridedSlice) +{ + auto g = loco::make_graph(); + auto node_ss = g->nodes()->create<luci::CircleStridedSlice>(); + node_ss->begin_mask(1); + node_ss->end_mask(2); + node_ss->ellipsis_mask(3); + node_ss->new_axis_mask(4); + node_ss->shrink_axis_mask(5); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_ss, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_ss = dynamic_cast<luci::CircleStridedSlice *>(cloned); + ASSERT_NE(nullptr, cloned_ss); + ASSERT_EQ(node_ss->begin_mask(), cloned_ss->begin_mask()); + ASSERT_EQ(node_ss->end_mask(), cloned_ss->end_mask()); + ASSERT_EQ(node_ss->ellipsis_mask(), cloned_ss->ellipsis_mask()); + ASSERT_EQ(node_ss->new_axis_mask(), cloned_ss->new_axis_mask()); + ASSERT_EQ(node_ss->shrink_axis_mask(), cloned_ss->shrink_axis_mask()); +} diff --git a/compiler/luci/service/src/Nodes/CircleSub.cpp b/compiler/luci/service/src/Nodes/CircleSub.cpp new file mode 100644 index 000000000..fb4bab19a --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSub.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleSub *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleSub>(); + if (cloned != nullptr) + cloned->fusedActivationFunction(node->fusedActivationFunction()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSub.test.cpp b/compiler/luci/service/src/Nodes/CircleSub.test.cpp new file mode 100644 index 000000000..e6bd7b8ff --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSub.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Sub) +{ + auto g = loco::make_graph(); + auto node_sub = g->nodes()->create<luci::CircleSub>(); + node_sub->fusedActivationFunction(luci::FusedActFunc::RELU); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sub, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sub = dynamic_cast<luci::CircleSub *>(cloned); + ASSERT_NE(nullptr, cloned_sub); + ASSERT_EQ(node_sub->fusedActivationFunction(), cloned_sub->fusedActivationFunction()); +} + +TEST(CloneNodeTest, clone_Sub_NEG) +{ + auto g = loco::make_graph(); + auto node_sub = g->nodes()->create<luci::CircleSub>(); + node_sub->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sub, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleSum.cpp b/compiler/luci/service/src/Nodes/CircleSum.cpp index 9ef90e8e0..29e6ee5f1 100644 --- a/compiler/luci/service/src/Nodes/CircleSum.cpp +++ b/compiler/luci/service/src/Nodes/CircleSum.cpp @@ -1,11 +1,11 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (c) 2021 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 + * 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, @@ -14,15 +14,17 @@ * limitations under the License. */ -#include <luci/Service/CircleShapeSignatureInference.h> +#include "CircleCloneNode.h" namespace luci { -ShapeSignature ssinf::Algorithm::visit(const luci::CircleSum *node) +luci::CircleNode *CloneNode::visit(const luci::CircleSum *node) { - return legalized_signature( - reduced_signature(node->input(), node->reduction_indices(), node->keep_dims())); + auto *cloned = _graph->nodes()->create<luci::CircleSum>(); + if (cloned != nullptr) + cloned->keep_dims(node->keep_dims()); + return cloned; } } // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleSum.test.cpp b/compiler/luci/service/src/Nodes/CircleSum.test.cpp new file mode 100644 index 000000000..aa1b0d128 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleSum.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Sum) +{ + auto g = loco::make_graph(); + auto node_sum = g->nodes()->create<luci::CircleSum>(); + node_sum->keep_dims(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_sum, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_sum = dynamic_cast<luci::CircleSum *>(cloned); + ASSERT_NE(nullptr, cloned_sum); + ASSERT_EQ(node_sum->keep_dims(), cloned_sum->keep_dims()); +} diff --git a/compiler/luci/service/src/Nodes/CircleTanh.cpp b/compiler/luci/service/src/Nodes/CircleTanh.cpp new file mode 100644 index 000000000..9cb35932f --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTanh.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleTanh *) +{ + return _graph->nodes()->create<luci::CircleTanh>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleTanh.test.cpp b/compiler/luci/service/src/Nodes/CircleTanh.test.cpp new file mode 100644 index 000000000..0215b42ca --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTanh.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Tanh) +{ + auto g = loco::make_graph(); + auto node_tanh = g->nodes()->create<luci::CircleTanh>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_tanh, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_tanh = dynamic_cast<luci::CircleTanh *>(cloned); + ASSERT_NE(nullptr, cloned_tanh); +} diff --git a/compiler/luci/service/src/Nodes/CircleTile.cpp b/compiler/luci/service/src/Nodes/CircleTile.cpp new file mode 100644 index 000000000..21c32e021 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTile.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleTile *) +{ + return _graph->nodes()->create<luci::CircleTile>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleTile.test.cpp b/compiler/luci/service/src/Nodes/CircleTile.test.cpp new file mode 100644 index 000000000..089c86ccb --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTile.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Tile) +{ + auto g = loco::make_graph(); + auto node_tile = g->nodes()->create<luci::CircleTile>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_tile, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_tile = dynamic_cast<luci::CircleTile *>(cloned); + ASSERT_NE(nullptr, cloned_tile); +} diff --git a/compiler/luci/service/src/Nodes/CircleTopKV2.cpp b/compiler/luci/service/src/Nodes/CircleTopKV2.cpp new file mode 100644 index 000000000..e940c03dd --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTopKV2.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleTopKV2 *) +{ + return _graph->nodes()->create<luci::CircleTopKV2>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleTopKV2.test.cpp b/compiler/luci/service/src/Nodes/CircleTopKV2.test.cpp new file mode 100644 index 000000000..7f68a408d --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTopKV2.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_TopKV2) +{ + auto g = loco::make_graph(); + auto node_top = g->nodes()->create<luci::CircleTopKV2>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_top, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_top = dynamic_cast<luci::CircleTopKV2 *>(cloned); + ASSERT_NE(nullptr, cloned_top); +} diff --git a/compiler/luci/service/src/Nodes/CircleTopKV2Out.cpp b/compiler/luci/service/src/Nodes/CircleTopKV2Out.cpp new file mode 100644 index 000000000..5c13f2be1 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTopKV2Out.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleTopKV2Out *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleTopKV2Out>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleTopKV2Out.test.cpp b/compiler/luci/service/src/Nodes/CircleTopKV2Out.test.cpp new file mode 100644 index 000000000..cfba61f10 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTopKV2Out.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_TopKV2Out) +{ + auto g = loco::make_graph(); + auto node_tout = g->nodes()->create<luci::CircleTopKV2Out>(); + node_tout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_tout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_tout = dynamic_cast<luci::CircleTopKV2Out *>(cloned); + ASSERT_NE(nullptr, cloned_tout); + ASSERT_EQ(node_tout->index(), cloned_tout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleTranspose.cpp b/compiler/luci/service/src/Nodes/CircleTranspose.cpp new file mode 100644 index 000000000..81db55269 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTranspose.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleTranspose *) +{ + return _graph->nodes()->create<luci::CircleTranspose>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleTranspose.test.cpp b/compiler/luci/service/src/Nodes/CircleTranspose.test.cpp new file mode 100644 index 000000000..9447d1a5b --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTranspose.test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/Service/CircleShapeInference.h> + +#include <loco/IR/TensorShape.h> + +#include <gtest/gtest.h> + +TEST(ShapeRuleTest, transpose_simple) +{ + luci::CircleInput input; + luci::CircleConst perm; + luci::CircleTranspose transpose; + + input.shape({3, 8, 1}); + input.shape_status(luci::ShapeStatus::VALID); + + perm.dtype(loco::DataType::S32); + perm.rank(1); + perm.dim(0).set(3); + perm.size<loco::DataType::S32>(3); + perm.at<loco::DataType::S32>(0) = 1; + perm.at<loco::DataType::S32>(1) = 2; + perm.at<loco::DataType::S32>(2) = 0; + perm.shape_status(luci::ShapeStatus::VALID); + + transpose.a(&input); + transpose.perm(&perm); + + loco::TensorShape shape; + luci::sinf::Rule shape_inf_rule; + + ASSERT_TRUE(shape_inf_rule.infer(&transpose, shape)); + ASSERT_EQ(3, shape.rank()); + ASSERT_EQ(8, shape.dim(0).value()); + ASSERT_EQ(1, shape.dim(1).value()); + ASSERT_EQ(3, shape.dim(2).value()); +} + +TEST(CloneNodeTest, clone_Transpose) +{ + auto g = loco::make_graph(); + auto node_tr = g->nodes()->create<luci::CircleTranspose>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_tr, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_tr = dynamic_cast<luci::CircleTranspose *>(cloned); + ASSERT_NE(nullptr, cloned_tr); +} diff --git a/compiler/luci/service/src/Nodes/CircleTransposeConv.cpp b/compiler/luci/service/src/Nodes/CircleTransposeConv.cpp new file mode 100644 index 000000000..1fe41bdb2 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTransposeConv.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleTransposeConv *node) +{ + if (node->padding() == luci::Padding::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleTransposeConv>(); + if (cloned != nullptr) + { + cloned->padding(node->padding()); + cloned->stride()->h(node->stride()->h()); + cloned->stride()->w(node->stride()->w()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleTransposeConv.test.cpp b/compiler/luci/service/src/Nodes/CircleTransposeConv.test.cpp new file mode 100644 index 000000000..29a656c03 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleTransposeConv.test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_TransposeConv) +{ + auto g = loco::make_graph(); + auto node_trconv = g->nodes()->create<luci::CircleTransposeConv>(); + node_trconv->padding(luci::Padding::SAME); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_trconv, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_trconv = dynamic_cast<luci::CircleTransposeConv *>(cloned); + ASSERT_NE(nullptr, cloned_trconv); + ASSERT_EQ(node_trconv->padding(), cloned_trconv->padding()); +} + +TEST(CloneNodeTest, clone_TransposeConv_padding_NEG) +{ + auto g = loco::make_graph(); + auto node_trconv = g->nodes()->create<luci::CircleTransposeConv>(); + node_trconv->padding(luci::Padding::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_trconv, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp b/compiler/luci/service/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp new file mode 100644 index 000000000..12205f3b0 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnidirectionalSequenceLSTM.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleUnidirectionalSequenceLSTM *node) +{ + if (node->fusedActivationFunction() == luci::FusedActFunc::UNDEFINED) + return nullptr; + + auto *cloned = _graph->nodes()->create<luci::CircleUnidirectionalSequenceLSTM>(); + if (cloned != nullptr) + { + cloned->fusedActivationFunction(node->fusedActivationFunction()); + cloned->cell_clip(node->cell_clip()); + cloned->proj_clip(node->proj_clip()); + cloned->time_major(node->time_major()); + cloned->asymmetric_quantize_inputs(node->asymmetric_quantize_inputs()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp b/compiler/luci/service/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp new file mode 100644 index 000000000..c3816ab27 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnidirectionalSequenceLSTM.test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_UnidirectionalSequenceLSTM) +{ + auto g = loco::make_graph(); + auto node_uslstm = g->nodes()->create<luci::CircleUnidirectionalSequenceLSTM>(); + node_uslstm->fusedActivationFunction(luci::FusedActFunc::RELU); + node_uslstm->cell_clip(1.1f); + node_uslstm->proj_clip(2.2f); + node_uslstm->time_major(true); + node_uslstm->asymmetric_quantize_inputs(true); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_uslstm, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_uslstm = dynamic_cast<luci::CircleUnidirectionalSequenceLSTM *>(cloned); + ASSERT_NE(nullptr, cloned_uslstm); + ASSERT_EQ(node_uslstm->fusedActivationFunction(), cloned_uslstm->fusedActivationFunction()); + ASSERT_EQ(node_uslstm->cell_clip(), cloned_uslstm->cell_clip()); + ASSERT_EQ(node_uslstm->proj_clip(), cloned_uslstm->proj_clip()); + ASSERT_EQ(node_uslstm->time_major(), cloned_uslstm->time_major()); + ASSERT_EQ(node_uslstm->asymmetric_quantize_inputs(), cloned_uslstm->asymmetric_quantize_inputs()); +} + +TEST(CloneNodeTest, clone_UnidirectionalSequenceLSTM_NEG) +{ + auto g = loco::make_graph(); + auto node_uslstm = g->nodes()->create<luci::CircleUnidirectionalSequenceLSTM>(); + node_uslstm->fusedActivationFunction(luci::FusedActFunc::UNDEFINED); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_uslstm, gc.get()); + ASSERT_EQ(nullptr, cloned); +} diff --git a/compiler/luci/service/src/Nodes/CircleUnique.cpp b/compiler/luci/service/src/Nodes/CircleUnique.cpp new file mode 100644 index 000000000..bde2ea0dc --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnique.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleUnique *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleUnique>(); + if (cloned != nullptr) + cloned->idx_out_type(node->idx_out_type()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleUnique.test.cpp b/compiler/luci/service/src/Nodes/CircleUnique.test.cpp new file mode 100644 index 000000000..a8ff9eade --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnique.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Unique) +{ + auto g = loco::make_graph(); + auto node_uniq = g->nodes()->create<luci::CircleUnique>(); + node_uniq->idx_out_type(loco::DataType::S32); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_uniq, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_uniq = dynamic_cast<luci::CircleUnique *>(cloned); + ASSERT_NE(nullptr, cloned_uniq); + ASSERT_EQ(node_uniq->idx_out_type(), cloned_uniq->idx_out_type()); +} diff --git a/compiler/luci/service/src/Nodes/CircleUniqueOut.cpp b/compiler/luci/service/src/Nodes/CircleUniqueOut.cpp new file mode 100644 index 000000000..30093f9db --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUniqueOut.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleUniqueOut *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleUniqueOut>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleUniqueOut.test.cpp b/compiler/luci/service/src/Nodes/CircleUniqueOut.test.cpp new file mode 100644 index 000000000..780ad4b78 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUniqueOut.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_UniqueOut) +{ + auto g = loco::make_graph(); + auto node_uout = g->nodes()->create<luci::CircleUniqueOut>(); + node_uout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_uout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_uout = dynamic_cast<luci::CircleUniqueOut *>(cloned); + ASSERT_NE(nullptr, cloned_uout); + ASSERT_EQ(node_uout->index(), cloned_uout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleUnpack.cpp b/compiler/luci/service/src/Nodes/CircleUnpack.cpp new file mode 100644 index 000000000..f9d61c426 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnpack.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleUnpack *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleUnpack>(); + if (cloned != nullptr) + { + cloned->num(node->num()); + cloned->axis(node->axis()); + } + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleUnpack.test.cpp b/compiler/luci/service/src/Nodes/CircleUnpack.test.cpp new file mode 100644 index 000000000..6559a9276 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnpack.test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Unpack) +{ + auto g = loco::make_graph(); + auto node_unp = g->nodes()->create<luci::CircleUnpack>(); + node_unp->num(1); + node_unp->axis(2); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_unp, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_unp = dynamic_cast<luci::CircleUnpack *>(cloned); + ASSERT_NE(nullptr, cloned_unp); + ASSERT_EQ(node_unp->num(), cloned_unp->num()); + ASSERT_EQ(node_unp->axis(), cloned_unp->axis()); +} diff --git a/compiler/luci/service/src/Nodes/CircleUnpackOut.cpp b/compiler/luci/service/src/Nodes/CircleUnpackOut.cpp new file mode 100644 index 000000000..342d5daca --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnpackOut.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleUnpackOut *node) +{ + auto *cloned = _graph->nodes()->create<luci::CircleUnpackOut>(); + if (cloned != nullptr) + cloned->index(node->index()); + return cloned; +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleUnpackOut.test.cpp b/compiler/luci/service/src/Nodes/CircleUnpackOut.test.cpp new file mode 100644 index 000000000..ec9bb974e --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleUnpackOut.test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_UnpackOut) +{ + auto g = loco::make_graph(); + auto node_uout = g->nodes()->create<luci::CircleUnpackOut>(); + node_uout->index(1); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_uout, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_uout = dynamic_cast<luci::CircleUnpackOut *>(cloned); + ASSERT_NE(nullptr, cloned_uout); + ASSERT_EQ(node_uout->index(), cloned_uout->index()); +} diff --git a/compiler/luci/service/src/Nodes/CircleWhere.cpp b/compiler/luci/service/src/Nodes/CircleWhere.cpp new file mode 100644 index 000000000..73f4b64ac --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleWhere.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleWhere *) +{ + return _graph->nodes()->create<luci::CircleWhere>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleWhere.test.cpp b/compiler/luci/service/src/Nodes/CircleWhere.test.cpp new file mode 100644 index 000000000..352719d85 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleWhere.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_Where) +{ + auto g = loco::make_graph(); + auto node_wh = g->nodes()->create<luci::CircleWhere>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_wh, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_wh = dynamic_cast<luci::CircleWhere *>(cloned); + ASSERT_NE(nullptr, cloned_wh); +} diff --git a/compiler/luci/service/src/Nodes/CircleZerosLike.cpp b/compiler/luci/service/src/Nodes/CircleZerosLike.cpp new file mode 100644 index 000000000..2ee455857 --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleZerosLike.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 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 "CircleCloneNode.h" + +namespace luci +{ + +luci::CircleNode *CloneNode::visit(const luci::CircleZerosLike *) +{ + return _graph->nodes()->create<luci::CircleZerosLike>(); +} + +} // namespace luci diff --git a/compiler/luci/service/src/Nodes/CircleZerosLike.test.cpp b/compiler/luci/service/src/Nodes/CircleZerosLike.test.cpp new file mode 100644 index 000000000..6e0a4b3be --- /dev/null +++ b/compiler/luci/service/src/Nodes/CircleZerosLike.test.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 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/CircleNodeClone.h" + +#include <gtest/gtest.h> + +TEST(CloneNodeTest, clone_ZerosLike) +{ + auto g = loco::make_graph(); + auto node_zl = g->nodes()->create<luci::CircleZerosLike>(); + + auto gc = loco::make_graph(); + auto cloned = luci::clone_node(node_zl, gc.get()); + ASSERT_NE(nullptr, cloned); + ASSERT_EQ(gc.get(), cloned->graph()); + + auto cloned_zl = dynamic_cast<luci::CircleZerosLike *>(cloned); + ASSERT_NE(nullptr, cloned_zl); +} diff --git a/compiler/luci/service/src/ShapeDescription.cpp b/compiler/luci/service/src/ShapeDescription.cpp index 01a638f8f..adfb7e342 100644 --- a/compiler/luci/service/src/ShapeDescription.cpp +++ b/compiler/luci/service/src/ShapeDescription.cpp @@ -31,7 +31,7 @@ ShapeDescription to_shape_description(const luci::CircleNode *circle_node) res._dims.resize(circle_node->rank()); for (uint32_t i = 0; i < circle_node->rank(); ++i) - res._dims.at(i) = circle_node->dim(i).value(); + res._dims.at(i) = circle_node->dim(i).known() ? circle_node->dim(i).value() : -1; return res; } @@ -53,95 +53,12 @@ ShapeDescription to_shape_description(const loco::TensorShape &shape) return res; } -ShapeDescription to_shape_description(const loco::FeatureShape &shape) -{ - ShapeDescription res; - - res._rank_known = true; - - // T/F Lite encodes a feature map as a NHWC tensor - res._dims.resize(4); - res._dims.at(0) = shape.count().value(); - res._dims.at(1) = shape.height().value(); - res._dims.at(2) = shape.width().value(); - res._dims.at(3) = shape.depth().value(); - - return res; -} - -ShapeDescription to_shape_description(const loco::FilterShape &shape) -{ - ShapeDescription res; - - res._rank_known = true; - - // T/F Lite encodes a convolution filter as a NHWC tensor - res._dims.resize(4); - res._dims.at(0) = shape.count().value(); - res._dims.at(1) = shape.height().value(); - res._dims.at(2) = shape.width().value(); - res._dims.at(3) = shape.depth().value(); - - return res; -} - -ShapeDescription to_shape_description(const loco::DepthwiseFilterShape &shape) -{ - ShapeDescription res; - - res._rank_known = true; - - // T/F Lite encodes a depthwise convolution filter as a [1, H, W, C*M] tensor - res._dims.resize(4); - res._dims.at(0) = 1; - res._dims.at(1) = shape.height().value(); - res._dims.at(2) = shape.width().value(); - res._dims.at(3) = shape.depth().value() * shape.multiplier().value(); - - return res; -} - -ShapeDescription to_shape_description(const loco::BiasShape &shape) -{ - ShapeDescription res; - - res._rank_known = true; - - res._dims.resize(1); - res._dims.at(0) = shape.length().value(); - - return res; -} - -ShapeDescription to_shape_description(const loco::MatrixShape &shape) -{ - ShapeDescription res; - - res._rank_known = true; - - res._dims.resize(2); - res._dims.at(0) = shape.height().value(); - res._dims.at(1) = shape.width().value(); - - return res; -} - ShapeDescription to_shape_description(const loco::NodeShape &shape) { switch (shape.domain()) { case loco::Domain::Tensor: return to_shape_description(shape.as<loco::TensorShape>()); - case loco::Domain::Feature: - return to_shape_description(shape.as<loco::FeatureShape>()); - case loco::Domain::Filter: - return to_shape_description(shape.as<loco::FilterShape>()); - case loco::Domain::DepthwiseFilter: - return to_shape_description(shape.as<loco::DepthwiseFilterShape>()); - case loco::Domain::Bias: - return to_shape_description(shape.as<loco::BiasShape>()); - case loco::Domain::Matrix: - return to_shape_description(shape.as<loco::MatrixShape>()); default: break; } diff --git a/compiler/luci/service/src/ShapeDescription.test.cpp b/compiler/luci/service/src/ShapeDescription.test.cpp new file mode 100644 index 000000000..6e53aac75 --- /dev/null +++ b/compiler/luci/service/src/ShapeDescription.test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 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/ShapeDescription.h" + +#include <luci/IR/CircleNode.h> +#include <luci/IR/Nodes/CircleConst.h> + +#include <gtest/gtest.h> + +TEST(ShapeDescriptionTest, CircleNode) +{ + // Use CircleConst as CircleNode + luci::CircleConst circle_const; + circle_const.shape({1, 2, 3, 4}); + + auto sd = luci::to_shape_description(&circle_const); + + ASSERT_EQ(4, sd._dims.size()); + ASSERT_EQ(1, sd._dims.at(0)); + ASSERT_TRUE(sd._rank_known); +} + +TEST(ShapeDescriptionTest, TensorShape) +{ + loco::TensorShape tensor_shape{1, 2, 3, 4}; + loco::NodeShape node_shape(tensor_shape); + + auto sd = luci::to_shape_description(node_shape); + + ASSERT_EQ(4, sd._dims.size()); + ASSERT_EQ(1, sd._dims.at(0)); + ASSERT_TRUE(sd._rank_known); +} + +TEST(ShapeDescriptionTest, BiasShape_NEG) +{ + loco::BiasShape bias_shape; + bias_shape.length() = 1; + loco::NodeShape node_shape(bias_shape); + + EXPECT_THROW(luci::to_shape_description(node_shape), std::exception); +} diff --git a/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp index 341201148..c5864f938 100644 --- a/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp +++ b/compiler/luci/service/src/ShapeInfer_StridedSlice.cpp @@ -17,12 +17,12 @@ #include "ShapeInfer_StridedSlice.h" #include "Check.h" +#include "CircleShapeInferenceHelper.h" #include <luci/IR/CircleNode.h> #include <loco/IR/DataType.h> #include <loco/IR/NodeShape.h> #include <oops/InternalExn.h> -#include <loco/Service/ShapeInference.h> #include <cmath> #include <cstdint> @@ -245,7 +245,7 @@ loco::TensorShape infer_output_shape(const CircleStridedSlice *node) assert(node->new_axis_mask() == 0); auto op_params = BuildStridedSliceParams(node); - loco::TensorShape input_shape = loco::shape_get(input_node).as<loco::TensorShape>(); + loco::TensorShape input_shape = luci::shape_get(input_node).as<loco::TensorShape>(); uint32_t num_input_axes = input_shape.rank(); assert(begin_node->size<S32>() <= num_input_axes); diff --git a/compiler/luci/service/src/Validate.cpp b/compiler/luci/service/src/Validate.cpp index 3f732b6fe..7ed14c356 100644 --- a/compiler/luci/service/src/Validate.cpp +++ b/compiler/luci/service/src/Validate.cpp @@ -20,10 +20,9 @@ #include <luci/Log.h> #include <loco/IR/NodeShape.h> -#include <loco/Service/ShapeInference.h> -#include <loco/Service/TypeInference.h> #include <cassert> +#include <unordered_map> #include <vector> namespace @@ -36,7 +35,11 @@ std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape { if (r) os << ","; - os << tensor_shape.dim(r).value(); + + if (tensor_shape.dim(r).known()) + os << tensor_shape.dim(r).value(); + else + os << "?"; } os << "]"; return os; @@ -49,7 +52,11 @@ std::ostream &operator<<(std::ostream &os, const luci::CircleNode *circle_node) { if (r) os << ","; - os << circle_node->dim(r).value(); + + if (circle_node->dim(r).known()) + os << circle_node->dim(r).value(); + else + os << "?"; } os << "]"; return os; @@ -99,10 +106,24 @@ bool validate_shape_dtype(loco::Graph *g) auto go_tensor_shape = graph_out->shape(); assert(go_tensor_shape); + // NOTE Even if shape of graph output is [] (which means "shape inference was impossible") + // but shape of CircleNode is not, it can be valid case because shape inference + // algorithm of CircleNode may be upgraded than before. The opposite is possible either. + // If such cases are appeared, following validation code should be fixed. bool is_shape_valid = (circle_node->rank() == go_tensor_shape->rank()); for (uint32_t i = 0; is_shape_valid && i < circle_node->rank(); ++i) - if (circle_node->dim(i).value() != go_tensor_shape->dim(i).value()) + { + if (!circle_node->dim(i).known() || !go_tensor_shape->dim(i).known()) + { + // If at least one of two dimensions is unknown, + // the unknown dimension can accept any value. + INFO(l) << "Unknown dimension is matched with known dimension" << std::endl; + } + else if (circle_node->dim(i).value() != go_tensor_shape->dim(i).value()) + { is_shape_valid = false; + } + } if (is_shape_valid == false) { @@ -124,72 +145,62 @@ bool validate_shape_dtype(loco::Graph *g) return true; } -bool validate_shape_signature(loco::Graph *g) -{ - LOGGER(l); - - for (auto node : loco::postorder_traversal(loco::output_nodes(g))) - { - auto circle_node = loco::must_cast<luci::CircleNode *>(node); - const auto shape_signature = circle_node->shape_signature(); +} // namespace - if (shape_signature.rank() == 0) - continue; +namespace luci +{ - // Rank of shape and shape signature should be same - if (circle_node->rank() != shape_signature.rank()) - { - INFO(l) << "[luci] Rank of shape signature for " << circle_node->name() << " do not match" - << std::endl; - return false; - } +bool validate(loco::Graph *g) +{ + if (!loco::valid(g)) + return false; - bool has_unknown = false; + if (!validate_shape_dtype(g)) + return false; - // If shape siganture is not -1, dimension value should be same - for (uint32_t d = 0; d < shape_signature.rank(); ++d) - { - if (shape_signature.dim(d) != -1 && - shape_signature.dim(d) != (int32_t)(circle_node->dim(d).value())) - { - INFO(l) << "[luci] Dimension " << d << "of shape signature for " << circle_node->name() - << " do not match" << std::endl; - return false; - } + // TODO add more validation - if (shape_signature.dim(d) == -1) - has_unknown = true; - } + return true; +} - // Shape signature should have at least one -1 value. - if (!has_unknown) - { - INFO(l) << "[luci] Shape signature in " << circle_node->name() - << " do not have unknown dimension" << std::endl; +bool validate_name(loco::Graph *g) +{ + auto nodes = g->nodes(); + for (uint32_t n = 0; n < nodes->size(); ++n) + { + auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n)); + auto name = node->name(); + if (name.empty()) return false; - } } return true; } -} // namespace - -namespace luci +bool validate_unique_name(luci::Module *m) { + std::unordered_map<std::string, bool> names_col; -bool validate(loco::Graph *g) -{ - if (!loco::valid(g)) - return false; - - if (!validate_shape_dtype(g)) - return false; - - if (!validate_shape_signature(g)) - return false; + for (size_t g = 0; g < m->size(); ++g) + { + auto graph = m->graph(g); + auto nodes = graph->nodes(); + for (uint32_t n = 0; n < nodes->size(); ++n) + { + auto node = loco::must_cast<luci::CircleNode *>(nodes->at(n)); + // skip CircleOutput as it may have same name with from() node + auto output = dynamic_cast<luci::CircleOutput *>(node); + if (output != nullptr) + continue; + + auto name = node->name(); + auto it = names_col.find(name); + if (it != names_col.end()) + return false; - // TODO add more validation + names_col[name] = true; + } + } return true; } diff --git a/compiler/luci/service/src/Validate.test.cpp b/compiler/luci/service/src/Validate.test.cpp new file mode 100644 index 000000000..8ce6d895b --- /dev/null +++ b/compiler/luci/service/src/Validate.test.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 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/Validate.h" + +#include <luci/test/TestIOGraph.h> + +#include <luci/IR/Nodes/CircleAdd.h> +#include <luci/IR/Nodes/CircleSqrt.h> + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->dtype(loco::DataType::S32); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class SqrtGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + SqrtGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + SqrtGraphlet::init(g(), shape); + + _sqrt->x(input()); + + output()->from(_sqrt); + + // set output name to _sqrt: CircleOutput may have duplicate name + output()->name(_sqrt->name()); + } +}; + +class Sqrt2xGraphlet +{ +public: + Sqrt2xGraphlet() = default; + +public: + void init(loco::Graph *g, const ShapeU32 input_shape) + { + _sqrt1 = g->nodes()->create<luci::CircleSqrt>(); + _sqrt1->dtype(loco::DataType::S32); + _sqrt1->name("sqrt"); + + _sqrt2 = g->nodes()->create<luci::CircleSqrt>(); + _sqrt2->dtype(loco::DataType::S32); + _sqrt2->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt1 = nullptr; + luci::CircleSqrt *_sqrt2 = nullptr; +}; + +class Sqrt2xGraph : public TestIOGraph, public Sqrt2xGraphlet +{ +public: + Sqrt2xGraph() = default; + +public: + void init(const ShapeU32 shape) + { + TestIOGraph::init(shape, shape); + Sqrt2xGraphlet::init(g(), shape); + + _sqrt1->x(input()); + + _sqrt2->x(_sqrt1); + + output()->from(_sqrt2); + } +}; + +} // namespace + +TEST(ValidateTest, non_empty_name) +{ + SqrtGraph g; + g.init({3, 3}); + + ASSERT_TRUE(luci::validate_name(g.g())); +} + +TEST(ValidateTest, unique_name) +{ + luci::Module module; + + SqrtGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + ASSERT_TRUE(luci::validate_unique_name(&module)); +} + +TEST(ValidateTest, unique_name_NEG) +{ + luci::Module module; + + Sqrt2xGraph g; + g.init({3, 3}); + g.transfer_to(&module); + + ASSERT_FALSE(luci::validate_unique_name(&module)); +} diff --git a/compiler/luci/tester/CMakeLists.txt b/compiler/luci/tester/CMakeLists.txt index 3ac06ef3a..13aab11e7 100644 --- a/compiler/luci/tester/CMakeLists.txt +++ b/compiler/luci/tester/CMakeLists.txt @@ -6,6 +6,7 @@ TargetRequire_Return(${REQUIRED_TARGETS}) set(SRCS_READ_TESTER src/ReadTester.cpp + src/ReadModule.cpp ) add_executable(luci_readtester "${SRCS_READ_TESTER}") @@ -18,6 +19,7 @@ target_link_libraries(luci_readtester PRIVATE safemain) set(SRCS_WRITE_TESTER src/WriteTester.cpp + src/ReadModule.cpp ) add_executable(luci_writetester "${SRCS_WRITE_TESTER}") @@ -28,3 +30,22 @@ target_link_libraries(luci_writetester PRIVATE luci_export) target_link_libraries(luci_writetester PRIVATE foder) target_link_libraries(luci_writetester PRIVATE oops) target_link_libraries(luci_writetester PRIVATE safemain) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(luci_readtester_test src/ReadTester.test.cpp ${SRCS_READ_TESTER}) +target_link_libraries(luci_readtester_test luci_import) +target_link_libraries(luci_readtester_test luci_service) +target_link_libraries(luci_readtester_test luci_pass) +target_link_libraries(luci_readtester_test foder) + +GTest_AddTest(luci_writetester_test src/WriteTester.test.cpp ${SRCS_WRITE_TESTER}) +target_link_libraries(luci_writetester_test luci_import) +target_link_libraries(luci_writetester_test luci_service) +target_link_libraries(luci_writetester_test luci_pass) +target_link_libraries(luci_writetester_test luci_export) +target_link_libraries(luci_writetester_test foder) diff --git a/compiler/luci/tester/src/ReadModule.cpp b/compiler/luci/tester/src/ReadModule.cpp new file mode 100644 index 000000000..87c1233f0 --- /dev/null +++ b/compiler/luci/tester/src/ReadModule.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 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 "ReadModule.h" + +#include <luci/Pass/CircleShapeInferencePass.h> +#include <luci/Pass/CircleTypeInferencePass.h> +#include <luci/Service/Validate.h> + +#include <logo/Phase.h> + +#include <iostream> +#include <string> +#include <vector> + +std::unique_ptr<luci::Module> ReadModule(std::string &input_path) +{ + // Load model from the file + foder::FileLoader file_loader{input_path}; + std::vector<char> model_data = file_loader.load(); + const circle::Model *circle_model = circle::GetModel(model_data.data()); + if (circle_model == nullptr) + { + std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; + return nullptr; + } + + luci::Importer importer; + auto module = importer.importModule(circle_model); + assert(module->size() > 0); + + for (size_t g = 0; g < module->size(); ++g) + { + auto graph = module->graph(g); + if (graph == nullptr) + return nullptr; + + { + logo::Phase phase; + + phase.emplace_back(std::make_unique<luci::CircleShapeInferencePass>()); + phase.emplace_back(std::make_unique<luci::CircleTypeInferencePass>()); + + logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{graph}; + phase_runner.run(phase); + } + + if (!luci::validate(graph)) + return nullptr; + } + return module; +} diff --git a/compiler/luci/tester/src/ReadModule.h b/compiler/luci/tester/src/ReadModule.h new file mode 100644 index 000000000..dfa9bad6b --- /dev/null +++ b/compiler/luci/tester/src/ReadModule.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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 __LUCI_TESTER_READ_MODULE_H__ +#define __LUCI_TESTER_READ_MODULE_H__ + +#include <luci/Importer.h> +#include <foder/FileLoader.h> + +#include <memory> +#include <string> + +std::unique_ptr<luci::Module> ReadModule(std::string &input_path); + +#endif // __LUCI_TESTER_READ_MODULE_H__ diff --git a/compiler/luci/tester/src/ReadTester.cpp b/compiler/luci/tester/src/ReadTester.cpp index f270a232c..864343e43 100644 --- a/compiler/luci/tester/src/ReadTester.cpp +++ b/compiler/luci/tester/src/ReadTester.cpp @@ -14,18 +14,9 @@ * limitations under the License. */ -#include <foder/FileLoader.h> - -#include <luci/Importer.h> -#include <luci/Service/Validate.h> -#include <luci/Pass/ShapeInferencePass.h> -#include <luci/Pass/TypeInferencePass.h> - -// Following passes will be removed after refactoring is finished -#include <luci/Pass/MigrateLegacyShapeDtypePass.h> +#include "ReadModule.h" #include <iostream> -#include <map> #include <string> namespace @@ -68,45 +59,9 @@ int entry(int argc, char **argv) std::cout << "[INFO] Circle is '" << input_path << "'" << std::endl; - // Load model from the file - foder::FileLoader file_loader{input_path}; - std::vector<char> model_data = file_loader.load(); - const circle::Model *circle_model = circle::GetModel(model_data.data()); - if (circle_model == nullptr) - { - std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; + auto module = ReadModule(input_path); + if (module == nullptr) return EXIT_FAILURE; - } - - luci::Importer importer; - auto module = importer.importModule(circle_model); - assert(module->size() > 0); - for (size_t g = 0; g < module->size(); ++g) - { - auto graph = module->graph(g); - if (graph == nullptr) - return 255; - - { - luci::ShapeInferencePass pass; - while (pass.run(graph) == true) - ; - } - { - luci::TypeInferencePass pass; - while (pass.run(graph) == true) - ; - } - { - // This pass will be removed after refactoring is finished - luci::MigrateLegacyShapeDtypePass pass; - while (pass.run(graph) == true) - ; - } - - if (!luci::validate(graph)) - return 255; - } return 0; } diff --git a/compiler/luci/tester/src/ReadTester.test.cpp b/compiler/luci/tester/src/ReadTester.test.cpp new file mode 100644 index 000000000..f3850d517 --- /dev/null +++ b/compiler/luci/tester/src/ReadTester.test.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> + +// From ReadTester.cpp +int entry(int argc, char **argv); + +TEST(ReadTesterTest, invalid_argc_NEG) +{ + char argv_1[20]; + strcpy(argv_1, "ReadTesterTest"); + + int argc = 1; + char *argv[] = {argv_1}; + + ASSERT_NE(0, entry(argc, argv)); +} + +TEST(ReadTesterTest, invalid_file_NEG) +{ + char argv_1[20], argv_2[20]; + strcpy(argv_1, "ReadTesterTest"); + strcpy(argv_2, "not_a_file"); + + int argc = 2; + char *argv[] = {argv_1, argv_2}; + + EXPECT_THROW(entry(argc, argv), std::runtime_error); +} diff --git a/compiler/luci/tester/src/WriteTester.cpp b/compiler/luci/tester/src/WriteTester.cpp index 9a6e8de05..0d3a1efa2 100644 --- a/compiler/luci/tester/src/WriteTester.cpp +++ b/compiler/luci/tester/src/WriteTester.cpp @@ -14,21 +14,13 @@ * limitations under the License. */ -#include <foder/FileLoader.h> +#include "ReadModule.h" -#include <luci/Importer.h> -#include <luci/Pass/ShapeInferencePass.h> -#include <luci/Pass/TypeInferencePass.h> -#include <luci/Service/Validate.h> #include <luci/CircleExporter.h> #include <oops/InternalExn.h> -// Following passes will be removed after refactoring is finished -#include <luci/Pass/MigrateLegacyShapeDtypePass.h> - #include <fstream> #include <iostream> -#include <map> #include <string> namespace @@ -51,12 +43,12 @@ struct CircleExpContract : public luci::CircleExporter::Contract { public: CircleExpContract(loco::Graph *graph, const std::string &filename) - : _graph(graph), _filepath(filename) + : _graph(graph), _filepath(filename) { // NOTHING TO DO } CircleExpContract(luci::Module *module, const std::string &filename) - : _module(module), _filepath(filename) + : _module(module), _filepath(filename) { // NOTHING TO DO } @@ -111,47 +103,9 @@ int entry(int argc, char **argv) std::cout << "[INFO] Circle from '" << input_path << "' to '" << output_path << "'" << std::endl; - // Load model from the file - foder::FileLoader file_loader{input_path}; - std::vector<char> model_data = file_loader.load(); - const circle::Model *circle_model = circle::GetModel(model_data.data()); - if (circle_model == nullptr) - { - std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl; + auto module = ReadModule(input_path); + if (module == nullptr) return EXIT_FAILURE; - } - - // Import from input Circle file - luci::Importer importer; - auto module = importer.importModule(circle_model); - assert(module->size() > 0); - - for (size_t g = 0; g < module->size(); ++g) - { - auto graph = module->graph(g); - if (graph == nullptr) - return 255; - - { - luci::ShapeInferencePass pass; - while (pass.run(graph) == true) - ; - } - { - luci::TypeInferencePass pass; - while (pass.run(graph) == true) - ; - } - { - // This pass will be removed after refactoring is finished - luci::MigrateLegacyShapeDtypePass pass; - while (pass.run(graph) == true) - ; - } - - if (!luci::validate(graph)) - return 255; - } // Export to output Circle file luci::CircleExporter exporter; diff --git a/compiler/luci/tester/src/WriteTester.test.cpp b/compiler/luci/tester/src/WriteTester.test.cpp new file mode 100644 index 000000000..9d34c5f98 --- /dev/null +++ b/compiler/luci/tester/src/WriteTester.test.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> + +// From WriteTester.cpp +int entry(int argc, char **argv); + +TEST(WriteTesterTest, invalid_argc_NEG) +{ + char argv_1[20]; + strcpy(argv_1, "WriteTesterTest"); + + int argc = 1; + char *argv[] = {argv_1}; + + ASSERT_NE(0, entry(argc, argv)); +} + +TEST(WriteTesterTest, invalid_file_NEG) +{ + char argv_1[20], argv_2[20], argv_3[20]; + strcpy(argv_1, "WriteTesterTest"); + strcpy(argv_2, "not_a_file"); + strcpy(argv_3, "not_a_file"); + + int argc = 3; + char *argv[] = {argv_1, argv_2, argv_3}; + + EXPECT_THROW(entry(argc, argv), std::runtime_error); +} diff --git a/compiler/luci/testhelper/CMakeLists.txt b/compiler/luci/testhelper/CMakeLists.txt new file mode 100644 index 000000000..86aa66225 --- /dev/null +++ b/compiler/luci/testhelper/CMakeLists.txt @@ -0,0 +1,25 @@ +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +# NOTE we are using "*.test.cpp" NOT to be included in static analyzer tools + +# testhelper library itself +set(HELPER_SOURCE + src/TestShape.test.cpp + ) + +add_library(luci_testhelper STATIC ${HELPER_SOURCE}) +target_include_directories(luci_testhelper PRIVATE src) +target_include_directories(luci_testhelper PUBLIC include) +target_link_libraries(luci_testhelper luci_lang) + +# test for testhelper library +set(TESTER_SOURCE + src/TestIOGraph.test.cpp + ) + +GTest_AddTest(luci_testhelper_test ${TESTER_SOURCE}) +target_link_libraries(luci_testhelper_test luci_testhelper) diff --git a/compiler/luci/testhelper/README.md b/compiler/luci/testhelper/README.md new file mode 100644 index 000000000..6bdb92aa4 --- /dev/null +++ b/compiler/luci/testhelper/README.md @@ -0,0 +1,3 @@ +# luci-testhelper + +_luci-testhelper_ provides Helper classes for unit testing diff --git a/compiler/luci/testhelper/include/luci/test/TestIOGraph.h b/compiler/luci/testhelper/include/luci/test/TestIOGraph.h new file mode 100644 index 000000000..ae04f4dbc --- /dev/null +++ b/compiler/luci/testhelper/include/luci/test/TestIOGraph.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2021 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 __LUCI_TESTHELPER_TEST_IO_GRAPH_H__ +#define __LUCI_TESTHELPER_TEST_IO_GRAPH_H__ + +#include "TestShape.h" + +#include <luci/IR/CircleNodes.h> +#include <luci/IR/Module.h> + +#include <memory> +#include <stdexcept> + +namespace luci +{ +namespace test +{ + +/** + * @brief Graphlet with Inputs and loco::Graph for multiple inputs + * @note Every Graph will have Input(s) and Output(s) + * We put loco::Graph only in IsGraphlet not to declare separate + * class for loco::Graph + */ +template <unsigned N> class TestIsGraphlet +{ +public: + TestIsGraphlet() + { + for (uint32_t n = 0; n < N; ++n) + { + _graph_inputs[n] = nullptr; + _inputs[n] = nullptr; + } + _g = loco::make_graph(); + } + +public: + virtual void init(loco::Graph *g, const std::initializer_list<ShapeU32> shape_in) + { + if (shape_in.size() != N) + throw std::runtime_error("Failed to init TestIsGraphlet"); + + auto shpin = shape_in.begin(); + for (uint32_t n = 0; n < N; ++n) + { + _graph_inputs[n] = g->inputs()->create(); + + _inputs[n] = g->nodes()->create<luci::CircleInput>(); + _inputs[n]->shape(*shpin); + _inputs[n]->shape_status(luci::ShapeStatus::VALID); + _inputs[n]->dtype(loco::DataType::FLOAT32); + _inputs[n]->name("input_" + std::to_string(n)); + + _inputs[n]->index(_graph_inputs[n]->index()); + + auto input_shape = std::make_unique<loco::TensorShape>(); + set_shape_vector(input_shape.get(), *shpin); + _graph_inputs[n]->shape(std::move(input_shape)); + _graph_inputs[n]->dtype(loco::DataType::FLOAT32); + + shpin++; + } + } + +public: + loco::Graph *g(void) { return _g.get(); } + luci::CircleInput *input(int idx) { return _inputs[idx]; } + uint32_t num_inputs(void) { return N; } + +public: + void transfer_to(luci::Module *module) + { + // WARNING: after g is transfered, _graph_inputs, _inputs + // and _graph_outputs, _outputs in TestOsGraphlet will be invalid. + // arrays are not cleared as this is just helpers to unit tests + module->add(std::move(_g)); + } + +protected: + std::unique_ptr<loco::Graph> _g; + std::array<loco::GraphInput *, N> _graph_inputs; + std::array<luci::CircleInput *, N> _inputs; +}; + +/** + * @brief Graphlet with one Input + */ +class TestIGraphlet : public TestIsGraphlet<1> +{ +public: + virtual void init(loco::Graph *g, const ShapeU32 shape_in) + { + TestIsGraphlet<1>::init(g, {shape_in}); + } + + luci::CircleInput *input() { return _inputs[0]; } +}; + +/** + * @brief Graphlet with Outputs for multiple outputs + */ +template <unsigned N> class TestOsGraphlet +{ +public: + TestOsGraphlet() + { + for (uint32_t n = 0; n < N; ++n) + { + _graph_outputs[n] = nullptr; + _outputs[n] = nullptr; + } + } + +public: + virtual void init(loco::Graph *g, const std::initializer_list<ShapeU32> shape_out) + { + if (shape_out.size() != N) + throw std::runtime_error("Failed to init TestOsGraphlet"); + + auto shpout = shape_out.begin(); + for (uint32_t n = 0; n < N; ++n) + { + _graph_outputs[n] = g->outputs()->create(); + + _outputs[n] = g->nodes()->create<luci::CircleOutput>(); + _outputs[n]->shape(*shpout); + _outputs[n]->shape_status(luci::ShapeStatus::VALID); + _outputs[n]->dtype(loco::DataType::FLOAT32); + _outputs[n]->name("output_" + std::to_string(n)); + + _outputs[n]->index(_graph_outputs[n]->index()); + + auto output_shape = std::make_unique<loco::TensorShape>(); + set_shape_vector(output_shape.get(), *shpout); + _graph_outputs[n]->shape(std::move(output_shape)); + _graph_outputs[n]->dtype(loco::DataType::FLOAT32); + + shpout++; + } + } + +public: + luci::CircleOutput *output(int idx) { return _outputs[idx]; } + +protected: + std::array<loco::GraphOutput *, N> _graph_outputs; + std::array<luci::CircleOutput *, N> _outputs; +}; + +/** + * @brief Graphlet with one Output + */ +class TestOGraphlet : public TestOsGraphlet<1> +{ +public: + virtual void init(loco::Graph *g, const ShapeU32 shape_out) + { + TestOsGraphlet<1>::init(g, {shape_out}); + } + + luci::CircleOutput *output() { return _outputs[0]; } +}; + +/** + * @brief Graph with Input and Output + */ +class TestIOGraph : public TestIGraphlet, public TestOGraphlet +{ +public: + TestIOGraph() = default; + +public: + virtual void init(const ShapeU32 shape_in, const ShapeU32 shape_out) + { + TestIGraphlet::init(g(), shape_in); + TestOGraphlet::init(g(), shape_out); + } +}; + +} // namespace test +} // namespace luci + +#endif // __LUCI_TESTHELPER_TEST_IO_GRAPH_H__ diff --git a/compiler/luci/testhelper/include/luci/test/TestShape.h b/compiler/luci/testhelper/include/luci/test/TestShape.h new file mode 100644 index 000000000..1a5adf7d6 --- /dev/null +++ b/compiler/luci/testhelper/include/luci/test/TestShape.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 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 __LUCI_TESTHELPER_TEST_SHAPE_H__ +#define __LUCI_TESTHELPER_TEST_SHAPE_H__ + +#include <luci/IR/CircleNode.h> + +#include <initializer_list> + +namespace luci +{ +namespace test +{ + +using ShapeU32 = std::initializer_list<uint32_t>; +using ShapeI32 = std::initializer_list<int32_t>; + +void set_shape_vector(loco::TensorShape *shape, const ShapeU32 &values); +void set_shape_vector(luci::CircleConst *const_node, const ShapeI32 &values); + +uint32_t num_elements(const ShapeU32 shape); + +} // namespace test +} // namespace luci + +#endif // __LUCI_TESTHELPER_TEST_SHAPE_H__ diff --git a/compiler/luci/testhelper/src/TestIOGraph.test.cpp b/compiler/luci/testhelper/src/TestIOGraph.test.cpp new file mode 100644 index 000000000..8a7d1e060 --- /dev/null +++ b/compiler/luci/testhelper/src/TestIOGraph.test.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2021 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/test/TestIOGraph.h" + +#include <gtest/gtest.h> + +namespace +{ + +using namespace luci::test; + +class SqrtGraphlet +{ +public: + SqrtGraphlet() = default; + + void init(loco::Graph *g) + { + _sqrt = g->nodes()->create<luci::CircleSqrt>(); + _sqrt->name("sqrt"); + } + +protected: + luci::CircleSqrt *_sqrt = nullptr; +}; + +class AddGraphlet +{ +public: + AddGraphlet() = default; + + void init(loco::Graph *g) + { + _add = g->nodes()->create<luci::CircleAdd>(); + _add->name("add"); + } + +protected: + luci::CircleAdd *_add = nullptr; +}; + +class ConvGraphlet +{ +public: + ConvGraphlet() = default; + + void init(loco::Graph *g) + { + _conv = g->nodes()->create<luci::CircleConv2D>(); + _conv->name("conv"); + } + +protected: + luci::CircleConv2D *_conv = nullptr; +}; + +} // namespace + +namespace +{ + +class TestOfTestIOGraph : public TestIOGraph, public SqrtGraphlet +{ +public: + TestOfTestIOGraph() = default; + +public: + void init(void) + { + TestIOGraph::init({1}, {1}); + SqrtGraphlet::init(g()); + + _sqrt->x(input()); + + output()->from(_sqrt); + } +}; + +class TestOfTestI2OGraph : public TestIsGraphlet<2>, public TestOGraphlet, public AddGraphlet +{ +public: + TestOfTestI2OGraph() = default; + +public: + void init(void) + { + TestIsGraphlet<2>::init(g(), {{2, 3}, {2, 3}}); + TestOsGraphlet<1>::init(g(), {{2, 3}}); + AddGraphlet::init(g()); + + _add->x(input(0)); + _add->y(input(1)); + + output()->from(_add); + } +}; + +class TestOfTestI3OGraph : public TestIsGraphlet<3>, public TestOGraphlet, public ConvGraphlet +{ +public: + TestOfTestI3OGraph() = default; + +public: + void init(void) + { + TestIsGraphlet<3>::init(g(), {{2, 3, 3, 4}, {1, 1}, {4}}); + TestOsGraphlet<1>::init(g(), {{2, 3, 3, 4}}); + ConvGraphlet::init(g()); + + _conv->input(input(0)); + _conv->filter(input(1)); + _conv->bias(input(2)); + + output()->from(_conv); + } +}; + +class FailOfTestI3OGraph : public TestIsGraphlet<3>, public TestOGraphlet, public ConvGraphlet +{ +public: + FailOfTestI3OGraph() = default; + +public: + void init(void) + { + TestIsGraphlet<3>::init(g(), {{2, 3, 3, 4}, {1, 1}}); + TestOsGraphlet<1>::init(g(), {{2, 3, 3, 4}}); + ConvGraphlet::init(g()); + + _conv->input(input(0)); + _conv->filter(input(1)); + _conv->bias(input(2)); + + output()->from(_conv); + } +}; + +} // namespace + +TEST(TestIOGraphTest, IOGraph_init) +{ + TestOfTestIOGraph tg; + tg.init(); + + SUCCEED(); +} + +TEST(TestIOGraphTest, I2OGraph_init) +{ + TestOfTestI2OGraph tg; + tg.init(); + + SUCCEED(); +} + +TEST(TestIOGraphTest, I3OGraph_init) +{ + TestOfTestI3OGraph tg; + tg.init(); + + SUCCEED(); +} + +TEST(TestIOGraphTest, I3OGraph_input_number_mismatch_NEG) +{ + FailOfTestI3OGraph fg; + EXPECT_THROW(fg.init(), std::runtime_error); +} diff --git a/compiler/luci/testhelper/src/TestShape.test.cpp b/compiler/luci/testhelper/src/TestShape.test.cpp new file mode 100644 index 000000000..9838c6182 --- /dev/null +++ b/compiler/luci/testhelper/src/TestShape.test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 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/test/TestShape.h" + +/** + * @note This file does not hold any test cases but provides methods for tests + */ + +namespace luci +{ +namespace test +{ + +void set_shape_vector(loco::TensorShape *shape, const ShapeU32 &values) +{ + uint32_t r = 0; + shape->rank(values.size()); + for (auto v : values) + shape->dim(r++).set(v); +} + +void set_shape_vector(luci::CircleConst *const_node, const ShapeI32 &values) +{ + const_node->rank(1); + const_node->dim(0).set(values.size()); + const_node->shape_status(luci::ShapeStatus::VALID); + const_node->dtype(loco::DataType::S32); + const_node->size<loco::DataType::S32>(values.size()); + uint32_t idx = 0; + for (auto val : values) + const_node->at<loco::DataType::S32>(idx++) = val; +} + +uint32_t num_elements(const ShapeU32 shape) +{ + uint32_t result = 1; + for (auto val : shape) + result = result * val; + return result; +} + +} // namespace test +} // namespace luci diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst index 897d41983..a278fa256 100644 --- a/compiler/luci/tests/test.lst +++ b/compiler/luci/tests/test.lst @@ -51,6 +51,8 @@ addread(ExpandDims_000) addread(ExpandDims_001) addread(ExpandDims_002) addread(ExpandDims_003) +addread(ExpandDims_004) +addread(FakeQuant_000) addread(Fill_000) addread(Fill_001) addread(Floor_000) @@ -151,6 +153,7 @@ addread(SelectV2_002) addread(Shape_000) addread(Sin_000) addread(Slice_000) +addread(Slice_001) addread(Softmax_000) addread(Softmax_U8_000) addread(SpaceToBatchND_000) @@ -166,6 +169,7 @@ addread(Sqrt_000) addread(Square_000) addread(SquaredDifference_000) addread(Squeeze_000) +addread(Squeeze_001) addread(StridedSlice_000) addread(StridedSlice_001) addread(StridedSlice_002) @@ -268,6 +272,8 @@ addwrite(ExpandDims_000) addwrite(ExpandDims_001) addwrite(ExpandDims_002) addwrite(ExpandDims_003) +addwrite(ExpandDims_004) +addwrite(FakeQuant_000) addwrite(Fill_000) addwrite(Fill_001) addwrite(Floor_000) @@ -367,6 +373,7 @@ addwrite(SelectV2_002) addwrite(Shape_000) addwrite(Sin_000) addwrite(Slice_000) +addwrite(Slice_001) addwrite(Softmax_000) addwrite(Softmax_U8_000) addwrite(SpaceToBatchND_000) @@ -382,6 +389,7 @@ addwrite(Sqrt_000) addwrite(Square_000) addwrite(SquaredDifference_000) addwrite(Squeeze_000) +addwrite(Squeeze_001) addwrite(StridedSlice_000) addwrite(StridedSlice_001) addwrite(StridedSlice_002) diff --git a/compiler/mir-interpreter/src/ops/Add.cpp b/compiler/mir-interpreter/src/ops/Add.cpp index 631b854b7..f80c63c15 100644 --- a/compiler/mir-interpreter/src/ops/Add.cpp +++ b/compiler/mir-interpreter/src/ops/Add.cpp @@ -106,13 +106,13 @@ void AddImpl<uint8_t>::run(const TensorVariant &lhs, const TensorVariant &rhs, T const int32_t shifted_lhs_val = lhs_val * (1 << left_shift); const int32_t shifted_rhs_val = rhs_val * (1 << left_shift); const int32_t scaled_lhs_val = - MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_lhs_val, lhs_multiplier, lhs_shift); + MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_lhs_val, lhs_multiplier, lhs_shift); const int32_t scaled_rhs_val = - MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_rhs_val, rhs_multiplier, rhs_shift); + MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_rhs_val, rhs_multiplier, rhs_shift); const int32_t raw_sum = scaled_lhs_val + scaled_rhs_val; const int32_t raw_output = - MultiplyByQuantizedMultiplierSmallerThanOneExp(raw_sum, output_multiplier, output_shift) + - output_offset; + MultiplyByQuantizedMultiplierSmallerThanOneExp(raw_sum, output_multiplier, output_shift) + + output_offset; const int32_t clamped_output = std::min(output_max, std::max(output_min, raw_output)); res_accessor.at(index) = static_cast<uint8_t>(clamped_output); } diff --git a/compiler/mir-interpreter/src/ops/AvgPool2D.cpp b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp index 3f1d65100..3f74cd1e8 100644 --- a/compiler/mir-interpreter/src/ops/AvgPool2D.cpp +++ b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp @@ -72,7 +72,7 @@ void AvgPool2DImpl<T>::run(const ops::AvgPool2DOp &op, const TensorVariant &inpu // Assuming NHWC format. for (int i = 0; i < num_spatial_dims; ++i) in_index.at(1 + i) = - out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; + out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; if (in_range.contains(in_index)) { @@ -145,7 +145,7 @@ void AvgPool2DImpl<uint8_t>::run(const ops::AvgPool2DOp &op, const TensorVariant // Assuming NHWC format. for (int i = 0; i < num_spatial_dims; ++i) in_index.at(1 + i) = - out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; + out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; if (in_range.contains(in_index)) { diff --git a/compiler/mir-interpreter/src/ops/CappedReLU.cpp b/compiler/mir-interpreter/src/ops/CappedReLU.cpp index 1ac95ac16..5b348d463 100644 --- a/compiler/mir-interpreter/src/ops/CappedReLU.cpp +++ b/compiler/mir-interpreter/src/ops/CappedReLU.cpp @@ -68,7 +68,7 @@ template <> struct CappedReLUImpl<uint8_t> { auto value = dequantize(arg_accessor.at(index), quant_info); auto out_value = - quantize(std::min(std::max(value, 0.0f), cap), result.getType().getQuantization()); + quantize(std::min(std::max(value, 0.0f), cap), result.getType().getQuantization()); res_accessor.at(index) = out_value; } } diff --git a/compiler/mir-interpreter/src/ops/Concat.cpp b/compiler/mir-interpreter/src/ops/Concat.cpp index 99fe00c31..3c71709e6 100644 --- a/compiler/mir-interpreter/src/ops/Concat.cpp +++ b/compiler/mir-interpreter/src/ops/Concat.cpp @@ -90,8 +90,8 @@ template <> struct ConcatImpl<uint8_t> }; void ConcatImpl<uint8_t>::run( - const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis, - mir::TensorVariant &output) + const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis, + mir::TensorVariant &output) { const size_t inputs_count = inputs.size(); std::vector<int32_t> input_zeropoints(inputs_count); @@ -154,7 +154,7 @@ void ConcatImpl<uint8_t>::run( for (int j = 0; j < copy_size; ++j) { const int32_t value = - static_cast<int32_t>(std::round(input_ptr[j] * scale + bias)) + output_zeropoint; + static_cast<int32_t>(std::round(input_ptr[j] * scale + bias)) + output_zeropoint; output_ptr[j] = static_cast<uint8_t>(std::max(std::min(255, value), 0)); } } diff --git a/compiler/mir-interpreter/src/ops/Conv2D.cpp b/compiler/mir-interpreter/src/ops/Conv2D.cpp index c9b98a56f..9f4339bda 100644 --- a/compiler/mir-interpreter/src/ops/Conv2D.cpp +++ b/compiler/mir-interpreter/src/ops/Conv2D.cpp @@ -109,9 +109,9 @@ void Conv2DImpl<T>::run(const TensorVariant &input, const TensorVariant &kernel, if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width)) { const std::int32_t in_offset = - calcOffset(input_shape, batch, in_y, in_x, in_group_offset + in_c); - const std::int32_t kernel_offset = calcOffset( - kernel_shape, out_group_offset + out_c, kernel_y, kernel_x, in_c); + calcOffset(input_shape, batch, in_y, in_x, in_group_offset + in_c); + const std::int32_t kernel_offset = + calcOffset(kernel_shape, out_group_offset + out_c, kernel_y, kernel_x, in_c); const T input_val = input_data[in_offset]; const T kernel_val = kernel_data[kernel_offset]; sum += kernel_val * input_val; @@ -121,7 +121,7 @@ void Conv2DImpl<T>::run(const TensorVariant &input, const TensorVariant &kernel, } const std::int32_t out_offset = - calcOffset(output_shape, batch, out_y, out_x, out_group_offset + out_c); + calcOffset(output_shape, batch, out_y, out_x, out_group_offset + out_c); result_data[out_offset] = sum; } } diff --git a/compiler/mir-interpreter/src/ops/DeConv2D.cpp b/compiler/mir-interpreter/src/ops/DeConv2D.cpp index 746d8c87c..f9e837ddb 100644 --- a/compiler/mir-interpreter/src/ops/DeConv2D.cpp +++ b/compiler/mir-interpreter/src/ops/DeConv2D.cpp @@ -98,9 +98,9 @@ void DeConv2DImpl<T>::run(const TensorVariant &input, const TensorVariant &kerne for (int32_t out_c = 0; out_c < num_out_channels; ++out_c) { const int32_t kernel_offset = - calcOffset(kernel_shape, in_c, kernel_y, kernel_x, out_c); + calcOffset(kernel_shape, in_c, kernel_y, kernel_x, out_c); const int32_t output_offset = - calcOffset(output_shape, batch, out_y, out_x, out_c); + calcOffset(output_shape, batch, out_y, out_x, out_c); const T kernel_val = kernel_data[kernel_offset]; output_data[output_offset] += input_val * kernel_val; } diff --git a/compiler/mir-interpreter/src/ops/Gather.cpp b/compiler/mir-interpreter/src/ops/Gather.cpp index 4328c26b2..11bffd411 100644 --- a/compiler/mir-interpreter/src/ops/Gather.cpp +++ b/compiler/mir-interpreter/src/ops/Gather.cpp @@ -64,7 +64,7 @@ void GatherImpl<T, IndicesT>::run(const TensorVariant &datav, const TensorVarian for (int32_t inner = 0; inner < inner_size; inner++) { output.atOffset((outer * num_indices + i) * inner_size + inner) = - data.atOffset((outer * axis_size + index) * inner_size + inner); + data.atOffset((outer * axis_size + index) * inner_size + inner); } } } diff --git a/compiler/mir-interpreter/src/ops/MaxPool2D.cpp b/compiler/mir-interpreter/src/ops/MaxPool2D.cpp index cec2f5984..6be1ccf08 100644 --- a/compiler/mir-interpreter/src/ops/MaxPool2D.cpp +++ b/compiler/mir-interpreter/src/ops/MaxPool2D.cpp @@ -72,7 +72,7 @@ void MaxPool2DImpl<T>::run(const TensorVariant &inputv, const ops::MaxPool2DOp & // Assuming NHWC format. for (int i = 0; i < num_spatial_dims; ++i) in_index.at(1 + i) = - out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; + out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; if (in_range.contains(in_index)) { @@ -137,7 +137,7 @@ void MaxPool2DImpl<uint8_t>::run(const TensorVariant &input, const ops::MaxPool2 // Assuming NHWC format. for (int i = 0; i < num_spatial_dims; ++i) in_index.at(1 + i) = - out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; + out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i]; if (in_range.contains(in_index)) { diff --git a/compiler/mir-interpreter/src/ops/QuantizationHelpers.h b/compiler/mir-interpreter/src/ops/QuantizationHelpers.h index 8faeffbd3..3ab6f1edc 100644 --- a/compiler/mir-interpreter/src/ops/QuantizationHelpers.h +++ b/compiler/mir-interpreter/src/ops/QuantizationHelpers.h @@ -110,7 +110,7 @@ inline int32_t MultiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multip int left_shift = shift > 0 ? shift : 0; int right_shift = shift > 0 ? 0 : -shift; return RoundingDivideByPOT( - SaturatingRoundingDoublingHighMul(x * (1 << left_shift), quantized_multiplier), right_shift); + SaturatingRoundingDoublingHighMul(x * (1 << left_shift), quantized_multiplier), right_shift); } inline int32_t MultiplyByQuantizedMultiplierSmallerThanOneExp(int32_t x, diff --git a/compiler/mir-interpreter/src/ops/Softmax.cpp b/compiler/mir-interpreter/src/ops/Softmax.cpp index f263f967d..554f8c371 100644 --- a/compiler/mir-interpreter/src/ops/Softmax.cpp +++ b/compiler/mir-interpreter/src/ops/Softmax.cpp @@ -70,7 +70,7 @@ void SoftmaxImpl<T>::run(const mir::TensorVariant &arg, int axis, mir::TensorVar mir::Index expsum_index = res_index; expsum_index.at(axis) = 0; res_accessor.at(res_index) = - std::exp(arg_accessor.at(res_index)) / expsum_accessor.at(expsum_index); + std::exp(arg_accessor.at(res_index)) / expsum_accessor.at(expsum_index); } } @@ -140,7 +140,7 @@ void SoftmaxImpl<uint8_t>::run(const mir::TensorVariant &input, int axis, const float prob_rescaled = table_offset[input_data[j]] * inv_sum_exp; const int32_t prob_quantized = static_cast<int32_t>(prob_rescaled + 0.5); output_data[j] = - static_cast<uint8_t>(std::max(std::min(clamp_max, prob_quantized), clamp_min)); + static_cast<uint8_t>(std::max(std::min(clamp_max, prob_quantized), clamp_min)); } input_data += last_dim; output_data += last_dim; diff --git a/compiler/mir/include/mir/Quantization.h b/compiler/mir/include/mir/Quantization.h index d266ee00d..901915a74 100644 --- a/compiler/mir/include/mir/Quantization.h +++ b/compiler/mir/include/mir/Quantization.h @@ -26,7 +26,7 @@ public: AffineQuantization() = default; AffineQuantization(float scale, int zero_point) - : _scale(scale), _zero_point(zero_point), _empty(false) + : _scale(scale), _zero_point(zero_point), _empty(false) { } diff --git a/compiler/mir/include/mir/ShapeRange.h b/compiler/mir/include/mir/ShapeRange.h index a450bf090..70b29715f 100644 --- a/compiler/mir/include/mir/ShapeRange.h +++ b/compiler/mir/include/mir/ShapeRange.h @@ -26,7 +26,7 @@ namespace mir { class ShapeIter - : public std::iterator<std::forward_iterator_tag, Index, std::size_t, Index *, Index &> + : public std::iterator<std::forward_iterator_tag, Index, std::size_t, Index *, Index &> { public: ShapeIter &operator++() diff --git a/compiler/mir/include/mir/TensorType.h b/compiler/mir/include/mir/TensorType.h index 98797d687..b94a26eeb 100644 --- a/compiler/mir/include/mir/TensorType.h +++ b/compiler/mir/include/mir/TensorType.h @@ -34,7 +34,7 @@ public: } TensorType(DataType element_type, const Shape &shape, const AffineQuantization &quant) - : _element_type(element_type), _shape(shape), _quantization(quant) + : _element_type(element_type), _shape(shape), _quantization(quant) { } diff --git a/compiler/mir/include/mir/ops/AvgPool2DOp.h b/compiler/mir/include/mir/ops/AvgPool2DOp.h index 47fe058ee..37fb66437 100644 --- a/compiler/mir/include/mir/ops/AvgPool2DOp.h +++ b/compiler/mir/include/mir/ops/AvgPool2DOp.h @@ -32,7 +32,7 @@ class AvgPool2DOp : public Operation { public: AvgPool2DOp(Output *arg, const AvgPool2DOpAttributes &attributes) - : Operation(Type::avgPool2D, {arg}), _attributes(attributes) + : Operation(Type::avgPool2D, {arg}), _attributes(attributes) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/ConcatOp.h b/compiler/mir/include/mir/ops/ConcatOp.h index 4f46d4449..d1f9142fa 100644 --- a/compiler/mir/include/mir/ops/ConcatOp.h +++ b/compiler/mir/include/mir/ops/ConcatOp.h @@ -31,7 +31,7 @@ class ConcatOp : public Operation { public: ConcatOp(const std::vector<Output *> &args, int32_t axis) - : Operation(Type::concat, args), _axis(axis) + : Operation(Type::concat, args), _axis(axis) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/Conv2DOp.h b/compiler/mir/include/mir/ops/Conv2DOp.h index ec818dae5..f8590a947 100644 --- a/compiler/mir/include/mir/ops/Conv2DOp.h +++ b/compiler/mir/include/mir/ops/Conv2DOp.h @@ -30,13 +30,13 @@ class Conv2DOp : public Operation { public: Conv2DOp(Output *input, Output *kernel, const Conv2DOpAttributes &attributes) - : Operation(Type::conv2D, {input, kernel}), _attributes(attributes) + : Operation(Type::conv2D, {input, kernel}), _attributes(attributes) { inferOutputTypes(); } Conv2DOp(Output *input, Output *kernel, Output *bias, const Conv2DOpAttributes &attributes) - : Operation(Type::conv2D, {input, kernel, bias}), _attributes(attributes) + : Operation(Type::conv2D, {input, kernel, bias}), _attributes(attributes) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/Deconv2DOp.h b/compiler/mir/include/mir/ops/Deconv2DOp.h index a7b548028..9565eeb37 100644 --- a/compiler/mir/include/mir/ops/Deconv2DOp.h +++ b/compiler/mir/include/mir/ops/Deconv2DOp.h @@ -33,14 +33,14 @@ class DeConv2DOp : public Operation { public: DeConv2DOp(Output *input, Output *kernel, const Deconv2DOpAttributes &attributes) - : Operation(Type::deConv2D, {input, kernel}), _attributes(attributes) + : Operation(Type::deConv2D, {input, kernel}), _attributes(attributes) { inferOutputTypes(); } DeConv2DOp(Output *input, Output *kernel, const Deconv2DOpAttributes &attributes, const Shape &output_shape) - : Operation(Type::deConv2D, {input, kernel}), _attributes(attributes) + : Operation(Type::deConv2D, {input, kernel}), _attributes(attributes) { assert(input->getElementType() == kernel->getElementType()); setOutputType(0, {input->getElementType(), output_shape}); diff --git a/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h b/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h index 347b8e94f..558d60a4a 100644 --- a/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h +++ b/compiler/mir/include/mir/ops/DepthwiseConv2DOp.h @@ -30,14 +30,14 @@ class DepthwiseConv2DOp : public Operation { public: DepthwiseConv2DOp(Output *input, Output *kernel, const Conv2DOpAttributes &attributes) - : Operation(Type::depthwiseConv, {input, kernel}), _attributes(attributes) + : Operation(Type::depthwiseConv, {input, kernel}), _attributes(attributes) { inferOutputTypes(); } DepthwiseConv2DOp(Output *input, Output *kernel, Output *bias, const Conv2DOpAttributes &attributes) - : Operation(Type::depthwiseConv, {input, kernel, bias}), _attributes(attributes) + : Operation(Type::depthwiseConv, {input, kernel, bias}), _attributes(attributes) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/FullyConnectedOp.h b/compiler/mir/include/mir/ops/FullyConnectedOp.h index 589c42df9..f937df539 100644 --- a/compiler/mir/include/mir/ops/FullyConnectedOp.h +++ b/compiler/mir/include/mir/ops/FullyConnectedOp.h @@ -29,13 +29,13 @@ class FullyConnectedOp : public Operation { public: FullyConnectedOp(Output *input, Output *weights) - : Operation(Type::fullyConnected, {input, weights}) + : Operation(Type::fullyConnected, {input, weights}) { inferOutputTypes(); } FullyConnectedOp(Output *input, Output *weights, Output *bias) - : Operation(Type::fullyConnected, {input, weights, bias}) + : Operation(Type::fullyConnected, {input, weights, bias}) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/GatherOp.h b/compiler/mir/include/mir/ops/GatherOp.h index 899c9f169..58ea04074 100644 --- a/compiler/mir/include/mir/ops/GatherOp.h +++ b/compiler/mir/include/mir/ops/GatherOp.h @@ -33,7 +33,7 @@ class GatherOp : public Operation { public: GatherOp(Output *data, Output *indices, int32_t axis) - : Operation(Type::gather, {data, indices}), _axis(axis) + : Operation(Type::gather, {data, indices}), _axis(axis) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/MaxPool2DOp.h b/compiler/mir/include/mir/ops/MaxPool2DOp.h index 7c5df4a53..4345cfc18 100644 --- a/compiler/mir/include/mir/ops/MaxPool2DOp.h +++ b/compiler/mir/include/mir/ops/MaxPool2DOp.h @@ -32,7 +32,7 @@ class MaxPool2DOp : public Operation { public: MaxPool2DOp(Output *arg, const MaxPool2DOpAttributes &attributes) - : Operation(Type::maxPool2D, {arg}), _attributes(attributes) + : Operation(Type::maxPool2D, {arg}), _attributes(attributes) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/PadOp.h b/compiler/mir/include/mir/ops/PadOp.h index 76453acec..d229a97bd 100644 --- a/compiler/mir/include/mir/ops/PadOp.h +++ b/compiler/mir/include/mir/ops/PadOp.h @@ -29,7 +29,7 @@ class PadOp : public Operation { public: PadOp(Output *arg, const PadOpAttributes &attributes) - : Operation(Type::pad, {arg}), _attributes(attributes) + : Operation(Type::pad, {arg}), _attributes(attributes) { assert(_attributes.padding_before.size() == _attributes.padding_after.size()); inferOutputTypes(); diff --git a/compiler/mir/include/mir/ops/ReduceMeanOp.h b/compiler/mir/include/mir/ops/ReduceMeanOp.h index add47ac75..5759b845e 100644 --- a/compiler/mir/include/mir/ops/ReduceMeanOp.h +++ b/compiler/mir/include/mir/ops/ReduceMeanOp.h @@ -29,7 +29,7 @@ class ReduceMeanOp : public ReduceOp { public: ReduceMeanOp(Output *arg, const std::vector<int> &reduction_dims, bool keep_dims) - : ReduceOp(Type::reduceMean, arg, reduction_dims, keep_dims) + : ReduceOp(Type::reduceMean, arg, reduction_dims, keep_dims) { } diff --git a/compiler/mir/include/mir/ops/ReduceOp.h b/compiler/mir/include/mir/ops/ReduceOp.h index 0f46a4596..5204a0903 100644 --- a/compiler/mir/include/mir/ops/ReduceOp.h +++ b/compiler/mir/include/mir/ops/ReduceOp.h @@ -29,7 +29,7 @@ class ReduceOp : public Operation { protected: ReduceOp(Type type, Output *arg, const std::vector<int> &reduction_dims, bool keep_dims) - : Operation(type, {arg}), _reduction_dims(reduction_dims), _keep_dims(keep_dims) + : Operation(type, {arg}), _reduction_dims(reduction_dims), _keep_dims(keep_dims) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/ResizeOp.h b/compiler/mir/include/mir/ops/ResizeOp.h index 51e1b0b76..62743e396 100644 --- a/compiler/mir/include/mir/ops/ResizeOp.h +++ b/compiler/mir/include/mir/ops/ResizeOp.h @@ -40,7 +40,7 @@ public: }; ResizeOp(Output *arg, ResizeMethod mode, const std::vector<float> &scales) - : Operation(Type::resizeIm, {arg}), _mode(mode), _scales(scales) + : Operation(Type::resizeIm, {arg}), _mode(mode), _scales(scales) { // Infer output shape based on given scales. auto &input_shape = getInputShape(0); @@ -61,7 +61,7 @@ public: } ResizeOp(Output *arg, ResizeMethod mode, const Shape &output_shape) - : Operation(Type::resizeIm, {arg}), _mode(mode) + : Operation(Type::resizeIm, {arg}), _mode(mode) { // Calculate scales based on given shape. auto &input_shape = getInputShape(0); diff --git a/compiler/mir/include/mir/ops/SliceOp.h b/compiler/mir/include/mir/ops/SliceOp.h index 6370de4fa..1627d4b82 100644 --- a/compiler/mir/include/mir/ops/SliceOp.h +++ b/compiler/mir/include/mir/ops/SliceOp.h @@ -28,7 +28,7 @@ class SliceOp : public Operation { public: SliceOp(Output *arg, const Shape &starts, const Shape &sizes) - : Operation(Type::slice, {arg}), _starts(starts), _sizes(sizes) + : Operation(Type::slice, {arg}), _starts(starts), _sizes(sizes) { inferOutputTypes(); } diff --git a/compiler/mir/include/mir/ops/SqueezeOp.h b/compiler/mir/include/mir/ops/SqueezeOp.h index 8ef2a78bb..735b7d86d 100644 --- a/compiler/mir/include/mir/ops/SqueezeOp.h +++ b/compiler/mir/include/mir/ops/SqueezeOp.h @@ -29,7 +29,7 @@ class SqueezeOp : public Operation { public: SqueezeOp(Output *arg, const std::vector<std::int32_t> &dims_to_squeeze) - : Operation(Type::squeeze, {arg}), _dims_to_squeeze(dims_to_squeeze) + : Operation(Type::squeeze, {arg}), _dims_to_squeeze(dims_to_squeeze) { // Infer output shape. inferOutputTypes(); diff --git a/compiler/mir/src/Graph.cpp b/compiler/mir/src/Graph.cpp index 0eccdac2b..04b005de4 100644 --- a/compiler/mir/src/Graph.cpp +++ b/compiler/mir/src/Graph.cpp @@ -123,11 +123,11 @@ void Graph::removeNode(Operation *op) if (op->getType() == Operation::Type::input) _inputs.erase( - std::remove(_inputs.begin(), _inputs.end(), op)); // NOLINT(bugprone-inaccurate-erase) + std::remove(_inputs.begin(), _inputs.end(), op)); // NOLINT(bugprone-inaccurate-erase) if (op->getType() == Operation::Type::output) _outputs.erase( - std::remove(_outputs.begin(), _outputs.end(), op)); // NOLINT(bugprone-inaccurate-erase) + std::remove(_outputs.begin(), _outputs.end(), op)); // NOLINT(bugprone-inaccurate-erase) _ops.erase(op); delete op; diff --git a/compiler/mir/src/Operation.cpp b/compiler/mir/src/Operation.cpp index 6f72acbf6..9ba395f94 100644 --- a/compiler/mir/src/Operation.cpp +++ b/compiler/mir/src/Operation.cpp @@ -40,7 +40,7 @@ void Operation::Output::replaceAllUsesWith(mir::Operation::Output *new_def) } Operation::Operation(Type type, const std::vector<Output *> &inputs, std::size_t num_outputs) - : _type(type) + : _type(type) { for (std::size_t i = 0; i < inputs.size(); ++i) { diff --git a/compiler/mir/src/Shape.cpp b/compiler/mir/src/Shape.cpp index 825420cd6..06dae0c54 100644 --- a/compiler/mir/src/Shape.cpp +++ b/compiler/mir/src/Shape.cpp @@ -48,9 +48,9 @@ Shape broadcastShapes(const Shape &lhs_shape, const Shape &rhs_shape) for (int i = 0; i < num_dims; ++i) { const std::int32_t lhs_dim = - (i >= num_dims - lhs_shape.rank()) ? lhs_shape.dim(i - (num_dims - lhs_shape.rank())) : 1; + (i >= num_dims - lhs_shape.rank()) ? lhs_shape.dim(i - (num_dims - lhs_shape.rank())) : 1; const std::int32_t rhs_dim = - (i >= num_dims - rhs_shape.rank()) ? rhs_shape.dim(i - (num_dims - rhs_shape.rank())) : 1; + (i >= num_dims - rhs_shape.rank()) ? rhs_shape.dim(i - (num_dims - rhs_shape.rank())) : 1; if (lhs_dim == 1) { result_shape.dim(i) = rhs_dim; diff --git a/compiler/mir/src/TensorVariant.cpp b/compiler/mir/src/TensorVariant.cpp index 9e57dbaf0..516c0df73 100644 --- a/compiler/mir/src/TensorVariant.cpp +++ b/compiler/mir/src/TensorVariant.cpp @@ -35,7 +35,7 @@ TensorVariant::TensorVariant(const TensorType &type) : _type(type), _strides(typ } TensorVariant::TensorVariant(DataType element_type, const Shape &shape) - : TensorVariant(TensorType(element_type, shape)) + : TensorVariant(TensorType(element_type, shape)) { } @@ -46,7 +46,7 @@ TensorVariant::TensorVariant(const TensorType &type, const void *data) : TensorV } TensorVariant::TensorVariant(DataType element_type, const Shape &shape, const void *data) - : TensorVariant(TensorType(element_type, shape), data) + : TensorVariant(TensorType(element_type, shape), data) { } @@ -57,8 +57,8 @@ TensorVariant::TensorVariant(DataType element_type, const Shape &shape, const vo * @param shape shape to broadcast to */ TensorVariant::TensorVariant(const TensorVariant &t_old, const Shape &shape) - : _type(t_old.getType().getElementType(), shape), _data(t_old._data), - _strides(static_cast<size_t>(shape.rank())), _element_size(t_old._element_size) + : _type(t_old.getType().getElementType(), shape), _data(t_old._data), + _strides(static_cast<size_t>(shape.rank())), _element_size(t_old._element_size) { int axis_old = t_old.getShape().rank() - 1; for (int d = shape.rank() - 1; d >= 0; d--) diff --git a/compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp b/compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp index 812fcc5cc..abecfc88a 100644 --- a/compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp +++ b/compiler/mir/src/mir_caffe2_importer/caffe2_importer.cpp @@ -99,7 +99,7 @@ using mir::Shape; Caffe2Importer::Caffe2Importer(std::string predict_net, std::string init_net, const std::vector<std::vector<int>> &input_shapes) - : _predictNet(std::move(predict_net)), _initNet(std::move(init_net)) + : _predictNet(std::move(predict_net)), _initNet(std::move(init_net)) { for (auto &shape : input_shapes) _inputShapes.emplace_back(shape); @@ -308,27 +308,27 @@ void Caffe2Importer::setGraphOutputs() } const std::map<std::string, SupportedCaffe2OpType> Caffe2Importer::_operatorTypes = { - {"Add", SupportedCaffe2OpType::add}, - {"AveragePool", SupportedCaffe2OpType::averagePool}, - {"Conv", SupportedCaffe2OpType::conv}, - {"Concat", SupportedCaffe2OpType::concat}, - {"ConstantFill", SupportedCaffe2OpType::constantFill}, - {"Dropout", SupportedCaffe2OpType::dropout}, - {"FC", SupportedCaffe2OpType::FC}, - {"GivenTensorFill", SupportedCaffe2OpType::givenTensorFill}, - {"MaxPool", SupportedCaffe2OpType::maxPool}, - {"Mul", SupportedCaffe2OpType::mul}, - {"Relu", SupportedCaffe2OpType::relu}, - {"ResizeNearest", SupportedCaffe2OpType::resizeNearest}, - {"Sigmoid", SupportedCaffe2OpType::sigmoid}, - {"Softmax", SupportedCaffe2OpType::softmax}, - {"SpatialBN", SupportedCaffe2OpType::spatialBN}, - {"Sum", SupportedCaffe2OpType::sum}, - {"Clip", SupportedCaffe2OpType::clip}, - {"Reshape", SupportedCaffe2OpType::reshape}, - {"GivenTensorInt64Fill", SupportedCaffe2OpType::givenTensorInt64Fill}, + {"Add", SupportedCaffe2OpType::add}, + {"AveragePool", SupportedCaffe2OpType::averagePool}, + {"Conv", SupportedCaffe2OpType::conv}, + {"Concat", SupportedCaffe2OpType::concat}, + {"ConstantFill", SupportedCaffe2OpType::constantFill}, + {"Dropout", SupportedCaffe2OpType::dropout}, + {"FC", SupportedCaffe2OpType::FC}, + {"GivenTensorFill", SupportedCaffe2OpType::givenTensorFill}, + {"MaxPool", SupportedCaffe2OpType::maxPool}, + {"Mul", SupportedCaffe2OpType::mul}, + {"Relu", SupportedCaffe2OpType::relu}, + {"ResizeNearest", SupportedCaffe2OpType::resizeNearest}, + {"Sigmoid", SupportedCaffe2OpType::sigmoid}, + {"Softmax", SupportedCaffe2OpType::softmax}, + {"SpatialBN", SupportedCaffe2OpType::spatialBN}, + {"Sum", SupportedCaffe2OpType::sum}, + {"Clip", SupportedCaffe2OpType::clip}, + {"Reshape", SupportedCaffe2OpType::reshape}, + {"GivenTensorInt64Fill", SupportedCaffe2OpType::givenTensorInt64Fill}, }; -} +} // namespace namespace mir_caffe2 { diff --git a/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp b/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp index 3390f4482..de0762dfa 100644 --- a/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp +++ b/compiler/mir/src/mir_caffe2_importer/caffe2_op_creator.cpp @@ -125,7 +125,7 @@ static std::vector<std::int32_t> getWindowSize(const ::caffe2::OperatorDef &op, { int is_global_pooling = getSingleArgument(op, "global_pooling", 0); bool has_custom_kernel_size = - hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w"); + hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w"); bool has_custom_kernels_size = hasArgument(op.arg(), "kernels"); int kernel_h(0), kernel_w(0); @@ -186,14 +186,13 @@ static void checkConvLikeOp(const ::caffe2::OperatorDef &op) if (has_custom_pad && hasArgument(op.arg(), "pad")) throw std::runtime_error("Custom pad can't be combined with overall pad"); - if (has_custom_pad && - !(hasArgument(op.arg(), "pad_l") && hasArgument(op.arg(), "pad_r") && - hasArgument(op.arg(), "pad_t") && hasArgument(op.arg(), "pad_b"))) + if (has_custom_pad && !(hasArgument(op.arg(), "pad_l") && hasArgument(op.arg(), "pad_r") && + hasArgument(op.arg(), "pad_t") && hasArgument(op.arg(), "pad_b"))) throw std::runtime_error("If one custom pad specified - all custom pads must be specified"); // Kernel size bool has_custom_kernel_size = - hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w"); + hasArgument(op.arg(), "kernel_h") || hasArgument(op.arg(), "kernel_w"); if (has_custom_kernel_size && hasArgument(op.arg(), "kernel")) throw std::runtime_error("Custom kernel size can't be combined with overall kernel size"); @@ -201,7 +200,7 @@ static void checkConvLikeOp(const ::caffe2::OperatorDef &op) if (has_custom_kernel_size && !(hasArgument(op.arg(), "kernel_h") && hasArgument(op.arg(), "kernel_w"))) throw std::runtime_error( - "If one custom kernel size specified - all custom kernel sizes must be specified"); + "If one custom kernel size specified - all custom kernel sizes must be specified"); } static mir::TensorVariant createTensor(const OperatorDef &op) @@ -356,7 +355,7 @@ Caffe2OpCreator::convertFC(const std::vector<mir::Operation::Output *> &inputs, auto reshape = createOp<ops::ReshapeOp>(inputs[0], shape)->getOutput(0); auto weights = - createOp<ops::TransposeOp>(inputs[1], std::vector<std::size_t>{1, 0})->getOutput(0); + createOp<ops::TransposeOp>(inputs[1], std::vector<std::size_t>{1, 0})->getOutput(0); auto result = createOp<ops::FullyConnectedOp>(reshape, weights)->getOutput(0); result = createOp<ops::AddOp>(result, inputs[2])->getOutput(0); @@ -420,8 +419,8 @@ Caffe2OpCreator::convertResizeNearest(const std::vector<mir::Operation::Output * scales[2] = getSingleArgument(op, "height_scale", 1.0f); scales[3] = getSingleArgument(op, "width_scale", 1.0f); auto result = - createOp<ops::ResizeOp>(inputs[0], ops::ResizeOp::ResizeMethod::nearestNeighbor, scales) - ->getOutput(0); + createOp<ops::ResizeOp>(inputs[0], ops::ResizeOp::ResizeMethod::nearestNeighbor, scales) + ->getOutput(0); return {result}; } @@ -450,7 +449,7 @@ Caffe2OpCreator::convertSpatialBN(const std::vector<mir::Operation::Output *> &i // Sanity checks if (op.input_size() != 5) throw std::runtime_error( - "SpatialBN must have exactly 5 inputs ('sums' and 'sumsq' are not supported yet)"); + "SpatialBN must have exactly 5 inputs ('sums' and 'sumsq' are not supported yet)"); if (getSingleArgument(op, "is_test", 1) != 1) throw std::runtime_error("SpatialBN: only test mode supported"); @@ -462,7 +461,7 @@ Caffe2OpCreator::convertSpatialBN(const std::vector<mir::Operation::Output *> &i auto var_op = dynamic_cast<mir::ops::ConstantOp *>(inputs[4]->getNode()); if (scale_op == nullptr || bias_op == nullptr || mean_op == nullptr || var_op == nullptr) throw std::runtime_error( - "SpatialBN: non-constant 'scale', 'bias', 'mean' and 'var' inputs are not supported yet."); + "SpatialBN: non-constant 'scale', 'bias', 'mean' and 'var' inputs are not supported yet."); const auto &scale_tensor = scale_op->getValue(); const auto &bias_tensor = bias_op->getValue(); diff --git a/compiler/mir/src/mir_caffe_importer/caffe_importer.cpp b/compiler/mir/src/mir_caffe_importer/caffe_importer.cpp index 49f13fbd8..c74658299 100644 --- a/compiler/mir/src/mir_caffe_importer/caffe_importer.cpp +++ b/compiler/mir/src/mir_caffe_importer/caffe_importer.cpp @@ -357,66 +357,66 @@ void CaffeImporter::setGraphOutputs(mir::Graph *graph) } const std::map<std::string, CaffeOpType> CaffeImporter::_operatorTypes = { - {"AbsVal", CaffeOpType::absVal}, - {"Accuracy", CaffeOpType::accuracy}, - {"ArgMax", CaffeOpType::argMax}, - {"BatchNorm", CaffeOpType::batchNorm}, - {"BatchReindex", CaffeOpType::batchReindex}, - {"Bias", CaffeOpType::bias}, - {"BNLL", CaffeOpType::BNLL}, - {"Clip", CaffeOpType::clip}, - {"Concat", CaffeOpType::concat}, - {"ContrastiveLoss", CaffeOpType::contrastiveLoss}, - {"Convolution", CaffeOpType::convolution}, - {"Crop", CaffeOpType::crop}, - {"Data", CaffeOpType::data}, - {"Deconvolution", CaffeOpType::deconvolution}, - {"Dropout", CaffeOpType::dropout}, - {"DummyData", CaffeOpType::dummyData}, - {"Eltwise", CaffeOpType::eltwise}, - {"ELU", CaffeOpType::ELU}, - {"Embed", CaffeOpType::embed}, - {"EuclidianLoss", CaffeOpType::euclidianLoss}, - {"Exp", CaffeOpType::exp}, - {"Filter", CaffeOpType::filter}, - {"Flatten", CaffeOpType::flatten}, - {"HDF5Data", CaffeOpType::HDF5Data}, - {"HDF5Output", CaffeOpType::HDF5Output}, - {"HingeLoss", CaffeOpType::hingeLoss}, - {"Im2Col", CaffeOpType::im2Col}, - {"ImageData", CaffeOpType::imageData}, - {"InfogainLoss", CaffeOpType::infogainLoss}, - {"InnerProduct", CaffeOpType::innerProduct}, - {"Input", CaffeOpType::input}, - {"Log", CaffeOpType::log}, - {"LRN", CaffeOpType::LRN}, - {"LSTM", CaffeOpType::LSTM}, - {"MemoryData", CaffeOpType::memoryData}, - {"MultinomialLogisticLoss", CaffeOpType::multinomialLogisticLoss}, - {"MVN", CaffeOpType::MVN}, - {"Parameter", CaffeOpType::parameter}, - {"Pooling", CaffeOpType::pooling}, - {"Power", CaffeOpType::power}, - {"PReLU", CaffeOpType::PReLU}, - {"Python", CaffeOpType::python}, - {"Recurrent", CaffeOpType::recurrent}, - {"Reduction", CaffeOpType::reduction}, - {"ReLU", CaffeOpType::ReLU}, - {"Reshape", CaffeOpType::reshape}, - {"RNN", CaffeOpType::RNN}, - {"Scale", CaffeOpType::scale}, - {"SigmoidCrossEntropyLoss", CaffeOpType::sigmoidCrossEntropyLoss}, - {"Sigmoid", CaffeOpType::sigmoid}, - {"Silence", CaffeOpType::silence}, - {"Softmax", CaffeOpType::softmax}, - {"SoftmaxWithLoss", CaffeOpType::softmaxWithLoss}, - {"SPP", CaffeOpType::SPP}, - {"Split", CaffeOpType::split}, - {"Slice", CaffeOpType::slice}, - {"TanH", CaffeOpType::tanh}, - {"Threshold", CaffeOpType::threshold}, - {"Tile", CaffeOpType::tile}, - {"WindowData", CaffeOpType::windowData}}; + {"AbsVal", CaffeOpType::absVal}, + {"Accuracy", CaffeOpType::accuracy}, + {"ArgMax", CaffeOpType::argMax}, + {"BatchNorm", CaffeOpType::batchNorm}, + {"BatchReindex", CaffeOpType::batchReindex}, + {"Bias", CaffeOpType::bias}, + {"BNLL", CaffeOpType::BNLL}, + {"Clip", CaffeOpType::clip}, + {"Concat", CaffeOpType::concat}, + {"ContrastiveLoss", CaffeOpType::contrastiveLoss}, + {"Convolution", CaffeOpType::convolution}, + {"Crop", CaffeOpType::crop}, + {"Data", CaffeOpType::data}, + {"Deconvolution", CaffeOpType::deconvolution}, + {"Dropout", CaffeOpType::dropout}, + {"DummyData", CaffeOpType::dummyData}, + {"Eltwise", CaffeOpType::eltwise}, + {"ELU", CaffeOpType::ELU}, + {"Embed", CaffeOpType::embed}, + {"EuclidianLoss", CaffeOpType::euclidianLoss}, + {"Exp", CaffeOpType::exp}, + {"Filter", CaffeOpType::filter}, + {"Flatten", CaffeOpType::flatten}, + {"HDF5Data", CaffeOpType::HDF5Data}, + {"HDF5Output", CaffeOpType::HDF5Output}, + {"HingeLoss", CaffeOpType::hingeLoss}, + {"Im2Col", CaffeOpType::im2Col}, + {"ImageData", CaffeOpType::imageData}, + {"InfogainLoss", CaffeOpType::infogainLoss}, + {"InnerProduct", CaffeOpType::innerProduct}, + {"Input", CaffeOpType::input}, + {"Log", CaffeOpType::log}, + {"LRN", CaffeOpType::LRN}, + {"LSTM", CaffeOpType::LSTM}, + {"MemoryData", CaffeOpType::memoryData}, + {"MultinomialLogisticLoss", CaffeOpType::multinomialLogisticLoss}, + {"MVN", CaffeOpType::MVN}, + {"Parameter", CaffeOpType::parameter}, + {"Pooling", CaffeOpType::pooling}, + {"Power", CaffeOpType::power}, + {"PReLU", CaffeOpType::PReLU}, + {"Python", CaffeOpType::python}, + {"Recurrent", CaffeOpType::recurrent}, + {"Reduction", CaffeOpType::reduction}, + {"ReLU", CaffeOpType::ReLU}, + {"Reshape", CaffeOpType::reshape}, + {"RNN", CaffeOpType::RNN}, + {"Scale", CaffeOpType::scale}, + {"SigmoidCrossEntropyLoss", CaffeOpType::sigmoidCrossEntropyLoss}, + {"Sigmoid", CaffeOpType::sigmoid}, + {"Silence", CaffeOpType::silence}, + {"Softmax", CaffeOpType::softmax}, + {"SoftmaxWithLoss", CaffeOpType::softmaxWithLoss}, + {"SPP", CaffeOpType::SPP}, + {"Split", CaffeOpType::split}, + {"Slice", CaffeOpType::slice}, + {"TanH", CaffeOpType::tanh}, + {"Threshold", CaffeOpType::threshold}, + {"Tile", CaffeOpType::tile}, + {"WindowData", CaffeOpType::windowData}}; } // namespace std::unique_ptr<mir::Graph> importModelFromBinaryFile(const std::string &filename) diff --git a/compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp b/compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp index 37edc69c4..a2c881b82 100644 --- a/compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp +++ b/compiler/mir/src/mir_caffe_importer/caffe_op_creator.cpp @@ -374,7 +374,7 @@ static void convertPoolingParam(const caffe::PoolingParameter ¶ms, { // Assuming NCHW format. const std::int32_t padded_input = - input_shape.dim(2 + i) + attributes.padding_before[i] + attributes.padding_after[i]; + input_shape.dim(2 + i) + attributes.padding_before[i] + attributes.padding_after[i]; if ((padded_input - attributes.window[i]) % attributes.strides[i] != 0) ++attributes.padding_after[i]; } @@ -449,7 +449,7 @@ CaffeOpCreator::convertSoftmax(const caffe::LayerParameter &layer, auto input = createOp<ops::TransposeOp>(inputs[0], std::vector<std::size_t>{0, 2, 3, 1}); auto softmax = createOp<ops::SoftmaxOp>(input->getOutput(0), axis); auto result = - createOp<ops::TransposeOp>(softmax->getOutput(0), std::vector<std::size_t>{0, 3, 1, 2}); + createOp<ops::TransposeOp>(softmax->getOutput(0), std::vector<std::size_t>{0, 3, 1, 2}); return {result->getOutput(0)}; } @@ -823,7 +823,7 @@ CaffeOpCreator::convertLSTM(const caffe::LayerParameter &layer, c_t = createOp<ops::AddOp>(createOp<ops::MulOp>(c_cont_t, f_t)->getOutput(0), createOp<ops::MulOp>(i_t, g_t)->getOutput(0)) - ->getOutput(0); + ->getOutput(0); h_t = createOp<ops::MulOp>(createOp<ops::TanhOp>(c_t)->getOutput(0), o_t)->getOutput(0); h_slices[t] = h_t; diff --git a/compiler/mir/src/mir_onnx_importer/AttributeHelpers.h b/compiler/mir/src/mir_onnx_importer/AttributeHelpers.h index 9a93b5b7d..ac1c3cfad 100644 --- a/compiler/mir/src/mir_onnx_importer/AttributeHelpers.h +++ b/compiler/mir/src/mir_onnx_importer/AttributeHelpers.h @@ -76,8 +76,8 @@ inline const onnx::AttributeProto *findAttribute(const onnx::NodeProto &node, { const auto &attributes = node.attribute(); const auto it = std::find_if( - attributes.cbegin(), attributes.cend(), - [&name](const onnx::AttributeProto &attribute) { return attribute.name() == name; }); + attributes.cbegin(), attributes.cend(), + [&name](const onnx::AttributeProto &attribute) { return attribute.name() == name; }); if (it == attributes.cend()) return nullptr; return &*it; diff --git a/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp b/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp index d98e6deae..2091968d8 100644 --- a/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp +++ b/compiler/mir/src/mir_onnx_importer/ConvPoolHelpers.cpp @@ -55,7 +55,7 @@ void inferAutoPadding(const std::string &pad_type, const mir::Shape &input_shape // Assuming input has NCHW format. const std::int32_t residual = input_shape.dim(2 + i) % strides[i]; const std::int32_t total_pad = std::max( - INT32_C(0), residual == 0 ? eff_window_size - strides[i] : eff_window_size - residual); + INT32_C(0), residual == 0 ? eff_window_size - strides[i] : eff_window_size - residual); if (pad_type == "SAME_UPPER") { padding_before[i] = total_pad / 2; diff --git a/compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp b/compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp index f3a9d182d..77656cf48 100644 --- a/compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp +++ b/compiler/mir/src/mir_onnx_importer/ONNXHelpers.cpp @@ -166,9 +166,9 @@ mir::Operation *foldConstants(mir::Graph *graph, mir::Operation *op) } bool is_foldable = - std::all_of(op->getInputs().begin(), op->getInputs().end(), [](mir::Operation::Output *out) { - return out->getNode()->getType() == mir::Operation::Type::constant; - }); + std::all_of(op->getInputs().begin(), op->getInputs().end(), [](mir::Operation::Output *out) { + return out->getNode()->getType() == mir::Operation::Type::constant; + }); if (!is_foldable) return op; diff --git a/compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp b/compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp index 8b996244f..6379b6c87 100644 --- a/compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp +++ b/compiler/mir/src/mir_onnx_importer/ONNXImporterImpl.cpp @@ -134,7 +134,7 @@ void ONNXImporterImpl::collectUnsupportedOps() auto opset = _modelCtx->getDomainOpsetVersion(onnx_node.domain()); NodeConverterRegistry::ConverterFunc converter = - NodeConverterRegistry::getInstance().lookup(op_type, opset); + NodeConverterRegistry::getInstance().lookup(op_type, opset); if (converter == nullptr) problems_op_set.emplace(op_type, opset); @@ -176,7 +176,7 @@ void ONNXImporterImpl::createGraphInputs() } auto elem_type = onnxDataTypeToMirDataType( - (onnx::TensorProto_DataType)input.type().tensor_type().elem_type()); + (onnx::TensorProto_DataType)input.type().tensor_type().elem_type()); mir::TensorType type{elem_type, shape}; auto *op = _graph->create<mir::ops::InputOp>(type); _converterCtx->setOutput(input.name(), op->getOutput(0)); @@ -199,7 +199,7 @@ std::unique_ptr<mir::Graph> ONNXImporterImpl::createIR() auto opset = _modelCtx->getDomainOpsetVersion(onnx_node.domain()); // Get converter NodeConverterRegistry::ConverterFunc converter = - NodeConverterRegistry::getInstance().lookup(op_type, opset); + NodeConverterRegistry::getInstance().lookup(op_type, opset); assert(converter != nullptr); converter(onnx_node, _converterCtx.get()); } diff --git a/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp index a11b18e89..573b41468 100644 --- a/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp +++ b/compiler/mir/src/mir_onnx_importer/ONNXNodeConverterRegistry.cpp @@ -117,8 +117,8 @@ NodeConverterRegistry::ConverterFunc NodeConverterRegistry::lookup(const std::st const VersionMap &conv_map = it->second; auto res = std::lower_bound( - conv_map.crbegin(), conv_map.crend(), opset, - [](const VersionMap::value_type &pair, int64_t opset) { return pair.first > opset; }); + conv_map.crbegin(), conv_map.crend(), opset, + [](const VersionMap::value_type &pair, int64_t opset) { return pair.first > opset; }); if (res == conv_map.crend()) { diff --git a/compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp b/compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp index 503feffc8..1ee136ea6 100644 --- a/compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp +++ b/compiler/mir/src/mir_onnx_importer/Op/AveragePool.cpp @@ -40,7 +40,7 @@ void convertAveragePoolV1(const onnx::NodeProto &onnx_node, ConverterContext *co constexpr int num_spatial_dims = 2; const auto strides = - getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1)); + getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1)); if (strides.size() != num_spatial_dims) throw std::runtime_error("AveragePool: attribute 'strides' has incorrect size."); diff --git a/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp b/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp index 8a6d8cc51..c743ee9e0 100644 --- a/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp +++ b/compiler/mir/src/mir_onnx_importer/Op/BatchNormalization.cpp @@ -81,7 +81,7 @@ void convertBatchNormalizationV9(const onnx::NodeProto &onnx_node, ConverterCont if (scale_op == nullptr || mean_op == nullptr || var_op == nullptr) throw std::runtime_error( - "BatchNormalization: only constant 'scale', 'mean' and 'variance' inputs are supported."); + "BatchNormalization: only constant 'scale', 'mean' and 'variance' inputs are supported."); mir::Tensor<float> scale_accessor(scale_op->getValue()); mir::Tensor<float> mean_accessor(mean_op->getValue()); diff --git a/compiler/mir/src/mir_onnx_importer/Op/Conv.cpp b/compiler/mir/src/mir_onnx_importer/Op/Conv.cpp index 7dc6ce818..7d78826a6 100644 --- a/compiler/mir/src/mir_onnx_importer/Op/Conv.cpp +++ b/compiler/mir/src/mir_onnx_importer/Op/Conv.cpp @@ -139,7 +139,7 @@ void convertConvV1(const onnx::NodeProto &onnx_node, ConverterContext *context) { auto bias = inputs[2]; bias = createOp<mir::ops::ReshapeOp>(graph, bias, mir::Shape{1, bias->getShape().dim(0), 1, 1}) - ->getOutput(0); + ->getOutput(0); result = createOp<mir::ops::AddOp>(graph, result, bias)->getOutput(0); } diff --git a/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp b/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp index 3078a1959..ea0b6fa5e 100644 --- a/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp +++ b/compiler/mir/src/mir_onnx_importer/Op/ConvTranspose.cpp @@ -49,19 +49,19 @@ void convertConvTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext * constexpr int num_spatial_dims = 2; const auto dilations = - getAttributeValue(onnx_node, "dilations", std::vector<std::int32_t>(num_spatial_dims, 1)); + getAttributeValue(onnx_node, "dilations", std::vector<std::int32_t>(num_spatial_dims, 1)); if (dilations.size() != num_spatial_dims) throw std::runtime_error("ConvTranspose: attribute 'dilations' has incorrect size."); if (!std::all_of(dilations.cbegin(), dilations.cend(), [](std::int32_t x) { return x == 1; })) throw std::runtime_error("ConvTranspose: attribute 'dilations' has unsupported value."); const auto strides = - getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1)); + getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1)); if (strides.size() != num_spatial_dims) throw std::runtime_error("ConvTranspose: attribute 'strides' has incorrect size."); - const auto output_padding = getAttributeValue(onnx_node, "output_padding", - std::vector<std::int32_t>(num_spatial_dims, 0)); + const auto output_padding = + getAttributeValue(onnx_node, "output_padding", std::vector<std::int32_t>(num_spatial_dims, 0)); if (output_padding.size() != num_spatial_dims) throw std::runtime_error("ConvTranspose: attribute 'output_padding' has incorrect size."); if (!std::all_of(output_padding.cbegin(), output_padding.cend(), @@ -71,8 +71,8 @@ void convertConvTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext * // Assuming kernel has IOHW format. assert(kernel->getShape().rank() == 4); const auto kernel_size = getAttributeValue( - onnx_node, "kernel_shape", - std::vector<std::int32_t>{kernel->getShape().dim(2), kernel->getShape().dim(3)}); + onnx_node, "kernel_shape", + std::vector<std::int32_t>{kernel->getShape().dim(2), kernel->getShape().dim(3)}); if (kernel_size.size() != num_spatial_dims) throw std::runtime_error("ConvTranspose: attribute 'kernel_shape' has incorrect size."); @@ -92,14 +92,14 @@ void convertConvTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext * attributes.strides = strides; attributes.data_format = mir::DataFormat::NCHW; attributes.padding_type = mir::ops::PaddingType::SameUpper; - result = createOp<mir::ops::DeConv2DOp>(graph, input, kernel, attributes, output_shape) - ->getOutput(0); + result = + createOp<mir::ops::DeConv2DOp>(graph, input, kernel, attributes, output_shape)->getOutput(0); } else { // TODO This code was not tested. throw std::runtime_error( - "ConvTranspose: absence of attribute 'output_shape' is not supported."); + "ConvTranspose: absence of attribute 'output_shape' is not supported."); std::vector<std::int32_t> padding_before(num_spatial_dims, 0); std::vector<std::int32_t> padding_after(num_spatial_dims, 0); if (const auto *pads_attr = findAttribute(onnx_node, "pads")) @@ -128,7 +128,7 @@ void convertConvTransposeV1(const onnx::NodeProto &onnx_node, ConverterContext * { auto bias = inputs[2]; bias = createOp<mir::ops::ReshapeOp>(graph, bias, mir::Shape{1, bias->getShape().dim(0), 1, 1}) - ->getOutput(0); + ->getOutput(0); result = createOp<mir::ops::AddOp>(graph, result, bias)->getOutput(0); } diff --git a/compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp b/compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp index 53e6e1556..6c9ef6621 100644 --- a/compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp +++ b/compiler/mir/src/mir_onnx_importer/Op/MaxPool.cpp @@ -40,7 +40,7 @@ void convertMaxPoolV1(const onnx::NodeProto &onnx_node, ConverterContext *contex constexpr int num_spatial_dims = 2; const auto strides = - getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1)); + getAttributeValue(onnx_node, "strides", std::vector<std::int32_t>(num_spatial_dims, 1)); if (strides.size() != num_spatial_dims) throw std::runtime_error("MaxPool: attribute 'strides' has incorrect size."); diff --git a/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp b/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp index ec43bffb4..9bfe16282 100644 --- a/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp +++ b/compiler/mir/src/mir_onnx_importer/Op/ReduceMean.cpp @@ -52,7 +52,7 @@ void convertReduceMeanV1(const onnx::NodeProto &onnx_node, ConverterContext *con mir::Graph *graph = context->getGraph(); auto result = - createOp<mir::ops::ReduceMeanOp>(graph, inputs[0], reduce_dims, keep_dims)->getOutput(0); + createOp<mir::ops::ReduceMeanOp>(graph, inputs[0], reduce_dims, keep_dims)->getOutput(0); context->setNodeOutputs(onnx_node, {result}); } diff --git a/compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp b/compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp index 346e22cc2..881ec89d3 100644 --- a/compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp +++ b/compiler/mir/src/mir_onnx_importer/Op/Upsample.cpp @@ -52,9 +52,9 @@ void convertUpsampleV1(const onnx::NodeProto &onnx_node, ConverterContext *conte scales_vector.at(3) = w_scale; auto result = - createOp<mir::ops::ResizeOp>(graph, inputs[0], - mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector) - ->getOutput(0); + createOp<mir::ops::ResizeOp>(graph, inputs[0], + mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector) + ->getOutput(0); context->setNodeOutputs(onnx_node, {result}); } @@ -74,7 +74,7 @@ void convertUpsampleV7(const onnx::NodeProto &onnx_node, ConverterContext *conte if (scales_attr->floats_size() != inputs[0]->getShape().rank()) throw std::runtime_error( - "Number of elements of scales should be the same as the rank of input"); + "Number of elements of scales should be the same as the rank of input"); assert(inputs[0]->getShape().rank() == 4 && "Only rank 4 is supported"); std::vector<float> scales_vector(4); @@ -85,9 +85,9 @@ void convertUpsampleV7(const onnx::NodeProto &onnx_node, ConverterContext *conte scales_vector.at(3) = scales_attr->floats(3); auto result = - createOp<mir::ops::ResizeOp>(graph, inputs[0], - mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector) - ->getOutput(0); + createOp<mir::ops::ResizeOp>(graph, inputs[0], + mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector) + ->getOutput(0); context->setNodeOutputs(onnx_node, {result}); } @@ -117,9 +117,9 @@ void convertUpsampleV9(const onnx::NodeProto &onnx_node, ConverterContext *conte scales_vector[i] = scales_tensor.atOffset(i); auto result = - createOp<mir::ops::ResizeOp>(graph, inputs[0], - mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector) - ->getOutput(0); + createOp<mir::ops::ResizeOp>(graph, inputs[0], + mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales_vector) + ->getOutput(0); context->setNodeOutputs(onnx_node, {result}); } diff --git a/compiler/mir/src/mir_tflite_importer/tflite_importer.cpp b/compiler/mir/src/mir_tflite_importer/tflite_importer.cpp index 3f245d2d4..7b91bf0ba 100644 --- a/compiler/mir/src/mir_tflite_importer/tflite_importer.cpp +++ b/compiler/mir/src/mir_tflite_importer/tflite_importer.cpp @@ -105,37 +105,37 @@ void TfliteImporter::import() } static const std::set<tflite::BuiltinOperator> supportedOperators = { - tflite::BuiltinOperator_ADD, - tflite::BuiltinOperator_AVERAGE_POOL_2D, - tflite::BuiltinOperator_CONCATENATION, - tflite::BuiltinOperator_CONV_2D, - tflite::BuiltinOperator_DEPTHWISE_CONV_2D, - tflite::BuiltinOperator_DIV, - tflite::BuiltinOperator_FULLY_CONNECTED, - tflite::BuiltinOperator_HARD_SWISH, - tflite::BuiltinOperator_LEAKY_RELU, - tflite::BuiltinOperator_LOGISTIC, - tflite::BuiltinOperator_MAX_POOL_2D, - tflite::BuiltinOperator_MAXIMUM, - tflite::BuiltinOperator_MEAN, - tflite::BuiltinOperator_MUL, - tflite::BuiltinOperator_PAD, - tflite::BuiltinOperator_RELU, - tflite::BuiltinOperator_RELU6, - tflite::BuiltinOperator_RESHAPE, - tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, - tflite::BuiltinOperator_RSQRT, - tflite::BuiltinOperator_SHAPE, - tflite::BuiltinOperator_SLICE, - tflite::BuiltinOperator_SOFTMAX, - tflite::BuiltinOperator_SQRT, - tflite::BuiltinOperator_SQUARED_DIFFERENCE, - tflite::BuiltinOperator_SQUEEZE, - tflite::BuiltinOperator_STRIDED_SLICE, - tflite::BuiltinOperator_SUB, - tflite::BuiltinOperator_TANH, - tflite::BuiltinOperator_TRANSPOSE, - tflite::BuiltinOperator_TRANSPOSE_CONV, + tflite::BuiltinOperator_ADD, + tflite::BuiltinOperator_AVERAGE_POOL_2D, + tflite::BuiltinOperator_CONCATENATION, + tflite::BuiltinOperator_CONV_2D, + tflite::BuiltinOperator_DEPTHWISE_CONV_2D, + tflite::BuiltinOperator_DIV, + tflite::BuiltinOperator_FULLY_CONNECTED, + tflite::BuiltinOperator_HARD_SWISH, + tflite::BuiltinOperator_LEAKY_RELU, + tflite::BuiltinOperator_LOGISTIC, + tflite::BuiltinOperator_MAX_POOL_2D, + tflite::BuiltinOperator_MAXIMUM, + tflite::BuiltinOperator_MEAN, + tflite::BuiltinOperator_MUL, + tflite::BuiltinOperator_PAD, + tflite::BuiltinOperator_RELU, + tflite::BuiltinOperator_RELU6, + tflite::BuiltinOperator_RESHAPE, + tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, + tflite::BuiltinOperator_RSQRT, + tflite::BuiltinOperator_SHAPE, + tflite::BuiltinOperator_SLICE, + tflite::BuiltinOperator_SOFTMAX, + tflite::BuiltinOperator_SQRT, + tflite::BuiltinOperator_SQUARED_DIFFERENCE, + tflite::BuiltinOperator_SQUEEZE, + tflite::BuiltinOperator_STRIDED_SLICE, + tflite::BuiltinOperator_SUB, + tflite::BuiltinOperator_TANH, + tflite::BuiltinOperator_TRANSPOSE, + tflite::BuiltinOperator_TRANSPOSE_CONV, }; void TfliteImporter::collectUnsupportedOps() @@ -268,8 +268,8 @@ void TfliteImporter::walkOperator(const tflite::SubGraphT *subgraph, const tflit outputs = _opCreator->convertConv2D(op->builtin_options.AsConv2DOptions(), inputs); break; case tflite::BuiltinOperator_DEPTHWISE_CONV_2D: - outputs = _opCreator->convertDepthwiseConv2D(op->builtin_options.AsDepthwiseConv2DOptions(), - inputs); + outputs = + _opCreator->convertDepthwiseConv2D(op->builtin_options.AsDepthwiseConv2DOptions(), inputs); break; case tflite::BuiltinOperator_MAX_POOL_2D: outputs = _opCreator->convertMaxPool2D(op->builtin_options.AsPool2DOptions(), inputs); @@ -279,21 +279,21 @@ void TfliteImporter::walkOperator(const tflite::SubGraphT *subgraph, const tflit break; case tflite::BuiltinOperator_CONCATENATION: outputs = - _opCreator->convertConcatenation(op->builtin_options.AsConcatenationOptions(), inputs); + _opCreator->convertConcatenation(op->builtin_options.AsConcatenationOptions(), inputs); break; case tflite::BuiltinOperator_RESHAPE: outputs = _opCreator->convertReshape(op->builtin_options.AsReshapeOptions(), inputs); break; case tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR: outputs = _opCreator->convertResizeNearestNeighbor( - op->builtin_options.AsResizeNearestNeighborOptions(), inputs); + op->builtin_options.AsResizeNearestNeighborOptions(), inputs); break; case tflite::BuiltinOperator_MEAN: outputs = _opCreator->convertMean(op->builtin_options.AsReducerOptions(), inputs); break; case tflite::BuiltinOperator_FULLY_CONNECTED: outputs = - _opCreator->convertFullyConnected(op->builtin_options.AsFullyConnectedOptions(), inputs); + _opCreator->convertFullyConnected(op->builtin_options.AsFullyConnectedOptions(), inputs); break; case tflite::BuiltinOperator_SOFTMAX: outputs = _opCreator->convertSoftmax(op->builtin_options.AsSoftmaxOptions(), inputs); @@ -333,7 +333,7 @@ void TfliteImporter::walkOperator(const tflite::SubGraphT *subgraph, const tflit break; case tflite::BuiltinOperator_TRANSPOSE_CONV: outputs = - _opCreator->convertTransposeConv(op->builtin_options.AsTransposeConvOptions(), inputs); + _opCreator->convertTransposeConv(op->builtin_options.AsTransposeConvOptions(), inputs); break; case tflite::BuiltinOperator_PAD: outputs = _opCreator->convertPad(op->builtin_options.AsPadOptions(), inputs); @@ -352,7 +352,7 @@ void TfliteImporter::walkOperator(const tflite::SubGraphT *subgraph, const tflit break; case tflite::BuiltinOperator_STRIDED_SLICE: outputs = - _opCreator->convertStridedSlice(op->builtin_options.AsStridedSliceOptions(), inputs); + _opCreator->convertStridedSlice(op->builtin_options.AsStridedSliceOptions(), inputs); break; case tflite::BuiltinOperator_LEAKY_RELU: outputs = _opCreator->convertLeakyReLU(op->builtin_options.AsLeakyReluOptions(), inputs); diff --git a/compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp b/compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp index d9f98da55..58425e9a9 100644 --- a/compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp +++ b/compiler/mir/src/mir_tflite_importer/tflite_op_creator.cpp @@ -92,9 +92,9 @@ static void calculatePadding(mir::ops::PaddingType padding_type, const mir::Shap { // Assuming NHWC format. const std::int32_t total_padding = - (input_shape.dim(1 + i) % strides[i] == 0) - ? std::max(0, window_size[i] - strides[i]) - : std::max(0, window_size[i] - input_shape.dim(1 + i) % strides[i]); + (input_shape.dim(1 + i) % strides[i] == 0) + ? std::max(0, window_size[i] - strides[i]) + : std::max(0, window_size[i] - input_shape.dim(1 + i) % strides[i]); padding_before[i] = total_padding / 2; padding_after[i] = total_padding - padding_before[i]; } @@ -332,7 +332,7 @@ TFLiteOpCreator::convertResizeNearestNeighbor(const tflite::ResizeNearestNeighbo Shape res_shape{input_shape.dim(0), size_tensor.at(mir::Index{0}), size_tensor.at(mir::Index{1}), input_shape.dim(3)}; auto result = - createOp<ops::ResizeOp>(input, ops::ResizeOp::ResizeMethod::nearestNeighbor, res_shape); + createOp<ops::ResizeOp>(input, ops::ResizeOp::ResizeMethod::nearestNeighbor, res_shape); return {result->getOutput(0)}; } diff --git a/compiler/mir/src/ops/AvgPool2DOp.cpp b/compiler/mir/src/ops/AvgPool2DOp.cpp index 52b67303f..945917208 100644 --- a/compiler/mir/src/ops/AvgPool2DOp.cpp +++ b/compiler/mir/src/ops/AvgPool2DOp.cpp @@ -50,7 +50,7 @@ void AvgPool2DOp::inferOutputTypes() // (in_size - window_size + 1 + stride - 1) / stride = // (in_size - window_size) / stride + 1 output_shape.dim(spatial_dim_index) = - (padded_input - _attributes.window[i]) / _attributes.strides[i] + 1; + (padded_input - _attributes.window[i]) / _attributes.strides[i] + 1; } setOutputType(0, {getInput(0)->getElementType(), output_shape}); diff --git a/compiler/mir/src/ops/Conv2DOp.cpp b/compiler/mir/src/ops/Conv2DOp.cpp index 1addc5734..1de73b62d 100644 --- a/compiler/mir/src/ops/Conv2DOp.cpp +++ b/compiler/mir/src/ops/Conv2DOp.cpp @@ -54,7 +54,7 @@ void Conv2DOp::inferOutputTypes() // (in_size - kernel_size + 1 + stride - 1) / stride = // (in_size - kernel_size) / stride + 1 output_shape.dim(spatial_dim_index) = - (padded_input - kernel_shape.dim(1 + i)) / _attributes.strides[i] + 1; + (padded_input - kernel_shape.dim(1 + i)) / _attributes.strides[i] + 1; } auto dt = getInput(0)->getElementType(); diff --git a/compiler/mir/src/ops/DeConv2DOp.cpp b/compiler/mir/src/ops/DeConv2DOp.cpp index 35b111bc0..08829d327 100644 --- a/compiler/mir/src/ops/DeConv2DOp.cpp +++ b/compiler/mir/src/ops/DeConv2DOp.cpp @@ -36,8 +36,8 @@ void DeConv2DOp::inferPaddings() { const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i); const std::int32_t total_padding = - (input_shape.dim(spatial_dim_index) - 1) * _attributes.strides[i] + kernel_shape.dim(i) - - output_shape.dim(spatial_dim_index); + (input_shape.dim(spatial_dim_index) - 1) * _attributes.strides[i] + kernel_shape.dim(i) - + output_shape.dim(spatial_dim_index); switch (_attributes.padding_type) { @@ -85,8 +85,8 @@ void DeConv2DOp::inferOutputTypes() { const int spatial_dim_index = getDataSpatialDimIndex(_attributes.data_format, i); output_shape.dim(spatial_dim_index) = - (input_shape.dim(spatial_dim_index) - 1) * _attributes.strides[i] + kernel_shape.dim(i) - - (_attributes.padding_before.at(i) + _attributes.padding_after.at(i)); + (input_shape.dim(spatial_dim_index) - 1) * _attributes.strides[i] + kernel_shape.dim(i) - + (_attributes.padding_before.at(i) + _attributes.padding_after.at(i)); } setOutputType(0, {getInput(0)->getElementType(), output_shape}); diff --git a/compiler/mir/src/ops/DepthwiseConv2DOp.cpp b/compiler/mir/src/ops/DepthwiseConv2DOp.cpp index 0154bcd09..521d2eb49 100644 --- a/compiler/mir/src/ops/DepthwiseConv2DOp.cpp +++ b/compiler/mir/src/ops/DepthwiseConv2DOp.cpp @@ -50,7 +50,7 @@ void DepthwiseConv2DOp::inferOutputTypes() // (in_size - kernel_size + 1 + stride - 1) / stride = // (in_size - kernel_size) / stride + 1 output_shape.dim(spatial_dim_index) = - (padded_input - kernel_shape.dim(i)) / _attributes.strides[i] + 1; + (padded_input - kernel_shape.dim(i)) / _attributes.strides[i] + 1; } setOutputType(0, {getInput(0)->getElementType(), output_shape}); diff --git a/compiler/mir/src/ops/MaxPool2DOp.cpp b/compiler/mir/src/ops/MaxPool2DOp.cpp index 38e72424e..0cb3aa93c 100644 --- a/compiler/mir/src/ops/MaxPool2DOp.cpp +++ b/compiler/mir/src/ops/MaxPool2DOp.cpp @@ -50,7 +50,7 @@ void MaxPool2DOp::inferOutputTypes() // (in_size - window_size + 1 + stride - 1) / stride = // (in_size - window_size) / stride + 1 output_shape.dim(spatial_dim_index) = - (padded_input - _attributes.window[i]) / _attributes.strides[i] + 1; + (padded_input - _attributes.window[i]) / _attributes.strides[i] + 1; } setOutputType(0, {getInput(0)->getElementType(), output_shape}); diff --git a/compiler/mir/src/ops/PadOp.cpp b/compiler/mir/src/ops/PadOp.cpp index 465856d92..38feaccdc 100644 --- a/compiler/mir/src/ops/PadOp.cpp +++ b/compiler/mir/src/ops/PadOp.cpp @@ -30,7 +30,7 @@ void PadOp::inferOutputTypes() for (int32_t dim = 0; dim < num_dims; ++dim) { out_shape.dim(dim) = - _attributes.padding_before[dim] + input_shape.dim(dim) + _attributes.padding_after[dim]; + _attributes.padding_before[dim] + input_shape.dim(dim) + _attributes.padding_after[dim]; } setOutputType(0, {getInput(0)->getElementType(), out_shape}); diff --git a/compiler/mir/src/ops/TransposeOp.cpp b/compiler/mir/src/ops/TransposeOp.cpp index 92282e17d..d04cdb4f2 100644 --- a/compiler/mir/src/ops/TransposeOp.cpp +++ b/compiler/mir/src/ops/TransposeOp.cpp @@ -22,7 +22,7 @@ namespace ops { TransposeOp::TransposeOp(Output *arg, const std::vector<std::size_t> &axis_order) - : Operation(Type::transpose, {arg}), _axis_order(axis_order) + : Operation(Type::transpose, {arg}), _axis_order(axis_order) { assert(_axis_order.size() == static_cast<std::size_t>(getInputShape(0).rank())); inferOutputTypes(); @@ -34,7 +34,7 @@ void TransposeOp::inferOutputTypes() Shape output_shape(input_shape.rank()); for (std::size_t i = 0; i < _axis_order.size(); ++i) output_shape.dim(static_cast<std::int64_t>(i)) = - input_shape.dim(static_cast<int32_t>(_axis_order.at(i))); + input_shape.dim(static_cast<int32_t>(_axis_order.at(i))); setOutputType(0, {getInput(0)->getElementType(), output_shape}); } diff --git a/compiler/mir/unittests/ShapeInference.cpp b/compiler/mir/unittests/ShapeInference.cpp index bae4ec5e2..c902b1e12 100644 --- a/compiler/mir/unittests/ShapeInference.cpp +++ b/compiler/mir/unittests/ShapeInference.cpp @@ -80,8 +80,8 @@ TEST(ShapeInferenceTest, ResizeWithScale) auto input = g.create<ops::InputOp>(input_type); auto op = - g.create<ops::ResizeOp>(input->getOutput(0), ops::ResizeOp::ResizeMethod::nearestNeighbor, - std::vector<float>{1, 6, 2, 1}); + g.create<ops::ResizeOp>(input->getOutput(0), ops::ResizeOp::ResizeMethod::nearestNeighbor, + std::vector<float>{1, 6, 2, 1}); ASSERT_EQ(result_shape, op->getOutputShape(0)); } diff --git a/compiler/mir/unittests/ShapeRange.cpp b/compiler/mir/unittests/ShapeRange.cpp index 3b32d0c61..3797e3ccc 100644 --- a/compiler/mir/unittests/ShapeRange.cpp +++ b/compiler/mir/unittests/ShapeRange.cpp @@ -29,7 +29,7 @@ struct ParamType template <typename... Args> explicit ParamType(int32_t actual_len, Args &&... args) - : actual_length(actual_len), shape({static_cast<int32_t>(args)...}) + : actual_length(actual_len), shape({static_cast<int32_t>(args)...}) { } }; diff --git a/compiler/mir2loco/src/mir2loco.test.cpp b/compiler/mir2loco/src/mir2loco.test.cpp index 3870caeb5..92ab99488 100644 --- a/compiler/mir2loco/src/mir2loco.test.cpp +++ b/compiler/mir2loco/src/mir2loco.test.cpp @@ -140,10 +140,10 @@ TEST_F(TestTransformer_mir2loco, Avg_Pool_Test) loco::Pull *pull_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0)); loco::FeatureEncode *encode_node = - dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(1)); + dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(1)); loco::AvgPool2D *pool_node = dynamic_cast<loco::AvgPool2D *>(loco_graph->nodes()->at(2)); loco::FeatureDecode *decode_node = - dynamic_cast<loco::FeatureDecode *>(loco_graph->nodes()->at(3)); + dynamic_cast<loco::FeatureDecode *>(loco_graph->nodes()->at(3)); loco::Push *push_node = dynamic_cast<loco::Push *>(loco_graph->nodes()->at(4)); ASSERT_NE(pull_node, nullptr); @@ -188,10 +188,10 @@ TEST_F(TestTransformer_mir2loco, Max_Pool_Test) loco::Pull *pull_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0)); loco::FeatureEncode *encode_node = - dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(1)); + dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(1)); loco::MaxPool2D *pool_node = dynamic_cast<loco::MaxPool2D *>(loco_graph->nodes()->at(2)); loco::FeatureDecode *decode_node = - dynamic_cast<loco::FeatureDecode *>(loco_graph->nodes()->at(3)); + dynamic_cast<loco::FeatureDecode *>(loco_graph->nodes()->at(3)); loco::Push *push_node = dynamic_cast<loco::Push *>(loco_graph->nodes()->at(4)); ASSERT_NE(pull_node, nullptr); @@ -273,7 +273,7 @@ TEST_F(TestTransformer_mir2loco, Reshape_Test) loco::Pull *pull_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0)); loco::Reshape<loco::ReshapeType::Fixed> *reshape_node = - dynamic_cast<loco::Reshape<loco::ReshapeType::Fixed> *>(loco_graph->nodes()->at(1)); + dynamic_cast<loco::Reshape<loco::ReshapeType::Fixed> *>(loco_graph->nodes()->at(1)); loco::Push *push_node = dynamic_cast<loco::Push *>(loco_graph->nodes()->at(2)); ASSERT_NE(pull_node, nullptr); @@ -385,11 +385,11 @@ TEST_F(TestTransformer_mir2loco, Conv2D_Test) loco::Pull *pull_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0)); loco::ConstGen *const_node = dynamic_cast<loco::ConstGen *>(loco_graph->nodes()->at(1)); loco::FeatureEncode *encode_node = - dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(2)); + dynamic_cast<loco::FeatureEncode *>(loco_graph->nodes()->at(2)); loco::FilterEncode *filter_node = dynamic_cast<loco::FilterEncode *>(loco_graph->nodes()->at(3)); loco::Conv2D *conv_node = dynamic_cast<loco::Conv2D *>(loco_graph->nodes()->at(4)); loco::FeatureDecode *decode_node = - dynamic_cast<loco::FeatureDecode *>(loco_graph->nodes()->at(5)); + dynamic_cast<loco::FeatureDecode *>(loco_graph->nodes()->at(5)); loco::Push *push_node = dynamic_cast<loco::Push *>(loco_graph->nodes()->at(6)); ASSERT_NE(pull_node, nullptr); @@ -430,7 +430,7 @@ TEST_F(TestTransformer_mir2loco, Softmax_Test) loco::Pull *pull_node = dynamic_cast<loco::Pull *>(loco_graph->nodes()->at(0)); loco::TensorSoftmax *softmax_node = - dynamic_cast<loco::TensorSoftmax *>(loco_graph->nodes()->at(1)); + dynamic_cast<loco::TensorSoftmax *>(loco_graph->nodes()->at(1)); loco::Push *push_node = dynamic_cast<loco::Push *>(loco_graph->nodes()->at(2)); ASSERT_NE(pull_node, nullptr); @@ -520,7 +520,7 @@ TEST_F(TestTransformer_mir2loco, DepthwiseConv2D_Test) attributes.padding_after = {7, 4}; auto *conv = - mir_graph.create<mir::ops::DepthwiseConv2DOp>(input, filter, attributes)->getOutput(0); + mir_graph.create<mir::ops::DepthwiseConv2DOp>(input, filter, attributes)->getOutput(0); mir_graph.create<mir::ops::OutputOp>(conv); input->setName("x"); @@ -545,7 +545,7 @@ TEST_F(TestTransformer_mir2loco, DepthwiseConv2D_Test) loco::DepthwiseConv2D *dw_conv_node = dynamic_cast<loco::DepthwiseConv2D *>(*encode_uses.begin()); ASSERT_NE(dw_conv_node, nullptr); loco::DepthwiseFilterEncode *filter_node = - dynamic_cast<loco::DepthwiseFilterEncode *>(dw_conv_node->ker()); + dynamic_cast<loco::DepthwiseFilterEncode *>(dw_conv_node->ker()); ASSERT_NE(filter_node, nullptr); ASSERT_EQ(dw_conv_node->ifm(), encode_node); // Check params @@ -611,7 +611,7 @@ TEST_F(TestTransformer_mir2loco, DeConv2D_Test) auto encode_uses = loco::succs(encode_node); ASSERT_EQ(encode_uses.size(), 1); loco::TransposedConv2D *tr_conv_node = - dynamic_cast<loco::TransposedConv2D *>(*encode_uses.begin()); + dynamic_cast<loco::TransposedConv2D *>(*encode_uses.begin()); ASSERT_NE(tr_conv_node, nullptr); loco::FilterEncode *filter_node = dynamic_cast<loco::FilterEncode *>(tr_conv_node->ker()); ASSERT_NE(filter_node, nullptr); @@ -703,8 +703,8 @@ TEST_F(TestTransformer_mir2loco, Transpose_Test) mir::TensorType input_type{mir::DataType::FLOAT32, {2, 7, 9, 5}}; auto *input = mir_graph.create<mir::ops::InputOp>(input_type)->getOutput(0); auto *transpose = - mir_graph.create<mir::ops::TransposeOp>(input, std::vector<std::size_t>{3, 0, 1, 2}) - ->getOutput(0); + mir_graph.create<mir::ops::TransposeOp>(input, std::vector<std::size_t>{3, 0, 1, 2}) + ->getOutput(0); mir_graph.create<mir::ops::OutputOp>(transpose); input->setName("x"); transpose->setName("y"); diff --git a/compiler/moco-log/CMakeLists.txt b/compiler/moco-log/CMakeLists.txt index 036b4e74b..af6052d0c 100644 --- a/compiler/moco-log/CMakeLists.txt +++ b/compiler/moco-log/CMakeLists.txt @@ -5,5 +5,4 @@ add_library(moco_log SHARED ${SOURCES}) target_include_directories(moco_log PUBLIC include) target_link_libraries(moco_log PUBLIC hermes) target_link_libraries(moco_log PRIVATE hermes_std) -target_link_libraries(moco_log PRIVATE stdex) install(TARGETS moco_log DESTINATION lib) diff --git a/compiler/moco-log/src/LoggingContext.cpp b/compiler/moco-log/src/LoggingContext.cpp index a004e1d3d..c75e5e21f 100644 --- a/compiler/moco-log/src/LoggingContext.cpp +++ b/compiler/moco-log/src/LoggingContext.cpp @@ -18,7 +18,8 @@ #include "moco/Log.h" #include <hermes/ConsoleReporter.h> -#include <stdex/Memory.h> + +#include <memory> namespace moco { @@ -30,8 +31,8 @@ hermes::Context *LoggingContext::get(void) if (ctx == nullptr) { ctx = new hermes::Context; - ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>()); - ctx->config(stdex::make_unique<LoggerConfig>()); + ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>()); + ctx->config(std::make_unique<LoggerConfig>()); } return ctx; diff --git a/compiler/moco-tf/CMakeLists.txt b/compiler/moco-tf/CMakeLists.txt index 5516388a4..7c42761ba 100644 --- a/compiler/moco-tf/CMakeLists.txt +++ b/compiler/moco-tf/CMakeLists.txt @@ -19,7 +19,6 @@ target_link_libraries(moco_tf_frontend PRIVATE moco_support) target_link_libraries(moco_tf_frontend PRIVATE bino) target_link_libraries(moco_tf_frontend PRIVATE fipe) target_link_libraries(moco_tf_frontend PRIVATE locop) -target_link_libraries(moco_tf_frontend PRIVATE stdex) target_link_libraries(moco_tf_frontend PRIVATE moco_log) target_link_libraries(moco_tf_frontend PRIVATE pepper_str) target_link_libraries(moco_tf_frontend PRIVATE pepper_strcast) @@ -44,7 +43,6 @@ target_link_libraries(moco_tf_frontend_test fipe) target_link_libraries(moco_tf_frontend_test locop) target_link_libraries(moco_tf_frontend_test moco_log) target_link_libraries(moco_tf_frontend_test moco_tf_frontend) -target_link_libraries(moco_tf_frontend_test stdex) target_link_libraries(moco_tf_frontend_test plier_tf) target_link_libraries(moco_tf_frontend_test locoex_customop) target_link_libraries(moco_tf_frontend_test logo) diff --git a/compiler/moco-tf/requires.cmake b/compiler/moco-tf/requires.cmake index 3e0fabee9..90590e374 100644 --- a/compiler/moco-tf/requires.cmake +++ b/compiler/moco-tf/requires.cmake @@ -2,7 +2,6 @@ require("fipe") require("loco") require("moco") require("locop") -require("stdex") require("moco-log") require("pepper-strcast") require("locomotiv") diff --git a/compiler/moco-tf/src/BroadcastHelper.h b/compiler/moco-tf/src/BroadcastHelper.h index 6238ad269..d4e1bba55 100644 --- a/compiler/moco-tf/src/BroadcastHelper.h +++ b/compiler/moco-tf/src/BroadcastHelper.h @@ -65,7 +65,7 @@ private: * This mimics "tf.broadcast_to" API in TensorFlow. */ static inline auto broadcast_to(const loco::TensorShape &shape) - -> decltype(bino::transform_both(std::declval<BroadcastFunctor>())) + -> decltype(bino::transform_both(std::declval<BroadcastFunctor>())) { return bino::transform_both(BroadcastFunctor{shape}); } diff --git a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp index b59a3f3d7..71f6230b7 100644 --- a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp @@ -24,7 +24,6 @@ #include <loco/Service/ShapeInference.h> -#include <stdex/Memory.h> #include <oops/UserExn.h> namespace diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp index d3cbd4ab3..1d3343933 100644 --- a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp @@ -32,7 +32,7 @@ using plier::tf::DataLayout; void set_filter_enc(loco::FilterEncode *filter_enc) { - auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + auto enc = std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); // In TensorFlow, Conv2dBackpropInput's filter is a 4-D tensor of following shape: // [filter_height, filter_width, out_channels, in_channels] or HWOI or HWNC (in/out in loco sense) @@ -163,9 +163,9 @@ loco::Padding2D Padding2DInference::operator()(void) // 'tight fit' output. When output size (set by 'input sizes' node input) is // larger than tight fit, extra spaces filled with zero. auto tight_output_vertical = tight_output_for_valid_padding( - input().vertical.value(), stride().vertical(), window().vertical()); + input().vertical.value(), stride().vertical(), window().vertical()); auto tight_output_horizontal = tight_output_for_valid_padding( - input().horizontal.value(), stride().horizontal(), window().horizontal()); + input().horizontal.value(), stride().horizontal(), window().horizontal()); if (output().vertical.value() < tight_output_vertical or output().horizontal.value() < tight_output_horizontal) @@ -191,8 +191,8 @@ loco::Padding2D Padding2DInference::operator()(void) auto whole_pad_vertical = padding_needed(input().vertical.value(), output().vertical.value(), stride().vertical(), window().vertical()); auto whole_pad_horizontal = - padding_needed(input().horizontal.value(), output().horizontal.value(), - stride().horizontal(), window().horizontal()); + padding_needed(input().horizontal.value(), output().horizontal.value(), stride().horizontal(), + window().horizontal()); loco::Padding2D res; diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp index a955793a8..30f01cdd3 100644 --- a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp @@ -29,7 +29,7 @@ using plier::tf::DataLayout; void set_filter_enc(loco::FilterEncode *filter_enc) { - auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); + auto enc = std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>(); // In TensorFlow, conv2d filter is a 4-D tensor of following shape: // [filter_height, filter_width, in_channels, out_channels] -> HWIO (HWCN) diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp index 50dddf637..dd04c2427 100644 --- a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp @@ -30,7 +30,7 @@ using plier::tf::DataLayout; void set_filter_enc(loco::DepthwiseFilterEncode *filter_enc) { - auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>(); + auto enc = std::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>(); // In TensorFlow, depthwiseconv2dnative filter is a 4-D tensor of following shape: // [filter_height, filter_width, in_channels, channel_multiplier] -> HWCM @@ -47,28 +47,28 @@ bool canonicalize_depthwiseconv2dnative(loco::Graph *graph, moco::TFDepthwiseCon LOGGER(l); /** - * @note This will replace TFDepthwiseConv2dNative node with Canonical FeatureEncode + - * DepthwiseFilterEncode + DepthwiseConv2D + FeatureDecode - * - * Before - * A -+- TFDepthwiseConv2dNative - C - * | - * B -+ - * - * After - * - * A -+ FeatureEncode ----------------+- DepthwiseConv2D - FeatureDecode - C - * | | - * +-(TFDepthwiseConv2dNative) | - * | | - * B -+ DepthwiseFilterEncode --------+ - * - * Where - * A : ifm of TFDepthwiseConv2dNative - * B : ker of TFDepthwiseConv2dNative - * C : a node that uses TFDepthwiseConv2dNative as an input - * TFDepthwiseConv2dNative is disconnected from other nodes - */ + * @note This will replace TFDepthwiseConv2dNative node with Canonical FeatureEncode + + * DepthwiseFilterEncode + DepthwiseConv2D + FeatureDecode + * + * Before + * A -+- TFDepthwiseConv2dNative - C + * | + * B -+ + * + * After + * + * A -+ FeatureEncode ----------------+- DepthwiseConv2D - FeatureDecode - C + * | | + * +-(TFDepthwiseConv2dNative) | + * | | + * B -+ DepthwiseFilterEncode --------+ + * + * Where + * A : ifm of TFDepthwiseConv2dNative + * B : ker of TFDepthwiseConv2dNative + * C : a node that uses TFDepthwiseConv2dNative as an input + * TFDepthwiseConv2dNative is disconnected from other nodes + */ INFO(l) << "TFNodeCanonicalize TFDepthwiseConv2dNative begin"; diff --git a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp index 36136aed4..28ecc3fc0 100644 --- a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp @@ -20,8 +20,6 @@ #include "loco/Service/TypeInference.h" -#include <stdex/Memory.h> - namespace { diff --git a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp index c53a880a8..1179ef7f6 100644 --- a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp @@ -18,8 +18,6 @@ #include <moco/IR/TFDialect.h> -#include <stdex/Memory.h> - namespace { diff --git a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp index 7965dc931..bb2a71bc0 100644 --- a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp @@ -18,8 +18,6 @@ #include <moco/IR/TFDialect.h> -#include <stdex/Memory.h> - namespace { diff --git a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp index c31dbf6d6..25eae6288 100644 --- a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp @@ -23,7 +23,6 @@ #include <loco/Service/TypeInference.h> -#include <stdex/Memory.h> #include <oops/UserExn.h> namespace diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp index 98af7b693..9fcb76c2a 100644 --- a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp @@ -31,16 +31,16 @@ bool canonicalize_softmax(loco::Graph *graph, moco::TFSoftmax *node) INFO(l) << "TFNodeCanonicalize TFSoftmax begin"; /** - * This will replace shape inferred TFSoftmax node into canonical TensorSoftmax - * - * Before - * In ---- TFSoftmax ---- Out(s) - * - * After - * ------ TFSoftmax - * / - * In ---- TensorSoftmax ----- Out(s) - */ + * This will replace shape inferred TFSoftmax node into canonical TensorSoftmax + * + * Before + * In ---- TFSoftmax ---- Out(s) + * + * After + * ------ TFSoftmax + * / + * In ---- TensorSoftmax ----- Out(s) + */ auto nodeshape = moco::node_shape(node); // Canonicalization into TensorSoftmax is valid when softmax has shape info diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h index ebaf04cfe..0dd31503f 100644 --- a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h +++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h @@ -30,8 +30,8 @@ namespace tf { /** -* @brief Canonicalize TF-dialect TFSoftmax into canonical Softmax node -*/ + * @brief Canonicalize TF-dialect TFSoftmax into canonical Softmax node + */ class SoftmaxCanonicalizer : public SimpleNodeTransform<moco::TFSoftmax> { public: diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp index 574fa3993..47ac40ea8 100644 --- a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp @@ -30,16 +30,16 @@ bool canonicalize_stopgradient(loco::Graph *graph, moco::TFStopGradient *node) INFO(l) << "TFNodeCanonicalize TFStopGradient begin"; /** - * This will replace shape inferred TFStopGradient node into canonical Forward - * - * Before - * In --- TFStopGradient --- Out(s) - * - * After - * -- TFStopGradient - * / - * In --- Forward --- Out(s) - */ + * This will replace shape inferred TFStopGradient node into canonical Forward + * + * Before + * In --- TFStopGradient --- Out(s) + * + * After + * -- TFStopGradient + * / + * In --- Forward --- Out(s) + */ // Create loco node to replace auto forward_node = graph->nodes()->create<loco::Forward>(); diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h index 6a17728a6..8346914c0 100644 --- a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h +++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h @@ -30,8 +30,8 @@ namespace tf { /** -* @brief Canonicalize TF-dialect TFStopGradient into canonical Forward node -*/ + * @brief Canonicalize TF-dialect TFStopGradient into canonical Forward node + */ class StopGradientCanonicalizer : public SimpleNodeTransform<moco::TFStopGradient> { public: diff --git a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp index 081e0e5f9..3adf1733c 100644 --- a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp @@ -18,8 +18,6 @@ #include <moco/IR/TFDialect.h> -#include <stdex/Memory.h> - namespace { diff --git a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp index 3f48a50fc..3b6e3c90c 100644 --- a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp @@ -18,8 +18,6 @@ #include <moco/IR/TFDialect.h> -#include <stdex/Memory.h> - namespace { diff --git a/compiler/moco-tf/src/Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalizer.cpp index 04bc7c57a..8e23d91df 100644 --- a/compiler/moco-tf/src/Canonicalizer.cpp +++ b/compiler/moco-tf/src/Canonicalizer.cpp @@ -56,8 +56,7 @@ #include <logo/Phase.h> -#include <stdex/Memory.h> - +#include <memory> #include <cassert> namespace @@ -92,41 +91,41 @@ void Canonicalizer::canonicalize(loco::Graph *g) const /* TRANSFORM DECLARATION BEGIN */ // Run shape and type inference at the top - phase.emplace_back(stdex::make_unique<ShapeInferencePass>()); - phase.emplace_back(stdex::make_unique<TypeInferencePass>()); + phase.emplace_back(std::make_unique<ShapeInferencePass>()); + phase.emplace_back(std::make_unique<TypeInferencePass>()); - phase.emplace_back(stdex::make_unique<AddCanonicalizer>()); - phase.emplace_back(stdex::make_unique<AvgPoolCanonicalizer>()); + phase.emplace_back(std::make_unique<AddCanonicalizer>()); + phase.emplace_back(std::make_unique<AvgPoolCanonicalizer>()); if (moco::tf::get<moco::tf::Knob::CanonicalizeBiasAdd>()) - phase.emplace_back(stdex::make_unique<BiasAddCanonicalizer>()); - phase.emplace_back(stdex::make_unique<ConcatV2Canonicalizer>()); + phase.emplace_back(std::make_unique<BiasAddCanonicalizer>()); + phase.emplace_back(std::make_unique<ConcatV2Canonicalizer>()); if (moco::tf::get<moco::tf::Knob::CanonicalizeConst>()) - phase.emplace_back(stdex::make_unique<ConstCanonicalizer>()); - phase.emplace_back(stdex::make_unique<Conv2DBackpropInputCanonicalizer>()); + phase.emplace_back(std::make_unique<ConstCanonicalizer>()); + phase.emplace_back(std::make_unique<Conv2DBackpropInputCanonicalizer>()); if (moco::tf::get<moco::tf::Knob::CanonicalizeConv2D>()) - phase.emplace_back(stdex::make_unique<Conv2DCanonicalizer>()); - phase.emplace_back(stdex::make_unique<DepthwiseConv2dNativeCanonicalizer>()); - phase.emplace_back(stdex::make_unique<IdentityCanonicalizer>()); - phase.emplace_back(stdex::make_unique<MaximumCanonicalizer>()); - phase.emplace_back(stdex::make_unique<MaxPoolCanonicalizer>()); - phase.emplace_back(stdex::make_unique<MeanCanonicalizer>()); - phase.emplace_back(stdex::make_unique<MulCanonicalizer>()); - phase.emplace_back(stdex::make_unique<PadCanonicalizer>()); - phase.emplace_back(stdex::make_unique<PlaceholderCanonicalizer>()); - phase.emplace_back(stdex::make_unique<RealDivCanonicalizer>()); - phase.emplace_back(stdex::make_unique<ReluCanonicalizer>()); - phase.emplace_back(stdex::make_unique<Relu6Canonicalizer>()); - phase.emplace_back(stdex::make_unique<ReshapeCanonicalizer>()); - phase.emplace_back(stdex::make_unique<RsqrtCanonicalizer>()); - phase.emplace_back(stdex::make_unique<SoftmaxCanonicalizer>()); - phase.emplace_back(stdex::make_unique<SqrtCanonicalizer>()); + phase.emplace_back(std::make_unique<Conv2DCanonicalizer>()); + phase.emplace_back(std::make_unique<DepthwiseConv2dNativeCanonicalizer>()); + phase.emplace_back(std::make_unique<IdentityCanonicalizer>()); + phase.emplace_back(std::make_unique<MaximumCanonicalizer>()); + phase.emplace_back(std::make_unique<MaxPoolCanonicalizer>()); + phase.emplace_back(std::make_unique<MeanCanonicalizer>()); + phase.emplace_back(std::make_unique<MulCanonicalizer>()); + phase.emplace_back(std::make_unique<PadCanonicalizer>()); + phase.emplace_back(std::make_unique<PlaceholderCanonicalizer>()); + phase.emplace_back(std::make_unique<RealDivCanonicalizer>()); + phase.emplace_back(std::make_unique<ReluCanonicalizer>()); + phase.emplace_back(std::make_unique<Relu6Canonicalizer>()); + phase.emplace_back(std::make_unique<ReshapeCanonicalizer>()); + phase.emplace_back(std::make_unique<RsqrtCanonicalizer>()); + phase.emplace_back(std::make_unique<SoftmaxCanonicalizer>()); + phase.emplace_back(std::make_unique<SqrtCanonicalizer>()); // NOTE SquaredDifference is handled in ResolveSquaredDifference - phase.emplace_back(stdex::make_unique<SqueezeCanonicalizer>()); - phase.emplace_back(stdex::make_unique<StopGradientCanonicalizer>()); - phase.emplace_back(stdex::make_unique<SubCanonicalizer>()); - phase.emplace_back(stdex::make_unique<TanhCanonicalizer>()); + phase.emplace_back(std::make_unique<SqueezeCanonicalizer>()); + phase.emplace_back(std::make_unique<StopGradientCanonicalizer>()); + phase.emplace_back(std::make_unique<SubCanonicalizer>()); + phase.emplace_back(std::make_unique<TanhCanonicalizer>()); // For virtual nodes - phase.emplace_back(stdex::make_unique<TFPushCanonicalizer>()); + phase.emplace_back(std::make_unique<TFPushCanonicalizer>()); /* TRANSFORM DECLARATION END */ ProgressReporter prog(g, logo::PhaseStrategy::Restart); diff --git a/compiler/moco-tf/src/CodecHelper.h b/compiler/moco-tf/src/CodecHelper.h index 85e4e2164..a4ca8d5ca 100644 --- a/compiler/moco-tf/src/CodecHelper.h +++ b/compiler/moco-tf/src/CodecHelper.h @@ -18,7 +18,8 @@ #define __CODEC_HELPER_H__ #include <plier/tf/Convert.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -27,7 +28,7 @@ using plier::tf::DataLayout; void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout) { - auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); + auto enc = std::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>(); if (data_layout == DataLayout::NHWC) { @@ -49,7 +50,7 @@ void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout) void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout) { - auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); + auto dec = std::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>(); if (data_layout == DataLayout::NHWC) { diff --git a/compiler/moco-tf/src/Frontend.cpp b/compiler/moco-tf/src/Frontend.cpp index a17d5dd0e..0d5250b17 100644 --- a/compiler/moco-tf/src/Frontend.cpp +++ b/compiler/moco-tf/src/Frontend.cpp @@ -31,13 +31,13 @@ #include <loco/Service/ShapeInference.h> -#include <stdex/Memory.h> #include <oops/UserExn.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/text_format.h> +#include <memory> #include <iostream> #include <sstream> #include <fstream> @@ -157,7 +157,7 @@ moco::GraphBuilderRegistry make_graph_builder_registry(const moco::ModelSignatur for (const auto &custom_op : sig.customops()) { std::unique_ptr<moco::tf::COpCallGraphBuilder> builder = - stdex::make_unique<moco::tf::COpCallGraphBuilder>(&sig); + std::make_unique<moco::tf::COpCallGraphBuilder>(&sig); registry.add(custom_op, std::move(builder)); } @@ -243,7 +243,7 @@ std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature, auto input = graph->inputs()->at(n); auto input_node = moco::placeholder_node(graph.get(), n); assert(input_node != nullptr); - input->shape(stdex::make_unique<loco::TensorShape>(tensor_shape(input_node))); + input->shape(std::make_unique<loco::TensorShape>(tensor_shape(input_node))); } for (uint32_t n = 0; n < graph->outputs()->size(); ++n) @@ -251,7 +251,7 @@ std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature, auto output = graph->outputs()->at(n); auto output_node = moco::push_node(graph.get(), n); assert(output_node != nullptr); - output->shape(stdex::make_unique<loco::TensorShape>(::tensor_shape(output_node))); + output->shape(std::make_unique<loco::TensorShape>(::tensor_shape(output_node))); } // Convert graph to hold only Canonical dialect diff --git a/compiler/moco-tf/src/Knob.cpp b/compiler/moco-tf/src/Knob.cpp index 0e1c7e0ea..a13895f68 100644 --- a/compiler/moco-tf/src/Knob.cpp +++ b/compiler/moco-tf/src/Knob.cpp @@ -109,12 +109,12 @@ namespace moco namespace tf { -#define KNOB_BOOL(NAME, DEFAULT, DESC) \ - template <> typename KnobTrait<Knob::NAME>::ValueType get<Knob::NAME>(void) \ - { \ - static typename KnobTrait<Knob::NAME>::ValueType value = \ - ::knob_load<typename KnobTrait<Knob::NAME>::ValueType>(::knob_loader(), #NAME, DEFAULT); \ - return value; \ +#define KNOB_BOOL(NAME, DEFAULT, DESC) \ + template <> typename KnobTrait<Knob::NAME>::ValueType get<Knob::NAME>(void) \ + { \ + static typename KnobTrait<Knob::NAME>::ValueType value = \ + ::knob_load<typename KnobTrait<Knob::NAME>::ValueType>(::knob_loader(), #NAME, DEFAULT); \ + return value; \ } #include "Knob.lst" #undef KNOB_BOOL diff --git a/compiler/moco-tf/src/LogHelper.cpp b/compiler/moco-tf/src/LogHelper.cpp index 92ff75569..6b127020a 100644 --- a/compiler/moco-tf/src/LogHelper.cpp +++ b/compiler/moco-tf/src/LogHelper.cpp @@ -74,7 +74,7 @@ namespace tf FormattedGraph fmt(loco::Graph *g) { - auto node_summary_builder = stdex::make_unique<TFNodeSummaryBuilderFactory>(); + auto node_summary_builder = std::make_unique<TFNodeSummaryBuilderFactory>(); return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder))); } diff --git a/compiler/moco-tf/src/Op/COpCall.cpp b/compiler/moco-tf/src/Op/COpCall.cpp index 801196f0f..af4bc9dc4 100644 --- a/compiler/moco-tf/src/Op/COpCall.cpp +++ b/compiler/moco-tf/src/Op/COpCall.cpp @@ -23,9 +23,9 @@ #include <moco/Names.h> #include <moco/tf/Frontend.h> #include <loco.h> -#include <stdex/Memory.h> #include <oops/UserExn.h> +#include <memory> #include <vector> #include <cassert> #include <stdexcept> @@ -37,7 +37,7 @@ class COpCallGraphUpdate final : public moco::GraphUpdate { public: COpCallGraphUpdate(locoex::COpCall *node, const std::vector<moco::TensorName> &input_names) - : _node(node), _input_names(input_names) + : _node(node), _input_names(input_names) { } @@ -94,11 +94,11 @@ void COpCallGraphBuilder::build(const tensorflow::NodeDef &tf_node, if (val.value_case() == tensorflow::AttrValue::kF) { - call_node->attr(name, stdex::make_unique<locoex::COpAttrFloat>(val.f())); + call_node->attr(name, std::make_unique<locoex::COpAttrFloat>(val.f())); } else if (val.value_case() == tensorflow::AttrValue::kI) { - call_node->attr(name, stdex::make_unique<locoex::COpAttrInt>(val.i())); + call_node->attr(name, std::make_unique<locoex::COpAttrInt>(val.i())); } // TODO define more types else @@ -118,7 +118,7 @@ void COpCallGraphBuilder::build(const tensorflow::NodeDef &tf_node, { input_names.emplace_back(TensorName(tf_node.input(i))); } - auto update = stdex::make_unique<COpCallGraphUpdate>(call_node, input_names); + auto update = std::make_unique<COpCallGraphUpdate>(call_node, input_names); updates->enroll(std::move(update)); } diff --git a/compiler/moco-tf/src/Op/COpCall.h b/compiler/moco-tf/src/Op/COpCall.h index 0bb8a93c9..2f0ee1e36 100644 --- a/compiler/moco-tf/src/Op/COpCall.h +++ b/compiler/moco-tf/src/Op/COpCall.h @@ -32,7 +32,9 @@ namespace tf class COpCallGraphBuilder final : public GraphBuilder { public: - COpCallGraphBuilder(const ModelSignature *signature) : _signature(signature) { /* empty */} + COpCallGraphBuilder(const ModelSignature *signature) : _signature(signature) + { /* empty */ + } bool validate(const tensorflow::NodeDef &) const override; void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; diff --git a/compiler/moco-tf/src/Op/COpCall.test.cpp b/compiler/moco-tf/src/Op/COpCall.test.cpp index f13118292..7e1ffa954 100644 --- a/compiler/moco-tf/src/Op/COpCall.test.cpp +++ b/compiler/moco-tf/src/Op/COpCall.test.cpp @@ -27,10 +27,11 @@ #include <loco.h> #include <plier/tf/TestHelper.h> -#include <stdex/Memory.h> #include <gtest/gtest.h> +#include <memory> + using namespace moco::tf::test; namespace @@ -91,7 +92,7 @@ TEST(Call_Test, Call_01) // import moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()}; - registry.add("new_custom_op", stdex::make_unique<moco::tf::COpCallGraphBuilder>(&signature)); + registry.add("new_custom_op", std::make_unique<moco::tf::COpCallGraphBuilder>(&signature)); moco::Importer importer(®istry); std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def); diff --git a/compiler/moco-tf/src/Optimizer.cpp b/compiler/moco-tf/src/Optimizer.cpp index f33b4109b..51e1e1c4f 100644 --- a/compiler/moco-tf/src/Optimizer.cpp +++ b/compiler/moco-tf/src/Optimizer.cpp @@ -22,7 +22,7 @@ #include <logo/Phase.h> -#include <stdex/Memory.h> +#include <memory> namespace moco { @@ -35,48 +35,48 @@ void Optimizer::optimize(loco::Graph *g) const /* TRANSFORM DECLARATION BEGIN */ // Shape inference is required for ResolveRedundantReshape - phase.emplace_back(stdex::make_unique<ShapeInferencePass>()); + phase.emplace_back(std::make_unique<ShapeInferencePass>()); if (moco::tf::get<moco::tf::Knob::ConstantFolding>()) { - phase.emplace_back(stdex::make_unique<logo::ConstantFoldingPass>()); + phase.emplace_back(std::make_unique<logo::ConstantFoldingPass>()); } if (moco::tf::get<moco::tf::Knob::RemoveDeadNode>()) { - phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>()); + phase.emplace_back(std::make_unique<logo::RemoveDeadNodePass>()); } if (moco::tf::get<moco::tf::Knob::ReorderDecode>() && moco::tf::get<moco::tf::Knob::ReorderDecodeTensorBiasAdd>()) { - phase.emplace_back(stdex::make_unique<logo::ReorderDecodePass<loco::TensorBiasAdd>>()); + phase.emplace_back(std::make_unique<logo::ReorderDecodePass<loco::TensorBiasAdd>>()); } if (moco::tf::get<moco::tf::Knob::ReorderDecode>() && moco::tf::get<moco::tf::Knob::ReorderDecodeReLU>()) { - phase.emplace_back(stdex::make_unique<logo::ReorderDecodePass<loco::ReLU>>()); + phase.emplace_back(std::make_unique<logo::ReorderDecodePass<loco::ReLU>>()); } if (moco::tf::get<moco::tf::Knob::SimplifyDomainConversion>()) { - phase.emplace_back(stdex::make_unique<logo::SimplifyDomainConversionPass>()); + phase.emplace_back(std::make_unique<logo::SimplifyDomainConversionPass>()); } if (moco::tf::get<moco::tf::Knob::RemoveForwardNode>()) { - phase.emplace_back(stdex::make_unique<logo::RemoveForwardNodePass>()); + phase.emplace_back(std::make_unique<logo::RemoveForwardNodePass>()); } if (moco::tf::get<moco::tf::Knob::ResolveDuplicateReshape>()) { - phase.emplace_back(stdex::make_unique<logo::ResolveDuplicateReshapePass>()); + phase.emplace_back(std::make_unique<logo::ResolveDuplicateReshapePass>()); } if (moco::tf::get<moco::tf::Knob::ResolveRedundantReshape>()) { - phase.emplace_back(stdex::make_unique<logo::ResolveRedundantReshapePass>()); + phase.emplace_back(std::make_unique<logo::ResolveRedundantReshapePass>()); } /* TRANSFORM DECLARATION END */ diff --git a/compiler/moco-tf/src/ProgressReporter.h b/compiler/moco-tf/src/ProgressReporter.h index 190d972c5..440d29221 100644 --- a/compiler/moco-tf/src/ProgressReporter.h +++ b/compiler/moco-tf/src/ProgressReporter.h @@ -30,7 +30,7 @@ class ProgressReporter : public logo::PhaseEventListener { public: ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy) - : _graph{graph}, _strategy{strategy} + : _graph{graph}, _strategy{strategy} { // DO NOTHING } diff --git a/compiler/moco-tf/src/TFFormattedGraph.h b/compiler/moco-tf/src/TFFormattedGraph.h index f79208536..81978954f 100644 --- a/compiler/moco-tf/src/TFFormattedGraph.h +++ b/compiler/moco-tf/src/TFFormattedGraph.h @@ -19,7 +19,7 @@ #include <locop/FormattedGraph.h> -#include <stdex/Memory.h> +#include <memory> namespace moco { @@ -49,7 +49,7 @@ public: public: std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final { - return stdex::make_unique<MocoNodeSummaryBuilder>(tlb); + return std::make_unique<MocoNodeSummaryBuilder>(tlb); } }; diff --git a/compiler/moco-tf/src/TFOptimizer.cpp b/compiler/moco-tf/src/TFOptimizer.cpp index 2256b99b8..720cd9d9a 100644 --- a/compiler/moco-tf/src/TFOptimizer.cpp +++ b/compiler/moco-tf/src/TFOptimizer.cpp @@ -22,7 +22,7 @@ #include <logo/Phase.h> -#include <stdex/Memory.h> +#include <memory> namespace moco { @@ -36,39 +36,39 @@ void TFOptimizer::optimize(loco::Graph *g) const /* TRANSFORM DECLARATION BEGIN */ if (moco::tf::get<moco::tf::Knob::ResolveFusedBatchNorm>()) { - phase.emplace_back(stdex::make_unique<moco::ResolveFusedBatchNorm>()); + phase.emplace_back(std::make_unique<moco::ResolveFusedBatchNorm>()); } if (moco::tf::get<moco::tf::Knob::FuseBinaryIntoPreceding>()) { - phase.emplace_back(stdex::make_unique<moco::FuseBinaryIntoPreceding>()); + phase.emplace_back(std::make_unique<moco::FuseBinaryIntoPreceding>()); } if (moco::tf::get<moco::tf::Knob::ResolveConstantShape>()) { - phase.emplace_back(stdex::make_unique<moco::ResolveConstantShape>()); + phase.emplace_back(std::make_unique<moco::ResolveConstantShape>()); } if (moco::tf::get<moco::tf::Knob::ResolveReshapeWildcardDim>()) { - phase.emplace_back(stdex::make_unique<moco::ResolveReshapeWildcardDim>()); + phase.emplace_back(std::make_unique<moco::ResolveReshapeWildcardDim>()); } if (moco::tf::get<moco::tf::Knob::ResolveSquaredDifference>()) { - phase.emplace_back(stdex::make_unique<moco::ResolveSquaredDifference>()); + phase.emplace_back(std::make_unique<moco::ResolveSquaredDifference>()); } if (moco::tf::get<moco::tf::Knob::RemoveTFIdentityNode>()) { - phase.emplace_back(stdex::make_unique<moco::RemoveTFIdentityNode>()); + phase.emplace_back(std::make_unique<moco::RemoveTFIdentityNode>()); } if (moco::tf::get<moco::tf::Knob::RemoveDeadNode>()) { - phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>()); + phase.emplace_back(std::make_unique<logo::RemoveDeadNodePass>()); } if (moco::tf::get<moco::tf::Knob::SqueezeReduceNode>()) { - phase.emplace_back(stdex::make_unique<moco::SqueezeReduceNode>()); + phase.emplace_back(std::make_unique<moco::SqueezeReduceNode>()); } // Shape inference is needed for added nodes doing above transformations - phase.emplace_back(stdex::make_unique<moco::tf::ShapeInferencePass>()); - phase.emplace_back(stdex::make_unique<moco::tf::TypeInferencePass>()); + phase.emplace_back(std::make_unique<moco::tf::ShapeInferencePass>()); + phase.emplace_back(std::make_unique<moco::tf::TypeInferencePass>()); /* TRANSFORM DECLARATION END */ ProgressReporter prog(g, logo::PhaseStrategy::Saturate); diff --git a/compiler/moco-tf/src/TestHelper.test.cpp b/compiler/moco-tf/src/TestHelper.test.cpp index 1e8c38e36..36ce1114a 100644 --- a/compiler/moco-tf/src/TestHelper.test.cpp +++ b/compiler/moco-tf/src/TestHelper.test.cpp @@ -48,7 +48,7 @@ void setup_output_node(loco::Graph *graph, loco::Node *last_node) #include <moco/IR/Nodes/TFConst.h> -#include <stdex/Memory.h> +#include <memory> #include <gtest/gtest.h> @@ -62,7 +62,7 @@ namespace test TFNodeBuildTester::TFNodeBuildTester() { _graph = loco::make_graph(); - _tensor_names = stdex::make_unique<moco::SymbolTable>(); + _tensor_names = std::make_unique<moco::SymbolTable>(); } void TFNodeBuildTester::inputs(const std::vector<std::string> &names) @@ -91,8 +91,8 @@ void TFNodeBuildTester::run(tensorflow::NodeDef &nodedef, moco::GraphBuilder &gr { assert(_output != nullptr); - auto node_defs = stdex::make_unique<moco::NodeDefTable>(); - auto updates = stdex::make_unique<moco::UpdateQueue>(); + auto node_defs = std::make_unique<moco::NodeDefTable>(); + auto updates = std::make_unique<moco::UpdateQueue>(); moco::GraphBuilderContext gb_context(_graph.get(), node_defs.get(), _tensor_names.get(), updates.get()); diff --git a/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp index 64ba9dfb1..8f46cfbbc 100644 --- a/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp +++ b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp @@ -46,8 +46,8 @@ bool ShapeInferencePass::run(loco::Graph *graph) loco::MultiDialectShapeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(TFDialect::get(), &tf_rule) - .bind(locoex::COpDialect::get(), &cop_rule); + .bind(TFDialect::get(), &tf_rule) + .bind(locoex::COpDialect::get(), &cop_rule); return loco::apply(&rules).to(graph); } diff --git a/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp index db6cf7521..2e2d4a9c1 100644 --- a/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp +++ b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp @@ -42,8 +42,8 @@ bool TypeInferencePass::run(loco::Graph *graph) loco::MultiDialectTypeInferenceRule rules; rules.bind(loco::CanonicalDialect::get(), &canonical_rule) - .bind(TFDialect::get(), &tf_rule) - .bind(locoex::COpDialect::get(), &cop_rule); + .bind(TFDialect::get(), &tf_rule) + .bind(locoex::COpDialect::get(), &cop_rule); loco::apply(&rules).to(graph); diff --git a/compiler/moco/import/CMakeLists.txt b/compiler/moco/import/CMakeLists.txt index 43107776e..460c2c98b 100644 --- a/compiler/moco/import/CMakeLists.txt +++ b/compiler/moco/import/CMakeLists.txt @@ -7,7 +7,6 @@ target_include_directories(moco_import PRIVATE src) target_include_directories(moco_import PUBLIC include) target_link_libraries(moco_import PUBLIC moco_lang) target_link_libraries(moco_import PUBLIC mio_tf) -target_link_libraries(moco_import PUBLIC stdex) target_link_libraries(moco_import PRIVATE nncc_common) target_link_libraries(moco_import PRIVATE plier_tf) target_link_libraries(moco_import PRIVATE oops) diff --git a/compiler/moco/import/include/moco/Import/GraphBuilderContext.h b/compiler/moco/import/include/moco/Import/GraphBuilderContext.h index ae4f02c2a..76a9644b5 100644 --- a/compiler/moco/import/include/moco/Import/GraphBuilderContext.h +++ b/compiler/moco/import/include/moco/Import/GraphBuilderContext.h @@ -118,7 +118,7 @@ class GraphBuilderContext public: GraphBuilderContext(loco::Graph *g, NodeDefTable *nodedef, SymbolTable *tensor_names, UpdateQueue *updates) - : _g(g), _nodedef(nodedef), _tensor_names(tensor_names), _updates(updates) + : _g(g), _nodedef(nodedef), _tensor_names(tensor_names), _updates(updates) { // DO NOTHING } diff --git a/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h b/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h index da65cffb8..c99dca1cf 100644 --- a/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h +++ b/compiler/moco/import/include/moco/Import/GraphBuilderRegistry.h @@ -82,6 +82,6 @@ private: std::map<const std::string, std::unique_ptr<GraphBuilder>> _builder_map; }; -} // namespace mono +} // namespace moco #endif // __MOCO_IMPORT_GRAPH_BUILDER_REGISTRY_H__ diff --git a/compiler/moco/import/include/moco/Import/Nodes/Softmax.h b/compiler/moco/import/include/moco/Import/Nodes/Softmax.h index 43fbb8852..290818958 100644 --- a/compiler/moco/import/include/moco/Import/Nodes/Softmax.h +++ b/compiler/moco/import/include/moco/Import/Nodes/Softmax.h @@ -23,8 +23,8 @@ namespace moco { /** -* @brief GraphBuilder for Softmax node -*/ + * @brief GraphBuilder for Softmax node + */ class SoftmaxGraphBuilder final : public GraphBuilder { public: diff --git a/compiler/moco/import/src/GraphBuilderRegistry.cpp b/compiler/moco/import/src/GraphBuilderRegistry.cpp index 3a028513f..7e91ca9d0 100644 --- a/compiler/moco/import/src/GraphBuilderRegistry.cpp +++ b/compiler/moco/import/src/GraphBuilderRegistry.cpp @@ -17,45 +17,45 @@ #include "moco/Import/GraphBuilderRegistry.h" #include "moco/Import/Nodes.h" -#include <stdex/Memory.h> +#include <memory> namespace moco { GraphBuilderRegistry::GraphBuilderRegistry() { - add("Add", stdex::make_unique<AddGraphBuilder>()); - add("AvgPool", stdex::make_unique<AvgPoolGraphBuilder>()); - add("BiasAdd", stdex::make_unique<BiasAddGraphBuilder>()); - add("ConcatV2", stdex::make_unique<ConcatV2GraphBuilder>()); - add("Const", stdex::make_unique<ConstGraphBuilder>()); - add("Conv2D", stdex::make_unique<Conv2DGraphBuilder>()); - add("Conv2DBackpropInput", stdex::make_unique<Conv2DBackpropInputGraphBuilder>()); - add("DepthwiseConv2dNative", stdex::make_unique<DepthwiseConv2dNativeGraphBuilder>()); - add("FakeQuantWithMinMaxVars", stdex::make_unique<FakeQuantWithMinMaxVarsGraphBuilder>()); - add("FusedBatchNorm", stdex::make_unique<FusedBatchNormGraphBuilder>()); - add("Identity", stdex::make_unique<IdentityGraphBuilder>()); - add("Maximum", stdex::make_unique<MaximumGraphBuilder>()); - add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>()); - add("Mean", stdex::make_unique<MeanGraphBuilder>()); - add("Mul", stdex::make_unique<MulGraphBuilder>()); - add("Pack", stdex::make_unique<PackGraphBuilder>()); - add("Pad", stdex::make_unique<PadGraphBuilder>()); - add("Placeholder", stdex::make_unique<PlaceholderGraphBuilder>()); - add("RealDiv", stdex::make_unique<RealDivGraphBuilder>()); - add("Relu", stdex::make_unique<ReluGraphBuilder>()); - add("Relu6", stdex::make_unique<Relu6GraphBuilder>()); - add("Reshape", stdex::make_unique<ReshapeGraphBuilder>()); - add("Rsqrt", stdex::make_unique<RsqrtGraphBuilder>()); - add("Shape", stdex::make_unique<ShapeGraphBuilder>()); - add("Softmax", stdex::make_unique<SoftmaxGraphBuilder>()); - add("Sqrt", stdex::make_unique<SqrtGraphBuilder>()); - add("SquaredDifference", stdex::make_unique<SquaredDifferenceGraphBuilder>()); - add("Squeeze", stdex::make_unique<SqueezeGraphBuilder>()); - add("StopGradient", stdex::make_unique<StopGradientGraphBuilder>()); - add("StridedSlice", stdex::make_unique<StridedSliceGraphBuilder>()); - add("Sub", stdex::make_unique<SubGraphBuilder>()); - add("Tanh", stdex::make_unique<TanhGraphBuilder>()); + add("Add", std::make_unique<AddGraphBuilder>()); + add("AvgPool", std::make_unique<AvgPoolGraphBuilder>()); + add("BiasAdd", std::make_unique<BiasAddGraphBuilder>()); + add("ConcatV2", std::make_unique<ConcatV2GraphBuilder>()); + add("Const", std::make_unique<ConstGraphBuilder>()); + add("Conv2D", std::make_unique<Conv2DGraphBuilder>()); + add("Conv2DBackpropInput", std::make_unique<Conv2DBackpropInputGraphBuilder>()); + add("DepthwiseConv2dNative", std::make_unique<DepthwiseConv2dNativeGraphBuilder>()); + add("FakeQuantWithMinMaxVars", std::make_unique<FakeQuantWithMinMaxVarsGraphBuilder>()); + add("FusedBatchNorm", std::make_unique<FusedBatchNormGraphBuilder>()); + add("Identity", std::make_unique<IdentityGraphBuilder>()); + add("Maximum", std::make_unique<MaximumGraphBuilder>()); + add("MaxPool", std::make_unique<MaxPoolGraphBuilder>()); + add("Mean", std::make_unique<MeanGraphBuilder>()); + add("Mul", std::make_unique<MulGraphBuilder>()); + add("Pack", std::make_unique<PackGraphBuilder>()); + add("Pad", std::make_unique<PadGraphBuilder>()); + add("Placeholder", std::make_unique<PlaceholderGraphBuilder>()); + add("RealDiv", std::make_unique<RealDivGraphBuilder>()); + add("Relu", std::make_unique<ReluGraphBuilder>()); + add("Relu6", std::make_unique<Relu6GraphBuilder>()); + add("Reshape", std::make_unique<ReshapeGraphBuilder>()); + add("Rsqrt", std::make_unique<RsqrtGraphBuilder>()); + add("Shape", std::make_unique<ShapeGraphBuilder>()); + add("Softmax", std::make_unique<SoftmaxGraphBuilder>()); + add("Sqrt", std::make_unique<SqrtGraphBuilder>()); + add("SquaredDifference", std::make_unique<SquaredDifferenceGraphBuilder>()); + add("Squeeze", std::make_unique<SqueezeGraphBuilder>()); + add("StopGradient", std::make_unique<StopGradientGraphBuilder>()); + add("StridedSlice", std::make_unique<StridedSliceGraphBuilder>()); + add("Sub", std::make_unique<SubGraphBuilder>()); + add("Tanh", std::make_unique<TanhGraphBuilder>()); // Virtual node like `TFPush` need not to be added here } diff --git a/compiler/moco/import/src/Importer.cpp b/compiler/moco/import/src/Importer.cpp index 3813affce..333f0f6a9 100644 --- a/compiler/moco/import/src/Importer.cpp +++ b/compiler/moco/import/src/Importer.cpp @@ -23,9 +23,9 @@ #include <moco/IR/Nodes/TFPlaceholder.h> #include <moco/IR/TFNode.h> -#include <stdex/Memory.h> #include <oops/UserExn.h> +#include <memory> #include <cassert> #include <sstream> #include <stdexcept> @@ -36,9 +36,9 @@ namespace void convert_graph(const moco::GraphBuilderSource &source, const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def, loco::Graph *graph) { - auto nodedef = stdex::make_unique<moco::NodeDefTable>(); - auto tensor_names = stdex::make_unique<moco::SymbolTable>(); - auto updates = stdex::make_unique<moco::UpdateQueue>(); + auto nodedef = std::make_unique<moco::NodeDefTable>(); + auto tensor_names = std::make_unique<moco::SymbolTable>(); + auto updates = std::make_unique<moco::UpdateQueue>(); moco::GraphBuilderContext gb_context(graph, nodedef.get(), tensor_names.get(), updates.get()); diff --git a/compiler/moco/import/src/Nodes/Add.cpp b/compiler/moco/import/src/Nodes/Add.cpp index 6981a55e1..af743316b 100644 --- a/compiler/moco/import/src/Nodes/Add.cpp +++ b/compiler/moco/import/src/Nodes/Add.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFAdd.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -78,7 +79,7 @@ void AddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext add_input_names.push_back(TensorName(node.input(0))); // x add_input_names.push_back(TensorName(node.input(1))); // y - auto tf_add_update = stdex::make_unique<TFAddGraphUpdate>(tf_add, add_input_names); + auto tf_add_update = std::make_unique<TFAddGraphUpdate>(tf_add, add_input_names); updates->enroll(std::move(tf_add_update)); } diff --git a/compiler/moco/import/src/Nodes/AvgPool.cpp b/compiler/moco/import/src/Nodes/AvgPool.cpp index 6d7fd36bb..95232b977 100644 --- a/compiler/moco/import/src/Nodes/AvgPool.cpp +++ b/compiler/moco/import/src/Nodes/AvgPool.cpp @@ -22,10 +22,10 @@ #include "Convert.h" #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> #include <oops/UserExn.h> +#include <memory> #include <cassert> #include <stdexcept> @@ -40,7 +40,7 @@ class TFAvgPoolGraphUpdate final : public GraphUpdate { public: TFAvgPoolGraphUpdate(TFAvgPool *node, const TensorName &name) - : _avgpool_node(node), _value_name(name) + : _avgpool_node(node), _value_name(name) { } @@ -127,7 +127,7 @@ void AvgPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon tensor_names->enroll(output_name, avgPool_node); // Record ifm inputs to featureEncode_node - auto update = stdex::make_unique<TFAvgPoolGraphUpdate>(avgPool_node, TensorName(node.input(0))); + auto update = std::make_unique<TFAvgPoolGraphUpdate>(avgPool_node, TensorName(node.input(0))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/BiasAdd.cpp b/compiler/moco/import/src/Nodes/BiasAdd.cpp index a3eb91116..d4bc161d5 100644 --- a/compiler/moco/import/src/Nodes/BiasAdd.cpp +++ b/compiler/moco/import/src/Nodes/BiasAdd.cpp @@ -22,10 +22,10 @@ #include <loco.h> #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> #include <oops/UserExn.h> +#include <memory> #include <cassert> #include <vector> @@ -37,7 +37,7 @@ class TFBiasAddGraphUpdate final : public GraphUpdate { public: TFBiasAddGraphUpdate(TFBiasAdd *biasadd, std::vector<TensorName> &names) - : _biasadd(biasadd), _names(names) + : _biasadd(biasadd), _names(names) { } @@ -115,7 +115,7 @@ void BiasAddGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon input_names.push_back(TensorName(node.input(0))); input_names.push_back(TensorName(node.input(1))); - auto update = stdex::make_unique<TFBiasAddGraphUpdate>(tf_bias_add, input_names); + auto update = std::make_unique<TFBiasAddGraphUpdate>(tf_bias_add, input_names); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Concat.cpp b/compiler/moco/import/src/Nodes/Concat.cpp index 8bf8a84b5..dea60a737 100644 --- a/compiler/moco/import/src/Nodes/Concat.cpp +++ b/compiler/moco/import/src/Nodes/Concat.cpp @@ -21,9 +21,9 @@ #include <moco/Names.h> #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> +#include <memory> #include <cassert> namespace @@ -35,7 +35,7 @@ class TFConcatV2GraphUpdate final : public GraphUpdate { public: TFConcatV2GraphUpdate(TFConcatV2 *node, std::vector<TensorName> names) - : _node(node), _names(names) + : _node(node), _names(names) { } @@ -102,7 +102,7 @@ void ConcatV2GraphBuilder::build(const tensorflow::NodeDef &node, TensorName output_name(node.name(), 0); tensor_names->enroll(output_name, concat_node); - auto update = stdex::make_unique<TFConcatV2GraphUpdate>(concat_node, input_names); + auto update = std::make_unique<TFConcatV2GraphUpdate>(concat_node, input_names); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Const.cpp b/compiler/moco/import/src/Nodes/Const.cpp index 15ea717db..7744cf889 100644 --- a/compiler/moco/import/src/Nodes/Const.cpp +++ b/compiler/moco/import/src/Nodes/Const.cpp @@ -228,7 +228,7 @@ void ConstGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderConte read_value_float32(const_node, num_elements, input_tensor); break; - // TODO support other types + // TODO support other types default: assert(false); diff --git a/compiler/moco/import/src/Nodes/Conv2D.cpp b/compiler/moco/import/src/Nodes/Conv2D.cpp index e6b98dcd1..acb9f76c6 100644 --- a/compiler/moco/import/src/Nodes/Conv2D.cpp +++ b/compiler/moco/import/src/Nodes/Conv2D.cpp @@ -24,10 +24,10 @@ #include <loco.h> #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> #include <oops/UserExn.h> +#include <memory> #include <cassert> #include <stdexcept> #include <algorithm> @@ -131,7 +131,7 @@ void Conv2DGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCont input_names.push_back(TensorName(node.input(1))); // kernel // Record ifm inputs to featureEncode_node - auto tfconv2d_update = stdex::make_unique<TFConv2DGraphUpdate>(conv2d, input_names); + auto tfconv2d_update = std::make_unique<TFConv2DGraphUpdate>(conv2d, input_names); updates->enroll(std::move(tfconv2d_update)); } diff --git a/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp index 74c6605ab..10fee9a8e 100644 --- a/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp +++ b/compiler/moco/import/src/Nodes/Conv2DBackpropInput.cpp @@ -21,10 +21,11 @@ #include "Convert.h" #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> #include <oops/UserExn.h> +#include <memory> + namespace { using namespace moco; @@ -34,7 +35,7 @@ class Conv2DBackpropInputGraphUpdate final : public GraphUpdate { public: Conv2DBackpropInputGraphUpdate(TFConv2DBackpropInput *node, std::vector<TensorName> names) - : _node(node), _input_names(names) + : _node(node), _input_names(names) { // DO NOTHING } @@ -132,7 +133,7 @@ void Conv2DBackpropInputGraphBuilder::build(const tensorflow::NodeDef &node, // update auto conv2d_backprop_update = - stdex::make_unique<Conv2DBackpropInputGraphUpdate>(conv2d_backprop, input_names); + std::make_unique<Conv2DBackpropInputGraphUpdate>(conv2d_backprop, input_names); updates->enroll(std::move(conv2d_backprop_update)); } diff --git a/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp index 3991a4d51..62e57207d 100644 --- a/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp +++ b/compiler/moco/import/src/Nodes/DepthwiseConv2dNative.cpp @@ -24,9 +24,9 @@ #include <plier/tf/Convert.h> #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> #include <oops/UserExn.h> +#include <memory> #include <cassert> using namespace plier::tf; @@ -39,7 +39,7 @@ class TFDepthwiseConv2dNativeGraphUpdate final : public GraphUpdate { public: TFDepthwiseConv2dNativeGraphUpdate(TFDepthwiseConv2dNative *node, std::vector<TensorName> names) - : _node(node), _names(names) + : _node(node), _names(names) { } @@ -139,8 +139,8 @@ void DepthwiseConv2dNativeGraphBuilder::build(const tensorflow::NodeDef &node, input_names.push_back(TensorName(node.input(1))); // kernel // Record ifm inputs to featureEncode_node - auto tfdepthwiseconv2dnative_update = stdex::make_unique<TFDepthwiseConv2dNativeGraphUpdate>( - depthwiseconv2d_native_node, input_names); + auto tfdepthwiseconv2dnative_update = + std::make_unique<TFDepthwiseConv2dNativeGraphUpdate>(depthwiseconv2d_native_node, input_names); updates->enroll(std::move(tfdepthwiseconv2dnative_update)); } diff --git a/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp index d2fa3d1eb..0bd354dc5 100644 --- a/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp +++ b/compiler/moco/import/src/Nodes/FakeQuantWithMinMaxVars.cpp @@ -24,8 +24,8 @@ #include <plier/tf/Convert.h> #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> +#include <memory> #include <cassert> using namespace plier::tf; @@ -39,7 +39,7 @@ class TFFakeQuantWithMinMaxVarsGraphUpdate final : public GraphUpdate public: TFFakeQuantWithMinMaxVarsGraphUpdate(TFFakeQuantWithMinMaxVars *node, std::vector<TensorName> names) - : _node(node), _names(names) + : _node(node), _names(names) { } @@ -115,7 +115,7 @@ void FakeQuantWithMinMaxVarsGraphBuilder::build(const tensorflow::NodeDef &node, // Record ifm inputs to featureEncode_node auto tffakequant_update = - stdex::make_unique<TFFakeQuantWithMinMaxVarsGraphUpdate>(fakequant_node, input_names); + std::make_unique<TFFakeQuantWithMinMaxVarsGraphUpdate>(fakequant_node, input_names); updates->enroll(std::move(tffakequant_update)); } diff --git a/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp b/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp index 59f98017c..8fc439ae3 100644 --- a/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp +++ b/compiler/moco/import/src/Nodes/FusedBatchNorm.cpp @@ -19,9 +19,10 @@ #include <moco/IR/Nodes/TFFusedBatchNorm.h> #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> +#include <memory> + namespace { @@ -34,7 +35,7 @@ class FusedBatchNormGraphUpdate final : public GraphUpdate { public: FusedBatchNormGraphUpdate(TFFusedBatchNorm *node, std::vector<TensorName> names) - : _node(node), _names(names) + : _node(node), _names(names) { } @@ -95,7 +96,7 @@ void FusedBatchNormGraphBuilder::build(const tensorflow::NodeDef &node, fbn_input_names.push_back(TensorName(node.input(3))); // mean fbn_input_names.push_back(TensorName(node.input(4))); // variance - auto tf_fbn_update = stdex::make_unique<FusedBatchNormGraphUpdate>(tf_fbn, fbn_input_names); + auto tf_fbn_update = std::make_unique<FusedBatchNormGraphUpdate>(tf_fbn, fbn_input_names); updates->enroll(std::move(tf_fbn_update)); } diff --git a/compiler/moco/import/src/Nodes/Identity.cpp b/compiler/moco/import/src/Nodes/Identity.cpp index 8ca0e2d01..c3b912b48 100644 --- a/compiler/moco/import/src/Nodes/Identity.cpp +++ b/compiler/moco/import/src/Nodes/Identity.cpp @@ -20,8 +20,8 @@ #include <moco/Names.h> #include <loco.h> -#include <stdex/Memory.h> +#include <memory> #include <vector> namespace @@ -33,7 +33,7 @@ class TFIdentityGraphUpdate final : public GraphUpdate { public: TFIdentityGraphUpdate(TFIdentity *node, const std::vector<TensorName> &names) - : _node(node), _names(names) + : _node(node), _names(names) { } @@ -88,7 +88,7 @@ void IdentityGraphBuilder::build(const tensorflow::NodeDef &node, { names.emplace_back(TensorName(node.input(i))); } - auto update = stdex::make_unique<TFIdentityGraphUpdate>(identity_node, names); + auto update = std::make_unique<TFIdentityGraphUpdate>(identity_node, names); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/MaxPool.cpp b/compiler/moco/import/src/Nodes/MaxPool.cpp index 63275a3b8..cf4b21224 100644 --- a/compiler/moco/import/src/Nodes/MaxPool.cpp +++ b/compiler/moco/import/src/Nodes/MaxPool.cpp @@ -24,10 +24,10 @@ #include <loco.h> #include <loco/IR/PermutingCodec.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> #include <oops/UserExn.h> +#include <memory> #include <cassert> #include <stdexcept> @@ -40,7 +40,7 @@ class TFMaxPoolGraphUpdate final : public GraphUpdate { public: TFMaxPoolGraphUpdate(TFMaxPool *node, const TensorName &name) - : _maxpool_node(node), _input_name(name) + : _maxpool_node(node), _input_name(name) { } @@ -132,7 +132,7 @@ void MaxPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon tensor_names->enroll(output_name, maxPool_node); // Record ifm inputs to featureEncode_node - auto update = stdex::make_unique<TFMaxPoolGraphUpdate>(maxPool_node, TensorName(node.input(0))); + auto update = std::make_unique<TFMaxPoolGraphUpdate>(maxPool_node, TensorName(node.input(0))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Maximum.cpp b/compiler/moco/import/src/Nodes/Maximum.cpp index 43bbbabe6..d2d039f27 100644 --- a/compiler/moco/import/src/Nodes/Maximum.cpp +++ b/compiler/moco/import/src/Nodes/Maximum.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFMaximum.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -80,7 +81,7 @@ void MaximumGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon add_input_names.push_back(TensorName(node.input(0))); // x add_input_names.push_back(TensorName(node.input(1))); // y - auto tf_maximum_update = stdex::make_unique<TFMaximumGraphUpdate>(tf_maximum, add_input_names); + auto tf_maximum_update = std::make_unique<TFMaximumGraphUpdate>(tf_maximum, add_input_names); updates->enroll(std::move(tf_maximum_update)); } diff --git a/compiler/moco/import/src/Nodes/Mean.cpp b/compiler/moco/import/src/Nodes/Mean.cpp index 30fb0f1f7..3f559bc41 100644 --- a/compiler/moco/import/src/Nodes/Mean.cpp +++ b/compiler/moco/import/src/Nodes/Mean.cpp @@ -19,9 +19,10 @@ #include <moco/IR/Nodes/TFMean.h> #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> +#include <memory> + namespace { using namespace moco; @@ -34,7 +35,7 @@ class MeanGraphUpdate final : public GraphUpdate public: MeanGraphUpdate(TFMean *node, const TensorName &&input_name, const TensorName &&reduction_indices_name) - : _node(node), _input_name(input_name), _reduction_indices_name(reduction_indices_name) + : _node(node), _input_name(input_name), _reduction_indices_name(reduction_indices_name) { // DO NOTHING } @@ -91,8 +92,8 @@ void MeanGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContex TensorName output_name(node.name(), 0); tensor_names->enroll(output_name, tf_mean); - auto update = stdex::make_unique<MeanGraphUpdate>(tf_mean, TensorName(node.input(0)), - TensorName(node.input(1))); + auto update = std::make_unique<MeanGraphUpdate>(tf_mean, TensorName(node.input(0)), + TensorName(node.input(1))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Mul.cpp b/compiler/moco/import/src/Nodes/Mul.cpp index ab926b59e..91c5a60e5 100644 --- a/compiler/moco/import/src/Nodes/Mul.cpp +++ b/compiler/moco/import/src/Nodes/Mul.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFMul.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -78,7 +79,7 @@ void MulGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext add_input_names.push_back(TensorName(node.input(0))); // x add_input_names.push_back(TensorName(node.input(1))); // y - auto tf_mul_update = stdex::make_unique<TFMulGraphUpdate>(tf_mul, add_input_names); + auto tf_mul_update = std::make_unique<TFMulGraphUpdate>(tf_mul, add_input_names); updates->enroll(std::move(tf_mul_update)); } diff --git a/compiler/moco/import/src/Nodes/Pack.cpp b/compiler/moco/import/src/Nodes/Pack.cpp index 45815a30e..153ee44ef 100644 --- a/compiler/moco/import/src/Nodes/Pack.cpp +++ b/compiler/moco/import/src/Nodes/Pack.cpp @@ -23,9 +23,9 @@ #include <loco.h> #include <loco/IR/NodeShape.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> +#include <memory> #include <cassert> namespace @@ -95,7 +95,7 @@ void PackGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContex TensorName output_name(node.name(), 0); tensor_names->enroll(output_name, pack_node); - auto update = stdex::make_unique<TFPackGraphUpdate>(pack_node, input_names); + auto update = std::make_unique<TFPackGraphUpdate>(pack_node, input_names); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Pad.cpp b/compiler/moco/import/src/Nodes/Pad.cpp index 262a68fa0..c1f466b44 100644 --- a/compiler/moco/import/src/Nodes/Pad.cpp +++ b/compiler/moco/import/src/Nodes/Pad.cpp @@ -19,9 +19,10 @@ #include <moco/IR/Nodes/TFPad.h> #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> +#include <memory> + namespace { @@ -84,7 +85,7 @@ void PadGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext add_input_names.push_back(TensorName(node.input(1))); // paddings // Queue node input update - auto tf_pad_update = stdex::make_unique<TFPadGraphUpdate>(tf_pad, add_input_names); + auto tf_pad_update = std::make_unique<TFPadGraphUpdate>(tf_pad, add_input_names); updates->enroll(std::move(tf_pad_update)); } diff --git a/compiler/moco/import/src/Nodes/RealDiv.cpp b/compiler/moco/import/src/Nodes/RealDiv.cpp index de3d57673..c747a2fb3 100644 --- a/compiler/moco/import/src/Nodes/RealDiv.cpp +++ b/compiler/moco/import/src/Nodes/RealDiv.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFRealDiv.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -79,7 +80,7 @@ void RealDivGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon div_input_names.push_back(TensorName(node.input(0))); // x div_input_names.push_back(TensorName(node.input(1))); // y - auto tf_div_update = stdex::make_unique<TFRealDivGraphUpdate>(tf_div, div_input_names); + auto tf_div_update = std::make_unique<TFRealDivGraphUpdate>(tf_div, div_input_names); updates->enroll(std::move(tf_div_update)); } diff --git a/compiler/moco/import/src/Nodes/Relu.cpp b/compiler/moco/import/src/Nodes/Relu.cpp index eedc8155d..c99e484e2 100644 --- a/compiler/moco/import/src/Nodes/Relu.cpp +++ b/compiler/moco/import/src/Nodes/Relu.cpp @@ -20,8 +20,8 @@ #include <moco/Names.h> #include <loco.h> -#include <stdex/Memory.h> +#include <memory> #include <cassert> #include <stdexcept> @@ -79,7 +79,7 @@ void ReluGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContex tensor_names->enroll(output_name, relu_node); // Queue node input update - auto update = stdex::make_unique<TFReluGraphUpdate>(relu_node, TensorName(node.input(0))); + auto update = std::make_unique<TFReluGraphUpdate>(relu_node, TensorName(node.input(0))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Relu6.cpp b/compiler/moco/import/src/Nodes/Relu6.cpp index 4700ba408..b7bbac5ce 100644 --- a/compiler/moco/import/src/Nodes/Relu6.cpp +++ b/compiler/moco/import/src/Nodes/Relu6.cpp @@ -18,7 +18,7 @@ #include <moco/IR/Nodes/TFRelu6.h> -#include <stdex/Memory.h> +#include <memory> namespace { @@ -73,7 +73,7 @@ void Relu6GraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderConte tensor_names->enroll(output_name, relu_node); // Queue node input update - auto update = stdex::make_unique<TFRelu6GraphUpdate>(relu_node, TensorName(node.input(0))); + auto update = std::make_unique<TFRelu6GraphUpdate>(relu_node, TensorName(node.input(0))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Reshape.cpp b/compiler/moco/import/src/Nodes/Reshape.cpp index 26e22513f..bdcafbf70 100644 --- a/compiler/moco/import/src/Nodes/Reshape.cpp +++ b/compiler/moco/import/src/Nodes/Reshape.cpp @@ -21,8 +21,8 @@ #include <moco/Names.h> #include <plier/tf/Convert.h> #include <loco.h> -#include <stdex/Memory.h> +#include <memory> #include <cassert> #include <stdexcept> @@ -94,7 +94,7 @@ void ReshapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon input_names.push_back(TensorName(node.input(1))); // shape // Queue node input update - auto update = stdex::make_unique<ReshapeGraphUpdate>(reshape, input_names); + auto update = std::make_unique<ReshapeGraphUpdate>(reshape, input_names); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Rsqrt.cpp b/compiler/moco/import/src/Nodes/Rsqrt.cpp index 979ac90c9..f96d99b68 100644 --- a/compiler/moco/import/src/Nodes/Rsqrt.cpp +++ b/compiler/moco/import/src/Nodes/Rsqrt.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFRsqrt.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -74,8 +75,7 @@ void RsqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderConte tensor_names->enroll(output_name, tf_rsqrt); // Queue node input update - auto tf_rsqrt_update = - stdex::make_unique<TFRsqrtGraphUpdate>(tf_rsqrt, TensorName(node.input(0))); + auto tf_rsqrt_update = std::make_unique<TFRsqrtGraphUpdate>(tf_rsqrt, TensorName(node.input(0))); updates->enroll(std::move(tf_rsqrt_update)); } diff --git a/compiler/moco/import/src/Nodes/Shape.cpp b/compiler/moco/import/src/Nodes/Shape.cpp index 1e112ebb0..b7eb339ef 100644 --- a/compiler/moco/import/src/Nodes/Shape.cpp +++ b/compiler/moco/import/src/Nodes/Shape.cpp @@ -19,9 +19,10 @@ #include <moco/IR/Nodes/TFShape.h> #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> +#include <memory> + namespace { using namespace moco; @@ -33,7 +34,7 @@ class ShapeGraphUpdate final : public GraphUpdate { public: ShapeGraphUpdate(TFShape *node, const TensorName &&input_name) - : _node(node), _input_name(input_name) + : _node(node), _input_name(input_name) { // DO NOTHING } @@ -93,7 +94,7 @@ void ShapeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderConte TensorName output_name(node.name(), 0); tensor_names->enroll(output_name, tf_shape); - auto update = stdex::make_unique<ShapeGraphUpdate>(tf_shape, TensorName(node.input(0))); + auto update = std::make_unique<ShapeGraphUpdate>(tf_shape, TensorName(node.input(0))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Softmax.cpp b/compiler/moco/import/src/Nodes/Softmax.cpp index 6f2c609ff..4fa962750 100644 --- a/compiler/moco/import/src/Nodes/Softmax.cpp +++ b/compiler/moco/import/src/Nodes/Softmax.cpp @@ -19,21 +19,22 @@ #include <moco/IR/Nodes/TFSoftmax.h> #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> +#include <memory> + namespace { using namespace moco; /** -* @brief GraphUpdate for Softmax node -*/ + * @brief GraphUpdate for Softmax node + */ class SoftmaxGraphUpdate final : public GraphUpdate { public: SoftmaxGraphUpdate(TFSoftmax *node, const TensorName &&input_name) - : _node(node), _input_name(input_name) + : _node(node), _input_name(input_name) { // DO NOTHING } @@ -79,7 +80,7 @@ void SoftmaxGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon TensorName output_name(node.name(), 0); tensor_names->enroll(output_name, tf_softmax); - auto update = stdex::make_unique<SoftmaxGraphUpdate>(tf_softmax, TensorName(node.input(0))); + auto update = std::make_unique<SoftmaxGraphUpdate>(tf_softmax, TensorName(node.input(0))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/Sqrt.cpp b/compiler/moco/import/src/Nodes/Sqrt.cpp index f891e48f6..0dbe15ede 100644 --- a/compiler/moco/import/src/Nodes/Sqrt.cpp +++ b/compiler/moco/import/src/Nodes/Sqrt.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFSqrt.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -74,7 +75,7 @@ void SqrtGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContex tensor_names->enroll(output_name, tf_sqrt); // Queue node input update - auto tf_sqrt_update = stdex::make_unique<TFSqrtGraphUpdate>(tf_sqrt, TensorName(node.input(0))); + auto tf_sqrt_update = std::make_unique<TFSqrtGraphUpdate>(tf_sqrt, TensorName(node.input(0))); updates->enroll(std::move(tf_sqrt_update)); } diff --git a/compiler/moco/import/src/Nodes/SquaredDifference.cpp b/compiler/moco/import/src/Nodes/SquaredDifference.cpp index 17a1fe93d..441f02a19 100644 --- a/compiler/moco/import/src/Nodes/SquaredDifference.cpp +++ b/compiler/moco/import/src/Nodes/SquaredDifference.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFSquaredDifference.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -33,7 +34,7 @@ class TFSquaredDifferenceGraphUpdate final : public GraphUpdate { public: TFSquaredDifferenceGraphUpdate(TFSquaredDifference *node, std::vector<TensorName> names) - : _node(node), _names(names) + : _node(node), _names(names) { } @@ -85,7 +86,7 @@ void SquaredDifferenceGraphBuilder::build(const tensorflow::NodeDef &node, // Queue node input update auto tf_sqrt_update = - stdex::make_unique<TFSquaredDifferenceGraphUpdate>(tf_sqdiff, add_input_names); + std::make_unique<TFSquaredDifferenceGraphUpdate>(tf_sqdiff, add_input_names); updates->enroll(std::move(tf_sqrt_update)); } diff --git a/compiler/moco/import/src/Nodes/Squeeze.cpp b/compiler/moco/import/src/Nodes/Squeeze.cpp index 1b4ebae6f..b013b840f 100644 --- a/compiler/moco/import/src/Nodes/Squeeze.cpp +++ b/compiler/moco/import/src/Nodes/Squeeze.cpp @@ -21,10 +21,11 @@ #include <moco/Names.h> #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> #include <oops/UserExn.h> +#include <memory> + namespace { using namespace moco; @@ -36,7 +37,7 @@ class SqueezeGraphUpdate final : public GraphUpdate { public: SqueezeGraphUpdate(TFSqueeze *node, const TensorName &&input_name) - : _node(node), _input_name(input_name) + : _node(node), _input_name(input_name) { // DO NOTHING } @@ -105,7 +106,7 @@ void SqueezeGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderCon TensorName output_name(node.name(), 0); tensor_names->enroll(output_name, tf_squeeze); - auto update = stdex::make_unique<SqueezeGraphUpdate>(tf_squeeze, TensorName(node.input(0))); + auto update = std::make_unique<SqueezeGraphUpdate>(tf_squeeze, TensorName(node.input(0))); updates->enroll(std::move(update)); } diff --git a/compiler/moco/import/src/Nodes/StopGradient.cpp b/compiler/moco/import/src/Nodes/StopGradient.cpp index 9caec6943..82f49dc4a 100644 --- a/compiler/moco/import/src/Nodes/StopGradient.cpp +++ b/compiler/moco/import/src/Nodes/StopGradient.cpp @@ -20,7 +20,8 @@ #include <loco.h> #include <plier/tf/Convert.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -80,7 +81,7 @@ void StopGradientGraphBuilder::build(const tensorflow::NodeDef &node, // Queue node input update auto tf_stopgradient_update = - stdex::make_unique<TFStopGradientGraphUpdate>(tf_stopgradient, TensorName(node.input(0))); + std::make_unique<TFStopGradientGraphUpdate>(tf_stopgradient, TensorName(node.input(0))); updates->enroll(std::move(tf_stopgradient_update)); } diff --git a/compiler/moco/import/src/Nodes/StridedSlice.cpp b/compiler/moco/import/src/Nodes/StridedSlice.cpp index 06d388be0..b0744a7e2 100644 --- a/compiler/moco/import/src/Nodes/StridedSlice.cpp +++ b/compiler/moco/import/src/Nodes/StridedSlice.cpp @@ -24,10 +24,11 @@ #include "Convert.h" #include <loco.h> -#include <stdex/Memory.h> #include <plier/tf/Convert.h> #include <oops/UserExn.h> +#include <memory> + namespace { using namespace moco; @@ -36,7 +37,7 @@ class TFStridedSliceGraphUpdate final : public GraphUpdate { public: TFStridedSliceGraphUpdate(TFStridedSlice *node, std::vector<TensorName> names) - : _node(node), _names(names) + : _node(node), _names(names) { } @@ -179,7 +180,7 @@ void StridedSliceGraphBuilder::build(const tensorflow::NodeDef &node, input_names.push_back(TensorName(node.input(2))); // end input_names.push_back(TensorName(node.input(3))); // strides - auto tfconv2d_update = stdex::make_unique<TFStridedSliceGraphUpdate>(stridedslice, input_names); + auto tfconv2d_update = std::make_unique<TFStridedSliceGraphUpdate>(stridedslice, input_names); updates->enroll(std::move(tfconv2d_update)); } diff --git a/compiler/moco/import/src/Nodes/Sub.cpp b/compiler/moco/import/src/Nodes/Sub.cpp index bdad81d67..4a657663e 100644 --- a/compiler/moco/import/src/Nodes/Sub.cpp +++ b/compiler/moco/import/src/Nodes/Sub.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFSub.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -78,7 +79,7 @@ void SubGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext sub_input_names.push_back(TensorName(node.input(0))); // x sub_input_names.push_back(TensorName(node.input(1))); // y - auto tf_sub_update = stdex::make_unique<TFSubGraphUpdate>(tf_sub, sub_input_names); + auto tf_sub_update = std::make_unique<TFSubGraphUpdate>(tf_sub, sub_input_names); updates->enroll(std::move(tf_sub_update)); } diff --git a/compiler/moco/import/src/Nodes/Tanh.cpp b/compiler/moco/import/src/Nodes/Tanh.cpp index c89fa862a..3a0b0a334 100644 --- a/compiler/moco/import/src/Nodes/Tanh.cpp +++ b/compiler/moco/import/src/Nodes/Tanh.cpp @@ -19,7 +19,8 @@ #include <moco/IR/Nodes/TFTanh.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> namespace { @@ -74,7 +75,7 @@ void TanhGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContex tensor_names->enroll(output_name, tf_tanh); // Queue node input update - auto tf_tanh_update = stdex::make_unique<TFTanhGraphUpdate>(tf_tanh, TensorName(node.input(0))); + auto tf_tanh_update = std::make_unique<TFTanhGraphUpdate>(tf_tanh, TensorName(node.input(0))); updates->enroll(std::move(tf_tanh_update)); } diff --git a/compiler/moco/import/src/TestHelper.test.cpp b/compiler/moco/import/src/TestHelper.test.cpp index 06c3dd372..d0390ad32 100644 --- a/compiler/moco/import/src/TestHelper.test.cpp +++ b/compiler/moco/import/src/TestHelper.test.cpp @@ -17,7 +17,8 @@ #include "TestHelper.h" #include <moco/IR/Nodes/TFConst.h> -#include <stdex/Memory.h> + +#include <memory> #include <gtest/gtest.h> @@ -29,7 +30,7 @@ namespace test TFNodeBuildTester::TFNodeBuildTester() { _graph = loco::make_graph(); - _tensor_names = stdex::make_unique<moco::SymbolTable>(); + _tensor_names = std::make_unique<moco::SymbolTable>(); } void TFNodeBuildTester::inputs(const std::vector<std::string> &names) @@ -71,8 +72,8 @@ void TFNodeBuildTester::run(tensorflow::NodeDef &nodedef, moco::GraphBuilder &gr { assert(_output != nullptr); - auto node_defs = stdex::make_unique<moco::NodeDefTable>(); - auto updates = stdex::make_unique<moco::UpdateQueue>(); + auto node_defs = std::make_unique<moco::NodeDefTable>(); + auto updates = std::make_unique<moco::UpdateQueue>(); moco::GraphBuilderContext gb_context(_graph.get(), node_defs.get(), _tensor_names.get(), updates.get()); diff --git a/compiler/moco/lang/CMakeLists.txt b/compiler/moco/lang/CMakeLists.txt index a64fdf92a..2543f2563 100644 --- a/compiler/moco/lang/CMakeLists.txt +++ b/compiler/moco/lang/CMakeLists.txt @@ -7,7 +7,6 @@ target_include_directories(moco_lang PRIVATE src) target_include_directories(moco_lang PUBLIC include) target_link_libraries(moco_lang PUBLIC loco) target_link_libraries(moco_lang PRIVATE nncc_common) -target_link_libraries(moco_lang PRIVATE stdex) install(TARGETS moco_lang DESTINATION lib) # moco_tf_frontend requires moco_lang if(NOT ENABLE_TEST) diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h index 43e620d24..69d867436 100644 --- a/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFConv2DBackpropInput.h @@ -68,7 +68,7 @@ node { * Note that this convention is against loco canonical's convention. */ class TFConv2DBackpropInput final - : public FixedArityNode<3, TFNodeImpl<TFOpcode::Conv2DBackpropInput>> + : public FixedArityNode<3, TFNodeImpl<TFOpcode::Conv2DBackpropInput>> { public: loco::Node *input_sizes(void) const { return at(0)->node(); } diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h b/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h index aefc0b5d9..2d7fa0c10 100644 --- a/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFDepthwiseConv2dNative.h @@ -25,7 +25,7 @@ namespace moco { class TFDepthwiseConv2dNative final - : public FixedArityNode<2, TFNodeImpl<TFOpcode::DepthwiseConv2dNative>> + : public FixedArityNode<2, TFNodeImpl<TFOpcode::DepthwiseConv2dNative>> { public: loco::Node *input(void) const { return at(0)->node(); } diff --git a/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h b/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h index ec54da596..55baac7de 100644 --- a/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h +++ b/compiler/moco/lang/include/moco/IR/Nodes/TFFakeQuantWithMinMaxVars.h @@ -25,7 +25,7 @@ namespace moco { class TFFakeQuantWithMinMaxVars final - : public FixedArityNode<3, TFNodeImpl<TFOpcode::FakeQuantWithMinMaxVars>> + : public FixedArityNode<3, TFNodeImpl<TFOpcode::FakeQuantWithMinMaxVars>> { public: loco::Node *inputs(void) const { return at(0)->node(); } diff --git a/compiler/moco/lang/src/IR/TFDialect.cpp b/compiler/moco/lang/src/IR/TFDialect.cpp index 35bbcc2c9..959ef98f5 100644 --- a/compiler/moco/lang/src/IR/TFDialect.cpp +++ b/compiler/moco/lang/src/IR/TFDialect.cpp @@ -21,8 +21,7 @@ #include <loco/IR/GraphInputIndex.h> #include <loco/IR/GraphOutputIndex.h> -#include <stdex/Memory.h> - +#include <memory> #include <cassert> #include <stdexcept> @@ -78,8 +77,8 @@ namespace moco TFDialect::TFDialect() { - service<loco::GraphInputIndexQueryService>(stdex::make_unique<GiiQueryServiceImpl>()); - service<loco::GraphOutputIndexQueryService>(stdex::make_unique<GoiQueryServiceImpl>()); + service<loco::GraphInputIndexQueryService>(std::make_unique<GiiQueryServiceImpl>()); + service<loco::GraphOutputIndexQueryService>(std::make_unique<GoiQueryServiceImpl>()); } loco::Dialect *TFDialect::get(void) diff --git a/compiler/moco/lang/src/IR/TFNode.cpp b/compiler/moco/lang/src/IR/TFNode.cpp index ab9356196..55c0e0c64 100644 --- a/compiler/moco/lang/src/IR/TFNode.cpp +++ b/compiler/moco/lang/src/IR/TFNode.cpp @@ -17,6 +17,7 @@ #include "moco/IR/TFNode.h" #include "moco/IR/TFDialect.h" +#include <memory> #include <cassert> namespace moco @@ -26,9 +27,6 @@ const loco::Dialect *TFNode::dialect(void) const { return TFDialect::get(); } } // namespace moco -// TODO move this to appropriate place -#include <stdex/Memory.h> - namespace moco { @@ -60,7 +58,7 @@ loco::GraphInputIndex index(const TFPlaceholder *node) void index(TFPlaceholder *node, const loco::GraphInputIndex index) { - node->annot(stdex::make_unique<GraphInputIndexAnnotation>(index)); + node->annot(std::make_unique<GraphInputIndexAnnotation>(index)); } loco::TensorShape tensor_shape(const TFPlaceholder *node) diff --git a/compiler/moco/pass/CMakeLists.txt b/compiler/moco/pass/CMakeLists.txt index 1eba86283..40c3d5a49 100644 --- a/compiler/moco/pass/CMakeLists.txt +++ b/compiler/moco/pass/CMakeLists.txt @@ -9,7 +9,6 @@ target_link_libraries(moco_pass PUBLIC loco) target_link_libraries(moco_pass PUBLIC logo_core) target_link_libraries(moco_pass PUBLIC moco_lang) target_link_libraries(moco_pass PRIVATE moco_support) -target_link_libraries(moco_pass PRIVATE stdex) target_link_libraries(moco_pass PRIVATE oops) install(TARGETS moco_pass DESTINATION lib) @@ -23,4 +22,3 @@ GTest_AddTest(moco_pass_test ${TESTS}) target_include_directories(moco_pass_test PRIVATE src) target_link_libraries(moco_pass_test moco_pass) target_link_libraries(moco_pass_test moco_support) -target_link_libraries(moco_pass_test stdex) diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h index 5528b8612..a5e25a0ce 100644 --- a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h +++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldMul.h @@ -26,7 +26,7 @@ namespace moco /** * @brief Constant folder for Const + Mul -> Const -*/ + */ class ConstantFoldMul : public logo::Pass { public: diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h index fc6bc0ace..f99c633ac 100644 --- a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h +++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldPack.h @@ -28,7 +28,7 @@ namespace moco /** * @brief Constant folder for Const + Pack -> Const -*/ + */ class ConstantFoldPack : public logo::Pass { public: diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h index 1e3492c2c..f57bdc05e 100644 --- a/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h +++ b/compiler/moco/pass/include/moco/Pass/Passes/ConstantFoldStridedSlice.h @@ -26,7 +26,7 @@ namespace moco /** * @brief Constant folder for Const + StridedSlice -> Const -*/ + */ class ConstantFoldStridedSlice : public logo::Pass { public: diff --git a/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h b/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h index 24e3567c0..4d5318c35 100644 --- a/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h +++ b/compiler/moco/pass/include/moco/Pass/Passes/FuseBinaryIntoPreceding.h @@ -26,7 +26,7 @@ namespace moco /** * @brief Fuse TFAdd, TFMul to preceding TFConv2D or TFDepthWiseConv2D -*/ + */ class FuseBinaryIntoPreceding : public logo::Pass { public: diff --git a/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h b/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h index ce5ea0bb0..1910a9ac7 100644 --- a/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h +++ b/compiler/moco/pass/include/moco/Pass/Passes/ResolveFusedBatchNorm.h @@ -26,7 +26,7 @@ namespace moco /** * @brief Trasform TFFusedBatchNorm into TFAdd + TFRsqrt + TFMul + TFBatchNorm -*/ + */ class ResolveFusedBatchNorm : public logo::Pass { public: diff --git a/compiler/moco/pass/src/ConstantFoldAdd.test.cpp b/compiler/moco/pass/src/ConstantFoldAdd.test.cpp index bc9489fbd..fdfbfb8d3 100644 --- a/compiler/moco/pass/src/ConstantFoldAdd.test.cpp +++ b/compiler/moco/pass/src/ConstantFoldAdd.test.cpp @@ -19,7 +19,8 @@ #include <moco/IR/TFNodes.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> #include <gtest/gtest.h> @@ -60,7 +61,7 @@ TEST(ConstantFoldAdd, basic_vector) } setup_output_node(&graph, add_node); - auto pass = stdex::make_unique<moco::ConstantFoldAdd>(); + auto pass = std::make_unique<moco::ConstantFoldAdd>(); bool cont = true; while (cont) { @@ -92,7 +93,7 @@ TEST(ConstantFoldAdd, basic_refinedet_1) } setup_output_node(&graph, add_node); - auto pass = stdex::make_unique<moco::ConstantFoldAdd>(); + auto pass = std::make_unique<moco::ConstantFoldAdd>(); bool cont = true; while (cont) { diff --git a/compiler/moco/pass/src/ConstantFoldHelper.cpp b/compiler/moco/pass/src/ConstantFoldHelper.cpp index 79b04863c..9dd5e00cd 100644 --- a/compiler/moco/pass/src/ConstantFoldHelper.cpp +++ b/compiler/moco/pass/src/ConstantFoldHelper.cpp @@ -164,7 +164,7 @@ void apply_binary_s32(const moco::TFConst *lhs, const moco::TFConst *rhs, moco:: for (uint32_t e = 0; e < nume; e++) { output->at<loco::DataType::S32>(e) = - f.apply(lhs->at<loco::DataType::S32>(e), rhs->at<loco::DataType::S32>(e)); + f.apply(lhs->at<loco::DataType::S32>(e), rhs->at<loco::DataType::S32>(e)); } } @@ -180,7 +180,7 @@ void apply_binary_f32(const moco::TFConst *lhs, const moco::TFConst *rhs, moco:: for (uint32_t e = 0; e < nume; e++) { output->at<loco::DataType::FLOAT32>(e) = - f.apply(lhs->at<loco::DataType::FLOAT32>(e), rhs->at<loco::DataType::FLOAT32>(e)); + f.apply(lhs->at<loco::DataType::FLOAT32>(e), rhs->at<loco::DataType::FLOAT32>(e)); } } diff --git a/compiler/moco/pass/src/ConstantFoldMul.test.cpp b/compiler/moco/pass/src/ConstantFoldMul.test.cpp index 4e9b78fd4..c7e7d9e65 100644 --- a/compiler/moco/pass/src/ConstantFoldMul.test.cpp +++ b/compiler/moco/pass/src/ConstantFoldMul.test.cpp @@ -19,7 +19,8 @@ #include <moco/IR/TFNodes.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> #include <gtest/gtest.h> @@ -60,7 +61,7 @@ TEST(ConstantFoldMul, basic_vector) } setup_output_node(&graph, mul_node); - auto pass = stdex::make_unique<moco::ConstantFoldMul>(); + auto pass = std::make_unique<moco::ConstantFoldMul>(); bool cont = true; while (cont) { @@ -92,7 +93,7 @@ TEST(ConstantFoldMul, basic_refinedet_1) } setup_output_node(&graph, mul_node); - auto pass = stdex::make_unique<moco::ConstantFoldMul>(); + auto pass = std::make_unique<moco::ConstantFoldMul>(); bool cont = true; while (cont) { diff --git a/compiler/moco/pass/src/ConstantFoldPack.test.cpp b/compiler/moco/pass/src/ConstantFoldPack.test.cpp index cb6eff0c8..c0fa48c7b 100644 --- a/compiler/moco/pass/src/ConstantFoldPack.test.cpp +++ b/compiler/moco/pass/src/ConstantFoldPack.test.cpp @@ -19,7 +19,8 @@ #include <moco/IR/TFNodes.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> #include <gtest/gtest.h> @@ -69,7 +70,7 @@ TEST(ConstantFoldPack, basic_scalar4_vector) identity->input(pack_node); setup_output_node(&graph, identity); - auto pass = stdex::make_unique<moco::ConstantFoldPack>(); + auto pass = std::make_unique<moco::ConstantFoldPack>(); bool cont = true; while (cont) { diff --git a/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp b/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp index b5bada221..3e8449977 100644 --- a/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp +++ b/compiler/moco/pass/src/ConstantFoldStridedSlice.test.cpp @@ -19,7 +19,8 @@ #include <moco/IR/TFNodes.h> #include <loco.h> -#include <stdex/Memory.h> + +#include <memory> #include <gtest/gtest.h> @@ -83,7 +84,7 @@ TEST(ConstantFoldStridedSlice, basic_matrix55_11) } setup_output_node(&graph, sslice_node); - auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + auto pass = std::make_unique<moco::ConstantFoldStridedSlice>(); bool cont = true; while (cont) { @@ -121,7 +122,7 @@ TEST(ConstantFoldStridedSlice, basic_vector4_0) } setup_output_node(&graph, sslice_node); - auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + auto pass = std::make_unique<moco::ConstantFoldStridedSlice>(); bool cont = true; while (cont) { @@ -157,7 +158,7 @@ TEST(ConstantFoldStridedSlice, basic_vector4_1) } setup_output_node(&graph, sslice_node); - auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + auto pass = std::make_unique<moco::ConstantFoldStridedSlice>(); bool cont = true; while (cont) { @@ -193,7 +194,7 @@ TEST(ConstantFoldStridedSlice, basic_vector4_2) } setup_output_node(&graph, sslice_node); - auto pass = stdex::make_unique<moco::ConstantFoldStridedSlice>(); + auto pass = std::make_unique<moco::ConstantFoldStridedSlice>(); bool cont = true; while (cont) { diff --git a/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp b/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp index f97546a80..9374dd5f9 100644 --- a/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp +++ b/compiler/moco/pass/src/Passes/FuseBinaryIntoPreceding.cpp @@ -318,7 +318,7 @@ bool fuse_to_preceding(loco::Graph *graph, moco::TFMul *node) fused_node = fused_conv_node<FuseType::Conv2D, moco::TFConv2D>(graph, mulparam, conv2d); else if (auto dw_conv2d = dynamic_cast<moco::TFDepthwiseConv2dNative *>(precedingOp)) fused_node = fused_conv_node<FuseType::DepthwiseConv2D, moco::TFDepthwiseConv2dNative>( - graph, mulparam, dw_conv2d); + graph, mulparam, dw_conv2d); // Not ready yet if (fused_node == nullptr) @@ -515,7 +515,7 @@ bool FuseBinaryIntoPreceding::run(loco::Graph *graph) } } { - // TODO support Div + // TODO support Div } { diff --git a/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp b/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp index b66add1ae..44e92e9a7 100644 --- a/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp +++ b/compiler/moco/pass/src/Passes/ResolveSquaredDifference.cpp @@ -24,8 +24,6 @@ #include <loco/IR/NodeShape.h> #include <loco/Service/ShapeInference.h> -#include <stdex/Memory.h> - namespace { diff --git a/compiler/moco/requires.cmake b/compiler/moco/requires.cmake index 1a7d36454..18b3a76aa 100644 --- a/compiler/moco/requires.cmake +++ b/compiler/moco/requires.cmake @@ -1,6 +1,5 @@ require("loco") require("locop") -require("stdex") require("moco-log") require("plier-tf") require("mio-tf") diff --git a/compiler/moco/service/CMakeLists.txt b/compiler/moco/service/CMakeLists.txt index dff0233b1..5213f718e 100644 --- a/compiler/moco/service/CMakeLists.txt +++ b/compiler/moco/service/CMakeLists.txt @@ -9,7 +9,6 @@ target_link_libraries(moco_service PUBLIC loco) target_link_libraries(moco_service PUBLIC moco_lang) target_link_libraries(moco_service PRIVATE moco_support) target_link_libraries(moco_service PRIVATE nncc_common) -target_link_libraries(moco_service PRIVATE stdex) target_link_libraries(moco_service PRIVATE oops) install(TARGETS moco_service DESTINATION lib) diff --git a/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp b/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp index 98434155e..6a9864dc5 100644 --- a/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp +++ b/compiler/moco/service/src/Service/TFShapeInferenceRule.cpp @@ -302,7 +302,7 @@ public: // output count is from input count, depth is from kernel 'CM' which is dim(2) * dim(3) auto output_feature_shape = input_feature_shape; output_feature_shape.depth() = - loco::Dimension(ker_tensor_shape.dim(2).value() * ker_tensor_shape.dim(3).value()); + loco::Dimension(ker_tensor_shape.dim(2).value() * ker_tensor_shape.dim(3).value()); auto output_plane_shape = infer_plane_shape(input_plane_shape); diff --git a/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h b/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h index 52324700a..c8a547681 100644 --- a/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h +++ b/compiler/moco/support/include/moco/Support/TFShapeInferenceHelper.h @@ -136,11 +136,11 @@ protected: if (_padding == "VALID") { res.height = - (p.input.height.value() + p.stride.height.value() - p.effective_window.height.value()) / - p.stride.height.value(); + (p.input.height.value() + p.stride.height.value() - p.effective_window.height.value()) / + p.stride.height.value(); res.width = - (p.input.width.value() + p.stride.width.value() - p.effective_window.width.value()) / - p.stride.width.value(); + (p.input.width.value() + p.stride.width.value() - p.effective_window.width.value()) / + p.stride.width.value(); } else if (_padding == "SAME") { diff --git a/compiler/nest/core/include/nest/expr/AddNode.h b/compiler/nest/core/include/nest/expr/AddNode.h index b9b5afb22..bb95692b6 100644 --- a/compiler/nest/core/include/nest/expr/AddNode.h +++ b/compiler/nest/core/include/nest/expr/AddNode.h @@ -30,7 +30,7 @@ class AddNode final : public Node { public: AddNode(const std::shared_ptr<expr::Node> &lhs, const std::shared_ptr<expr::Node> &rhs) - : _lhs{lhs}, _rhs{rhs} + : _lhs{lhs}, _rhs{rhs} { // DO NOTHING } diff --git a/compiler/nest/core/include/nest/expr/DerefNode.h b/compiler/nest/core/include/nest/expr/DerefNode.h index 19adfe3b3..8e3cc5690 100644 --- a/compiler/nest/core/include/nest/expr/DerefNode.h +++ b/compiler/nest/core/include/nest/expr/DerefNode.h @@ -31,7 +31,7 @@ class DerefNode final : public Node public: template <typename... Args> DerefNode(const DomainID &id, Args &&... indicies) - : _id{id}, _sub{std::forward<Args>(indicies)...} + : _id{id}, _sub{std::forward<Args>(indicies)...} { // DO NOTHING } diff --git a/compiler/nest/core/include/nest/expr/MulNode.h b/compiler/nest/core/include/nest/expr/MulNode.h index f388b33a3..bbf64d9bc 100644 --- a/compiler/nest/core/include/nest/expr/MulNode.h +++ b/compiler/nest/core/include/nest/expr/MulNode.h @@ -30,7 +30,7 @@ class MulNode final : public Node { public: MulNode(const std::shared_ptr<expr::Node> &lhs, const std::shared_ptr<expr::Node> &rhs) - : _lhs{lhs}, _rhs{rhs} + : _lhs{lhs}, _rhs{rhs} { // DO NOTHING } diff --git a/compiler/nest/core/src/Block.test.cpp b/compiler/nest/core/src/Block.test.cpp index d8faa0bdb..c48fcfa35 100644 --- a/compiler/nest/core/src/Block.test.cpp +++ b/compiler/nest/core/src/Block.test.cpp @@ -24,7 +24,7 @@ struct DummyNode final : public nest::stmt::Node { // Dummy Node for testing }; -} +} // namespace TEST(BLOCK, use_case_1) { diff --git a/compiler/nest/core/src/Closure.test.cpp b/compiler/nest/core/src/Closure.test.cpp index 495e2186a..458179fb8 100644 --- a/compiler/nest/core/src/Closure.test.cpp +++ b/compiler/nest/core/src/Closure.test.cpp @@ -23,7 +23,7 @@ namespace struct DummyNode final : public nest::expr::Node { }; -} +} // namespace TEST(Closure, ctor) { diff --git a/compiler/nest/core/src/Expr.test.cpp b/compiler/nest/core/src/Expr.test.cpp index 2e26c234a..1b2e7135a 100644 --- a/compiler/nest/core/src/Expr.test.cpp +++ b/compiler/nest/core/src/Expr.test.cpp @@ -25,7 +25,7 @@ namespace struct DummyNode final : public nest::expr::Node { }; -} +} // namespace TEST(EXPR, operator_sum) { diff --git a/compiler/nest/core/src/Ret.test.cpp b/compiler/nest/core/src/Ret.test.cpp index a85223578..98f47d897 100644 --- a/compiler/nest/core/src/Ret.test.cpp +++ b/compiler/nest/core/src/Ret.test.cpp @@ -23,7 +23,7 @@ namespace struct DummyNode final : public nest::expr::Node { }; -} +} // namespace TEST(RET, ctor) { diff --git a/compiler/nest/core/src/expr/AddNode.test.cpp b/compiler/nest/core/src/expr/AddNode.test.cpp index dba6cc826..d8ef1d08b 100644 --- a/compiler/nest/core/src/expr/AddNode.test.cpp +++ b/compiler/nest/core/src/expr/AddNode.test.cpp @@ -25,7 +25,7 @@ namespace struct DummyNode final : public nest::expr::Node { }; -} +} // namespace TEST(ADD_NODE, cast) { diff --git a/compiler/nest/core/src/expr/DerefNode.test.cpp b/compiler/nest/core/src/expr/DerefNode.test.cpp index 125d8bf1e..d0badd509 100644 --- a/compiler/nest/core/src/expr/DerefNode.test.cpp +++ b/compiler/nest/core/src/expr/DerefNode.test.cpp @@ -25,7 +25,7 @@ namespace struct DummyNode final : public nest::expr::Node { }; -} +} // namespace TEST(DEREF_NODE, cast) { diff --git a/compiler/nest/core/src/expr/MulNode.test.cpp b/compiler/nest/core/src/expr/MulNode.test.cpp index 85cb5a56e..bccbcb3b5 100644 --- a/compiler/nest/core/src/expr/MulNode.test.cpp +++ b/compiler/nest/core/src/expr/MulNode.test.cpp @@ -25,7 +25,7 @@ namespace struct DummyNode final : public nest::expr::Node { }; -} +} // namespace TEST(MUL_NODE, cast) { diff --git a/compiler/nest/core/src/stmt/PushNode.test.cpp b/compiler/nest/core/src/stmt/PushNode.test.cpp index c02c69220..fb58a125e 100644 --- a/compiler/nest/core/src/stmt/PushNode.test.cpp +++ b/compiler/nest/core/src/stmt/PushNode.test.cpp @@ -25,7 +25,7 @@ namespace struct DummyExprNode final : public nest::expr::Node { }; -} +} // namespace TEST(STMT_PUSH_NODE, cast) { diff --git a/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp b/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp index 3a5b9ecaf..cad05cc1d 100644 --- a/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp +++ b/compiler/nnc/backends/acl_soft_backend/AclCppGenerator.cpp @@ -30,7 +30,7 @@ using namespace std; namespace fs = boost::filesystem; AclCppCodeGenerator::AclCppCodeGenerator(string output_dir, string artifact_name) - : _output_dir(std::move(output_dir)), _artifact_name(std::move(artifact_name)) + : _output_dir(std::move(output_dir)), _artifact_name(std::move(artifact_name)) { } diff --git a/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp b/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp index b5e3734ae..0abe3ec72 100644 --- a/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp +++ b/compiler/nnc/backends/acl_soft_backend/AclCppOpGenerator.cpp @@ -33,8 +33,8 @@ using namespace std; using namespace mir; AclCppOpGenerator::AclCppOpGenerator(const string &name, ostream &par_out) - : _parOut(par_out), _module(name), _constrBlock(nullptr), _infBlock(nullptr), - _clScheduler(AF::id("arm_compute::CLScheduler")) + : _parOut(par_out), _module(name), _constrBlock(nullptr), _infBlock(nullptr), + _clScheduler(AF::id("arm_compute::CLScheduler")) { } @@ -60,13 +60,14 @@ const ArtifactModule &AclCppOpGenerator::generate(mir::Graph *g) _parInVar = _artifactClass->var(false, "std::ifstream", "_parIn"); _parIn = _parInVar->use(); string par_file_name = _module.name() + ".par"; - _constrBlock->call("open", {AF::lit("\"" + par_file_name + "\""), - AF::lit("std::ios_base::in | std::ios_base::binary")}, - _parIn); + _constrBlock->call( + "open", + {AF::lit("\"" + par_file_name + "\""), AF::lit("std::ios_base::in | std::ios_base::binary")}, + _parIn); auto file_fail = _constrBlock->ifCond(AF::call("fail", {}, _parIn)); auto file_fail_block = file_fail->getBlock(); file_fail_block->addStatement( - AF::lit("throw std::string(\"Failed to open file: " + par_file_name + " for reading\")")); + AF::lit("throw std::string(\"Failed to open file: " + par_file_name + " for reading\")")); // Traverse the computational graph. g->accept(this); @@ -89,8 +90,8 @@ void AclCppOpGenerator::visit(ops::ConcatOp &op) const auto *ir_output = op.getOutput(0); static const char *axis_names[] = { - "arm_compute::DataLayoutDimension::BATCHES", "arm_compute::DataLayoutDimension::CHANNEL", - "arm_compute::DataLayoutDimension::HEIGHT", "arm_compute::DataLayoutDimension::WIDTH"}; + "arm_compute::DataLayoutDimension::BATCHES", "arm_compute::DataLayoutDimension::CHANNEL", + "arm_compute::DataLayoutDimension::HEIGHT", "arm_compute::DataLayoutDimension::WIDTH"}; int axis = op.getAxis(); assert(axis >= 0 && axis < static_cast<int>(sizeof(axis_names) / sizeof(axis_names[0])) && @@ -105,8 +106,8 @@ void AclCppOpGenerator::visit(ops::ConcatOp &op) for (const Operation::Output *ir_input : ir_inputs) _constrBlock->call("push_back", {AF::ref(AF::id(tensorName(ir_input)))}, inputs); - auto layer = genLayer("arm_compute::CLConcatenateLayer", prefix, - {inputs, AF::ref(out), AF::lit(axis_name)}); + auto layer = + genLayer("arm_compute::CLConcatenateLayer", prefix, {inputs, AF::ref(out), AF::lit(axis_name)}); addToPersistentTensors(out); genLayerExecution(layer); @@ -214,13 +215,13 @@ shared_ptr<ArtifactVariable> AclCppOpGenerator::genPadStrideInfo(const Op &op, c string var_name = prefix + "_pad_stride_info"; list<std::shared_ptr<ArtifactExpr>> var_init_params = { - AF::lit(to_string(strides.dim(1))), - AF::lit(to_string(strides.dim(0))), - AF::lit(to_string(padding_before.at(1))), - AF::lit(to_string(padding_after.at(1))), - AF::lit(to_string(padding_before.at(0))), - AF::lit(to_string(padding_after.at(0))), - AF::lit("arm_compute::DimensionRoundingType::FLOOR")}; + AF::lit(to_string(strides.dim(1))), + AF::lit(to_string(strides.dim(0))), + AF::lit(to_string(padding_before.at(1))), + AF::lit(to_string(padding_after.at(1))), + AF::lit(to_string(padding_before.at(0))), + AF::lit(to_string(padding_after.at(0))), + AF::lit("arm_compute::DimensionRoundingType::FLOOR")}; auto pad_stride_info_var = block->var(type_name, var_name, {}, var_init_params); @@ -316,7 +317,7 @@ static bool shouldSerializeConstant(const ops::ConstantOp &op) // themselves, // so we don't serialize them here, also we don't serialize tensors from dangling ConstantOp static std::map<Operation::Type, std::size_t> self_serializing_ops_to_inputs{ - {Operation::Type::conv2D, 1}, {Operation::Type::fullyConnected, 1}}; + {Operation::Type::conv2D, 1}, {Operation::Type::fullyConnected, 1}}; for (Operation::Use use : op.getOutput(0)->getUses()) { @@ -420,8 +421,8 @@ void AclCppOpGenerator::visit(ops::PadOp &op) for (int i = 0; i < ir_input->getShape().rank(); ++i) { auto pad_var = _constrBlock->var( - "arm_compute::PaddingInfo", prefix + "_pad_" + to_string(i), {}, - {AF::lit(to_string(padding_before[i])), AF::lit(to_string(padding_after[i]))}); + "arm_compute::PaddingInfo", prefix + "_pad_" + to_string(i), {}, + {AF::lit(to_string(padding_before[i])), AF::lit(to_string(padding_after[i]))}); auto pad = pad_var->use(); _constrBlock->call("push_back", {pad}, pad_list); } @@ -430,7 +431,7 @@ void AclCppOpGenerator::visit(ops::PadOp &op) // FIXME Set up the `constant_value` parameter. assert(op.getPaddingValue() == 0.0f); auto layer = - genLayer("arm_compute::CLPadLayer", prefix, {AF::ref(input), AF::ref(out), pad_list}); + genLayer("arm_compute::CLPadLayer", prefix, {AF::ref(input), AF::ref(out), pad_list}); genLayerExecution(layer); } @@ -449,7 +450,7 @@ void AclCppOpGenerator::genPooling(Op &op, const std::string &pooling_type, bool // Transpose data from MIR format to format compatible with ACL const string transposed_input_name = output_tensor_name + "transposed_input"; shared_ptr<ArtifactId> transposed_input = - genTransposeMIRtoACL(transposed_input_name, ir_input->getShape(), in_id); + genTransposeMIRtoACL(transposed_input_name, ir_input->getShape(), in_id); const string layer_name = output_tensor_name + "_pooling_layer"; @@ -459,31 +460,31 @@ void AclCppOpGenerator::genPooling(Op &op, const std::string &pooling_type, bool // Create kernel window info shared_ptr<ArtifactVariable> kernel_window_var = _constrBlock->var( - "arm_compute::Size2D", layer_name + "_kernel_window", {}, - {AF::lit(to_string(op.getWindowSize()[1])), AF::lit(to_string(op.getWindowSize()[0]))}); + "arm_compute::Size2D", layer_name + "_kernel_window", {}, + {AF::lit(to_string(op.getWindowSize()[1])), AF::lit(to_string(op.getWindowSize()[0]))}); shared_ptr<ArtifactId> kernel_window = kernel_window_var->use(); // Create pooling info: pooling type, kernel info, strides, etc shared_ptr<ArtifactVariable> pooling_info_var = - _constrBlock->var("arm_compute::PoolingLayerInfo", layer_name + "_pooling_info", {}, - {AF::lit(pooling_type), kernel_window, pad_stride_info, - AF::lit(exclude_padding ? "true" : "false")}); + _constrBlock->var("arm_compute::PoolingLayerInfo", layer_name + "_pooling_info", {}, + {AF::lit(pooling_type), kernel_window, pad_stride_info, + AF::lit(exclude_padding ? "true" : "false")}); shared_ptr<ArtifactId> pooling_info = pooling_info_var->use(); // Generate auxiliary tensor to hold transposed output of pool in NCHW format Shape transposed_output_shape = transposeShape<0, 3, 1, 2>(ir_output->getShape()); shared_ptr<ArtifactId> transposed_output = - genTensor(layer_name + "_out_transpose", transposed_output_shape); + genTensor(layer_name + "_out_transpose", transposed_output_shape); // Actual layer creation shared_ptr<ArtifactId> layer = - genLayer("arm_compute::CLPoolingLayer", layer_name, - {AF::ref(transposed_input), AF::ref(transposed_output), pooling_info}); + genLayer("arm_compute::CLPoolingLayer", layer_name, + {AF::ref(transposed_input), AF::ref(transposed_output), pooling_info}); genTensorAllocation(_infBlock, transposed_output); genLayerExecution(layer); shared_ptr<ArtifactId> output = - genTransposeACLtoMIR(output_tensor_name, transposed_output_shape, transposed_output); + genTransposeACLtoMIR(output_tensor_name, transposed_output_shape, transposed_output); genTensorDeallocation(_infBlock, transposed_input); genTensorDeallocation(_infBlock, transposed_output); @@ -521,13 +522,13 @@ void AclCppOpGenerator::genConvolution(Op &op, const string &acl_func_name, cons // Generate auxiliary tensor to hold transposed input of convolution in NCHW format shared_ptr<ArtifactId> transposed_input = - genTransposeMIRtoACL(output_tensor_name + "_transposed_input", ir_input->getShape(), input); + genTransposeMIRtoACL(output_tensor_name + "_transposed_input", ir_input->getShape(), input); // Create the transposed output tensor in the DOM. const string transposed_output_name = output_tensor_name + "_transposed_output"; Shape transposed_output_shape = transposeShape<0, 3, 1, 2>(ir_output->getShape()); shared_ptr<ArtifactId> transposed_output = - genTensor(transposed_output_name, transposed_output_shape); + genTensor(transposed_output_name, transposed_output_shape); string operation_name = output_tensor_name + suffix; @@ -564,7 +565,7 @@ void AclCppOpGenerator::genConvolution(Op &op, const string &acl_func_name, cons // Generate auxiliar tensor to hold transposed output of convolution in NHWC format shared_ptr<ArtifactId> output = - genTransposeACLtoMIR(output_tensor_name, transposed_output_shape, transposed_output); + genTransposeACLtoMIR(output_tensor_name, transposed_output_shape, transposed_output); genTensorDeallocation(_infBlock, transposed_input); genTensorDeallocation(_infBlock, transposed_output); @@ -589,9 +590,9 @@ void AclCppOpGenerator::genActivation(const Operation &op, const std::string &ac // constructor. This instance profide information about the concrete activation function, // like: ReLU, Tanh etc and two optional parameter (alpha and betha) needed by some activations. auto activation_info_var = _constrBlock->var( - "arm_compute::ActivationLayerInfo", prefix + "_activation_info", {}, - {AF::lit("arm_compute::ActivationLayerInfo::ActivationFunction::" + activation_name), - AF::lit(to_string(a)), AF::lit(to_string(b))}); + "arm_compute::ActivationLayerInfo", prefix + "_activation_info", {}, + {AF::lit("arm_compute::ActivationLayerInfo::ActivationFunction::" + activation_name), + AF::lit(to_string(a)), AF::lit(to_string(b))}); auto activation_info = activation_info_var->use(); // Create an instance of the CLActivationLayer class as a member of the artifact class. @@ -619,9 +620,10 @@ shared_ptr<ArtifactId> AclCppOpGenerator::genAddition(const string &prefix, size auto arithmetic_add_layer = arithmetic_add_layer_var->use(); // Generate the call: arithmetic_add_layer.configure(&in1, &in2, &out); - _constrBlock->call("configure", {AF::ref(in1), AF::ref(in2), AF::ref(out), - AF::lit("arm_compute::ConvertPolicy::WRAP")}, - arithmetic_add_layer); + _constrBlock->call( + "configure", + {AF::ref(in1), AF::ref(in2), AF::ref(out), AF::lit("arm_compute::ConvertPolicy::WRAP")}, + arithmetic_add_layer); // Generate the call: arithmetic_add_layer.run(); _infBlock->call("run", {}, arithmetic_add_layer); @@ -696,8 +698,8 @@ string AclCppOpGenerator::tensorName(const Operation::Output *ir_tensor) const if (!tensor_name.empty()) { tensor_name = "_" + tensor_name; - replace_if(tensor_name.begin(), tensor_name.end(), [](char c) { return std::isalnum(c) == 0; }, - '_'); + replace_if( + tensor_name.begin(), tensor_name.end(), [](char c) { return std::isalnum(c) == 0; }, '_'); } else { @@ -740,7 +742,7 @@ shared_ptr<ArtifactId> AclCppOpGenerator::genTensor(const string &name, const Sh const char *type_name = "arm_compute::TensorShape"; shared_ptr<ArtifactId> shape = - genVectorInitializedVar(_constrBlock, type_name, name + "_shape", shape_vectorized); + genVectorInitializedVar(_constrBlock, type_name, name + "_shape", shape_vectorized); _constrBlock->call("initializeTensor", {id, shape}); if (gen_accessor) @@ -903,7 +905,7 @@ void AclCppOpGenerator::genTranspose(const std::shared_ptr<nnc::ArtifactId> &inp // Create operation parameter containing permutation vector shared_ptr<ArtifactId> perm_vector = genVectorInitializedVar( - _constrBlock, "arm_compute::PermutationVector", out_name + "_perm_param", acl_perm); + _constrBlock, "arm_compute::PermutationVector", out_name + "_perm_param", acl_perm); // Instantiate the CLPermute object. string layer_name = out_name + "_transpose_layer"; diff --git a/compiler/nnc/backends/acl_soft_backend/ArtifactModel.cpp b/compiler/nnc/backends/acl_soft_backend/ArtifactModel.cpp index 8888697e7..bbaa1f523 100644 --- a/compiler/nnc/backends/acl_soft_backend/ArtifactModel.cpp +++ b/compiler/nnc/backends/acl_soft_backend/ArtifactModel.cpp @@ -25,8 +25,8 @@ using namespace std; ArtifactFunctionCall::ArtifactFunctionCall(string func_name, list<shared_ptr<ArtifactExpr>> param_list, shared_ptr<ArtifactExpr> on, ArtifactCallType call_type) - : _funcName(std::move(func_name)), _callType(call_type), _on(std::move(on)), - _paramList(std::move(param_list)) + : _funcName(std::move(func_name)), _callType(call_type), _on(std::move(on)), + _paramList(std::move(param_list)) { } diff --git a/compiler/nnc/backends/acl_soft_backend/ArtifactModel.h b/compiler/nnc/backends/acl_soft_backend/ArtifactModel.h index 106c9bec3..89d803021 100644 --- a/compiler/nnc/backends/acl_soft_backend/ArtifactModel.h +++ b/compiler/nnc/backends/acl_soft_backend/ArtifactModel.h @@ -204,7 +204,7 @@ class ArtifactUnaryExpr : public ArtifactExpr { public: ArtifactUnaryExpr(ArtifactUnOp op, std::shared_ptr<ArtifactExpr> expr) - : _op(op), _expr(std::move(expr)) + : _op(op), _expr(std::move(expr)) { } @@ -248,7 +248,7 @@ class ArtifactBinaryExpr : public ArtifactExpr public: ArtifactBinaryExpr(ArtifactBinOp op, std::shared_ptr<ArtifactExpr> left, std::shared_ptr<ArtifactExpr> right) - : _op(op), _left(std::move(left)), _right(std::move(right)) + : _op(op), _left(std::move(left)), _right(std::move(right)) { } @@ -271,7 +271,7 @@ class ArtifactIndex : public ArtifactExpr { public: ArtifactIndex(std::shared_ptr<ArtifactExpr> expr, std::shared_ptr<ArtifactExpr> ind) - : _expr(std::move(expr)), _ind(std::move(ind)) + : _expr(std::move(expr)), _ind(std::move(ind)) { } @@ -328,8 +328,8 @@ public: ArtifactVariable(std::string type_name, std::string var_name, std::list<std::shared_ptr<ArtifactExpr>> dimensions = {}, std::list<std::shared_ptr<ArtifactExpr>> initializers = {}) - : _typeName(std::move(type_name)), _dimensions(std::move(dimensions)), - _initializers(std::move(initializers)), ArtifactNamed(std::move(var_name)) + : _typeName(std::move(type_name)), _dimensions(std::move(dimensions)), + _initializers(std::move(initializers)), ArtifactNamed(std::move(var_name)) { } @@ -469,7 +469,7 @@ public: explicit ArtifactForLoop(std::shared_ptr<ArtifactVariable> init = nullptr, std::shared_ptr<ArtifactExpr> cond = nullptr, std::shared_ptr<ArtifactExpr> iter = nullptr) - : _init(std::move(init)), _cond(std::move(cond)), _iter(std::move(iter)) + : _init(std::move(init)), _cond(std::move(cond)), _iter(std::move(iter)) { } @@ -527,7 +527,7 @@ public: */ ArtifactFunction(std::string ret_type_name, const std::string &func_name, std::list<std::shared_ptr<ArtifactVariable>> params = {}) - : ArtifactNamed(func_name), _params(std::move(params)), _retTypeName(std::move(ret_type_name)) + : ArtifactNamed(func_name), _params(std::move(params)), _retTypeName(std::move(ret_type_name)) { } @@ -568,7 +568,7 @@ public: const std::string &var_name, const std::list<std::shared_ptr<ArtifactExpr>> &dimensions = {}, const std::list<std::shared_ptr<ArtifactExpr>> &initializers = {}) - : ArtifactClassMember(owner), ArtifactVariable(type_name, var_name, dimensions, initializers) + : ArtifactClassMember(owner), ArtifactVariable(type_name, var_name, dimensions, initializers) { } @@ -584,7 +584,7 @@ public: ArtifactClassFunction(const ArtifactClass *owner, const std::string &ret_type_name, const std::string &func_name, const std::list<std::shared_ptr<ArtifactVariable>> ¶ms = {}) - : ArtifactClassMember(owner), ArtifactFunction(ret_type_name, func_name, params) + : ArtifactClassMember(owner), ArtifactFunction(ret_type_name, func_name, params) { } diff --git a/compiler/nnc/backends/interpreter/InterpreterBackend.cpp b/compiler/nnc/backends/interpreter/InterpreterBackend.cpp index 923a7cfc7..895daa115 100644 --- a/compiler/nnc/backends/interpreter/InterpreterBackend.cpp +++ b/compiler/nnc/backends/interpreter/InterpreterBackend.cpp @@ -104,7 +104,7 @@ static void writeTensorToHDF5File(const TensorVariant &tensor, std::string tenso static TensorVariant readTensorFromFile(const std::string &filename, const TensorType &type) { const std::size_t input_data_size = - type.getShape().numElements() * getDataTypeSize(type.getElementType()); + type.getShape().numElements() * getDataTypeSize(type.getElementType()); std::ifstream stream(filename, std::ios::in | std::ios::binary); if (stream.fail()) @@ -117,9 +117,9 @@ static TensorVariant readTensorFromFile(const std::string &filename, const Tenso int64_t file_size = end - begin; if (static_cast<std::size_t>(file_size) != input_data_size) - throw std::runtime_error("File \"" + filename + "\" has incorrect size: " + - std::to_string(file_size) + "(expected: " + - std::to_string(input_data_size) + ")."); + throw std::runtime_error("File \"" + filename + + "\" has incorrect size: " + std::to_string(file_size) + + "(expected: " + std::to_string(input_data_size) + ")."); std::unique_ptr<char[]> data(new char[input_data_size]); stream.read(data.get(), input_data_size); @@ -130,7 +130,7 @@ static TensorVariant readTensorFromFile(const std::string &filename, const Tenso } InterpreterBackend::InterpreterBackend(std::string input_dir, std::string output_dir) - : _input_dir(std::move(input_dir)), _output_dir(std::move(output_dir)) + : _input_dir(std::move(input_dir)), _output_dir(std::move(output_dir)) { } diff --git a/compiler/nnc/backends/soft_backend/CPPGenerator.cpp b/compiler/nnc/backends/soft_backend/CPPGenerator.cpp index 236881b80..097122882 100644 --- a/compiler/nnc/backends/soft_backend/CPPGenerator.cpp +++ b/compiler/nnc/backends/soft_backend/CPPGenerator.cpp @@ -80,7 +80,7 @@ static unique_ptr<ofstream> getStream(const string &path) } CPPCodeGenerator::CPPCodeGenerator(std::string output_dir, std::string artifact_name) - : _output_dir(std::move(output_dir)), _artifact_name(std::move(artifact_name)) + : _output_dir(std::move(output_dir)), _artifact_name(std::move(artifact_name)) { } @@ -187,12 +187,14 @@ void CPPCodeGenerator::materializeHeader(ostream &out, const ModelAnalyzer &ma) string class_name = ma.getModelName() + "Model"; out.write(cpp_header_types, sizeof(cpp_header_types)); - out << "class " << class_name << "\n" - "{\n" - "public:\n" - " " - << class_name << "(const std::string& parametersPath);\n" - " ~" + out << "class " << class_name + << "\n" + "{\n" + "public:\n" + " " + << class_name + << "(const std::string& parametersPath);\n" + " ~" << class_name << "();\n"; // generate input setters if (ma.getInputs().size() == 1) @@ -215,10 +217,12 @@ void CPPCodeGenerator::materializeHeader(ostream &out, const ModelAnalyzer &ma) out << " void doInference();\n\n" "private:\n" " " - << class_name << "() = delete;\n" - " " - << class_name << "(const " << class_name << "& orig) = delete;\n" - " " + << class_name + << "() = delete;\n" + " " + << class_name << "(const " << class_name + << "& orig) = delete;\n" + " " << class_name << "& operator=(const " << class_name << "& orig) = delete;\n"; // generate input/output tensors for (const size_t in_tensor_id : ma.getInputs()) @@ -273,8 +277,9 @@ void CPPCodeGenerator::printSetter(ostream &out, const string &class_name, { const string &var_name = _formattedTensors[td.id]; - out << "bool " << class_name << "::set" << setter_name << "(const Tensor& t)\n" - "{\n"; + out << "bool " << class_name << "::set" << setter_name + << "(const Tensor& t)\n" + "{\n"; // need to insert input correctness check const mir::Shape expected = td.shape; int rank = expected.rank(); @@ -286,9 +291,10 @@ void CPPCodeGenerator::printSetter(ostream &out, const string &class_name, out << " " << "if (t.getShape()[" << i << "] != " << expected.dim(i) << ") return false;\n"; } - out << " " << var_name << " = t;\n" - " return true;\n" - "}\n\n"; + out << " " << var_name + << " = t;\n" + " return true;\n" + "}\n\n"; } void CPPCodeGenerator::printGetter(ostream &out, const string &class_name, @@ -296,11 +302,13 @@ void CPPCodeGenerator::printGetter(ostream &out, const string &class_name, { const string &var_name = _formattedTensors[td.id]; - out << "shared_ptr<Tensor> " << class_name << "::get" << getter_name << "()\n" - "{\n" - " return " - << var_name << ";\n" - "}\n\n"; + out << "shared_ptr<Tensor> " << class_name << "::get" << getter_name + << "()\n" + "{\n" + " return " + << var_name + << ";\n" + "}\n\n"; } void CPPCodeGenerator::materializeCall(ostream &out, const ModelAnalyzer &ma, @@ -435,13 +443,15 @@ void CPPCodeGenerator::materializeCode(ostream &out, const ModelAnalyzer &ma, co << "(const string& parametersPath)\n" "{\n" " readParameters(_parameters, _paramSize, parametersPath, " - << s.getFormatVersion() << ", " << s.getModelHash() << ");\n" - "}\n\n"; + << s.getFormatVersion() << ", " << s.getModelHash() + << ");\n" + "}\n\n"; // gen NN destructor - out << class_name << "::~" << class_name << "()\n" - "{\n" - " releaseParameters(_parameters, _paramSize);\n" - "}\n\n"; + out << class_name << "::~" << class_name + << "()\n" + "{\n" + " releaseParameters(_parameters, _paramSize);\n" + "}\n\n"; // generate input setters // generate main setter if network has only one const auto &inputs = ma.getInputs(); @@ -473,8 +483,9 @@ void CPPCodeGenerator::materializeCode(ostream &out, const ModelAnalyzer &ma, co const TensorDescriptor &td = tensors[output_tensor_id]; printGetter(out, class_name, output_tensor_name, td); } - out << "void " << class_name << "::doInference()\n" - "{\n"; + out << "void " << class_name + << "::doInference()\n" + "{\n"; for (size_t output_tensor_id : ma.getPersistentTensors()) { const string &output_tensor_name = _formattedTensors[output_tensor_id]; diff --git a/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp b/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp index 82e62b531..2d555d0a9 100644 --- a/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp +++ b/compiler/nnc/backends/soft_backend/ModelAnalyzer.cpp @@ -62,7 +62,7 @@ void ModelAnalyzer::appendOperationToInference(Operation *op, const string &func { const auto &tensor_name = output.getName(); const auto tensor_id = - tensor_name.empty() ? declareTemporaryTensor() : declarePersistentTensor(tensor_name); + tensor_name.empty() ? declareTemporaryTensor() : declarePersistentTensor(tensor_name); node_output_tensors.push_back(tensor_id); } } @@ -82,7 +82,7 @@ void ModelAnalyzer::appendOperationToInference(Operation *op, const string &func std::copy(aux_args.begin(), aux_args.end(), std::back_inserter(node_input_tensors)); unique_ptr<Action> operation_call(new CallFunction( - op, function_name, std::move(node_input_tensors), std::move(node_output_tensors))); + op, function_name, std::move(node_input_tensors), std::move(node_output_tensors))); _inferenceSequence.push_back(std::move(operation_call)); _opToDescr[op] = _inferenceSequence.back().get(); } diff --git a/compiler/nnc/backends/soft_backend/ModelAnalyzer.h b/compiler/nnc/backends/soft_backend/ModelAnalyzer.h index 471c31011..6522bc655 100644 --- a/compiler/nnc/backends/soft_backend/ModelAnalyzer.h +++ b/compiler/nnc/backends/soft_backend/ModelAnalyzer.h @@ -42,9 +42,9 @@ class ModelAnalyzer : public mir::Visitor { public: /** - * @brief contructs inference sequence - * @param g pointer to graph to linearize - */ + * @brief contructs inference sequence + * @param g pointer to graph to linearize + */ void analyze(const mir::Graph *g); void visit(mir::ops::AbsOp &) override; diff --git a/compiler/nnc/backends/soft_backend/SequencedIR.h b/compiler/nnc/backends/soft_backend/SequencedIR.h index 9a761243e..ff062e043 100644 --- a/compiler/nnc/backends/soft_backend/SequencedIR.h +++ b/compiler/nnc/backends/soft_backend/SequencedIR.h @@ -91,7 +91,7 @@ struct TransposeTensor : public Action { TransposeTensor(size_t input, size_t output, std::vector<int32_t> &&perm) - : Action(Type::transposeTensor), perm(std::move(perm)), input(input), output(output) + : Action(Type::transposeTensor), perm(std::move(perm)), input(input), output(output) { } @@ -121,8 +121,8 @@ struct CallFunction : public Action CallFunction(mir::Operation *op, std::string func_name, std::vector<size_t> &&inputs, std::vector<size_t> &&outputs) - : Action(Type::callFunction), mirOp(op), funcName(std::move(func_name)), inputs(inputs), - outputs(outputs), paramStartOffset(0) + : Action(Type::callFunction), mirOp(op), funcName(std::move(func_name)), inputs(inputs), + outputs(outputs), paramStartOffset(0) { } diff --git a/compiler/nnc/driver/Options.cpp b/compiler/nnc/driver/Options.cpp index e22d01847..c1997fe6a 100644 --- a/compiler/nnc/driver/Options.cpp +++ b/compiler/nnc/driver/Options.cpp @@ -35,7 +35,7 @@ Option<bool> caffeFrontend(optname("--caffe"), overview("treat input file as Caf #else showopt(false) #endif // NNC_FRONTEND_CAFFE_ENABLED - ); +); Option<bool> onnxFrontend(optname("--onnx"), overview("treat input file as ONNX model"), false, optional(true), optvalues(""), nullptr, separators(""), #ifdef NNC_FRONTEND_ONNX_ENABLED @@ -43,7 +43,7 @@ Option<bool> onnxFrontend(optname("--onnx"), overview("treat input file as ONNX #else showopt(false) #endif // NNC_FRONTEND_ONNX_ENABLED - ); +); Option<bool> caffe2Frontend(optname("--caffe2"), overview("treat input file as Caffe2 model (predict_net.pb)"), false, @@ -83,16 +83,16 @@ Option<bool> tflFrontend(optname("--tflite"), #else showopt(false) #endif // NNC_FRONTEND_TFLITE_ENABLED - ); +); Option<std::string> - target(optname("--target"), - overview("select target language to emit for given architecture." - "Valid values are '" NNC_TARGET_ARM_CPP "', '" NNC_TARGET_X86_CPP - "', '" NNC_TARGET_ARM_GPU_CPP "', '" NNC_TARGET_INTERPRETER "'"), - std::string(), optional(false), - optvalues(NNC_TARGET_ARM_CPP "," NNC_TARGET_X86_CPP "," NNC_TARGET_ARM_GPU_CPP - "," NNC_TARGET_INTERPRETER), - nullptr, separators("=")); + target(optname("--target"), + overview("select target language to emit for given architecture." + "Valid values are '" NNC_TARGET_ARM_CPP "', '" NNC_TARGET_X86_CPP + "', '" NNC_TARGET_ARM_GPU_CPP "', '" NNC_TARGET_INTERPRETER "'"), + std::string(), optional(false), + optvalues(NNC_TARGET_ARM_CPP "," NNC_TARGET_X86_CPP "," NNC_TARGET_ARM_GPU_CPP + "," NNC_TARGET_INTERPRETER), + nullptr, separators("=")); /** * Options for *frontend* diff --git a/compiler/nnc/include/pass/PassData.h b/compiler/nnc/include/pass/PassData.h index e2c0b8129..1ff8af927 100644 --- a/compiler/nnc/include/pass/PassData.h +++ b/compiler/nnc/include/pass/PassData.h @@ -30,9 +30,8 @@ class PassData { public: /* implicit */ PassData(std::nullptr_t data) - : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) - _dataContainer{.unknown = data}, - _dataType(PDT::UNKNOWN) + : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) + _dataContainer{.unknown = data}, _dataType(PDT::UNKNOWN) { } @@ -40,9 +39,8 @@ public: * @brief Implicit conversion from Graph* to PassData */ /* implicit */ PassData(mir::Graph *graph) - : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) - _dataContainer{.graph = graph}, - _dataType(PDT::GRAPH) + : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) + _dataContainer{.graph = graph}, _dataType(PDT::GRAPH) { } @@ -60,9 +58,8 @@ public: * @brief Implicit conversion from Graph* to PassData */ /* implicit */ PassData(mir::TensorVariant *tv) - : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) - _dataContainer{.tensorVariant = tv}, - _dataType(PDT::TENSOR_VARIANT) + : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) + _dataContainer{.tensorVariant = tv}, _dataType(PDT::TENSOR_VARIANT) { } diff --git a/compiler/nnc/include/passes/optimizations/CombineTransposes.h b/compiler/nnc/include/passes/optimizations/CombineTransposes.h index 7d227cd5d..a08676e47 100644 --- a/compiler/nnc/include/passes/optimizations/CombineTransposes.h +++ b/compiler/nnc/include/passes/optimizations/CombineTransposes.h @@ -33,6 +33,7 @@ public: PassData run(PassData data) override; std::string getName() override { return "opt_combine_transposes"; }; + private: }; diff --git a/compiler/nnc/include/passes/optimizations/OptimizationUtils.h b/compiler/nnc/include/passes/optimizations/OptimizationUtils.h index 9a9212c12..83f455b2d 100644 --- a/compiler/nnc/include/passes/optimizations/OptimizationUtils.h +++ b/compiler/nnc/include/passes/optimizations/OptimizationUtils.h @@ -25,11 +25,11 @@ namespace nnc namespace opt_util { /** -* @brief Swap adjacent nodes in Graph. Creates new nodes and replaces the old ones with new. -* @param g MIR Graph -* @param top Node -* @param bottom Node -*/ + * @brief Swap adjacent nodes in Graph. Creates new nodes and replaces the old ones with new. + * @param g MIR Graph + * @param top Node + * @param bottom Node + */ void swapAdjacent(mir::Graph *g, mir::Operation *top, mir::Operation *bottom); // TODO: this function and it's usages should be removed, after DCE optimization will be implemented diff --git a/compiler/nnc/include/support/CommandLine.h b/compiler/nnc/include/support/CommandLine.h index 40777ff46..66466276d 100644 --- a/compiler/nnc/include/support/CommandLine.h +++ b/compiler/nnc/include/support/CommandLine.h @@ -38,7 +38,7 @@ class BadOption : public std::logic_error { public: explicit BadOption(const std::string &msg, std::string optname = "", std::string value = "") - : std::logic_error(msg), _option_name(std::move(optname)), _option_value(std::move(value)) + : std::logic_error(msg), _option_name(std::move(optname)), _option_value(std::move(value)) { } @@ -387,7 +387,7 @@ private: std::map<std::string, IOption *> _options_name; // map of name -> option std::vector<IOption *> _options; // options std::map<IOption::Group, std::vector<IOption *>> - _grouped_options; // map of groups: group -> vector of options + _grouped_options; // map of groups: group -> vector of options std::string _prog_name; // name of program int _args_num = 0; // number of command line arguments }; @@ -530,7 +530,7 @@ Option<T>::Option(const std::vector<std::string> &optnames, const std::string &d _group = group; _can_have_several_vals = - std::is_same<T, std::vector<std::string>>::value || std::is_same<T, std::vector<int>>::value; + std::is_same<T, std::vector<std::string>>::value || std::is_same<T, std::vector<int>>::value; assert(!(_can_have_several_vals && !_seps.empty()) && "option with several values can't have separators"); diff --git a/compiler/nnc/passes/optimizations/CombineTransposes.cpp b/compiler/nnc/passes/optimizations/CombineTransposes.cpp index e381a9cae..8a584d2d5 100644 --- a/compiler/nnc/passes/optimizations/CombineTransposes.cpp +++ b/compiler/nnc/passes/optimizations/CombineTransposes.cpp @@ -72,12 +72,12 @@ nnc::PassData nnc::CombineTransposes::run(nnc::PassData data) }; auto *bottom_transpose = dynamic_cast<mir::ops::TransposeOp *>(match.second); auto combined_axis_order = - combineAxisOrders(top_transpose->getAxisOrder(), bottom_transpose->getAxisOrder()); + combineAxisOrders(top_transpose->getAxisOrder(), bottom_transpose->getAxisOrder()); if (!isIdentityTranspose(combined_axis_order)) { auto new_tr_op = - g->create<mir::ops::TransposeOp>(top_transpose->getInput(0), combined_axis_order); + g->create<mir::ops::TransposeOp>(top_transpose->getInput(0), combined_axis_order); g->replaceNode(bottom_transpose, new_tr_op); } diff --git a/compiler/nnc/passes/optimizations/DeadCodeElimination.cpp b/compiler/nnc/passes/optimizations/DeadCodeElimination.cpp index b89dca1b7..371d9703f 100644 --- a/compiler/nnc/passes/optimizations/DeadCodeElimination.cpp +++ b/compiler/nnc/passes/optimizations/DeadCodeElimination.cpp @@ -33,8 +33,8 @@ nnc::PassData nnc::DeadCodeElimination::run(PassData data) return; bool has_no_uses = - std::all_of(op->getOutputs().cbegin(), op->getOutputs().cend(), - [](const Operation::Output &output) { return output.getUses().empty(); }); + std::all_of(op->getOutputs().cbegin(), op->getOutputs().cend(), + [](const Operation::Output &output) { return output.getUses().empty(); }); if (has_no_uses) { diff --git a/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp b/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp index 91686ef74..d69439fc3 100644 --- a/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp +++ b/compiler/nnc/passes/optimizations/FuseArithmeticOps.cpp @@ -215,10 +215,10 @@ bool sinkAddThroughMul(Graph *g) // Create new operations auto old_add_input = old_add_op->getInput(0); auto new_mul_op = - g->copyOpWithInputs(old_mul_op, {old_add_input, ols_mul_const_op->getOutput(0)}); + g->copyOpWithInputs(old_mul_op, {old_add_input, ols_mul_const_op->getOutput(0)}); auto new_add_const_op = mergeConstantOps(g, old_add_const_op, ols_mul_const_op, OpType::mul); auto new_add_op = - g->copyOpWithInputs(old_add_op, {new_mul_op->getOutput(0), new_add_const_op->getOutput(0)}); + g->copyOpWithInputs(old_add_op, {new_mul_op->getOutput(0), new_add_const_op->getOutput(0)}); // Replace old mul with new add and remove old nodes g->replaceNode(old_mul_op, new_add_op); diff --git a/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp b/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp index 8ff842660..fcdbba878 100644 --- a/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp +++ b/compiler/nnc/passes/transformations/DataFormatSwitcher.cpp @@ -27,7 +27,7 @@ namespace nnc { DataFormatSwitcher::DataFormatSwitcher(const mir::DataFormat target_format) - : _target_format(target_format) + : _target_format(target_format) { } @@ -89,10 +89,10 @@ mir::Operation::Output *DataFormatSwitcher::insertTransposeBefore(mir::Operation mir::Operation::Output *new_out; if (_target_format == mir::DataFormat::NHWC) new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 2, 3, 1}) - ->getOutput(0); // NCHW -> NHWC + ->getOutput(0); // NCHW -> NHWC else new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 3, 1, 2}) - ->getOutput(0); // NHWC -> NCHW + ->getOutput(0); // NHWC -> NCHW if (out->getType().isQuantized()) new_out->setQuantization(out->getType().getQuantization()); return new_out; @@ -103,10 +103,10 @@ mir::Operation::Output *DataFormatSwitcher::insertTransposeAfter(mir::Operation: mir::Operation::Output *new_out; if (_target_format == mir::DataFormat::NHWC) new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 3, 1, 2}) - ->getOutput(0); // NHWC -> NCHW + ->getOutput(0); // NHWC -> NCHW else new_out = _graph->create<mir::ops::TransposeOp>(out, std::vector<std::size_t>{0, 2, 3, 1}) - ->getOutput(0); // NCHW -> NHWC + ->getOutput(0); // NCHW -> NHWC if (out->getType().isQuantized()) new_out->setQuantization(out->getType().getQuantization()); return new_out; diff --git a/compiler/nnc/passes/transformations/LowerConv2D.cpp b/compiler/nnc/passes/transformations/LowerConv2D.cpp index 9e32978bc..9ae20527d 100644 --- a/compiler/nnc/passes/transformations/LowerConv2D.cpp +++ b/compiler/nnc/passes/transformations/LowerConv2D.cpp @@ -36,11 +36,11 @@ static void lowerConv2D(mir::Graph *graph, mir::ops::Conv2DOp *op) // [O, H, W, I / M] == [M, H, W, 1] -> [H, W, M, 1] std::vector<std::size_t> perm{1, 2, 0, 3}; mir::Operation::Output *new_kernel = - graph->create<mir::ops::TransposeOp>(kernel, perm)->getOutput(0); + graph->create<mir::ops::TransposeOp>(kernel, perm)->getOutput(0); mir::Conv2DOpAttributes attributes = op->getAttributes(); attributes.num_groups = 1; mir::Operation::Output *new_result = - graph->create<mir::ops::DepthwiseConv2DOp>(input, new_kernel, attributes)->getOutput(0); + graph->create<mir::ops::DepthwiseConv2DOp>(input, new_kernel, attributes)->getOutput(0); graph->replaceNode(op, new_result->getNode()); } } diff --git a/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp b/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp index 4ae020355..d39c9dcb5 100644 --- a/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp +++ b/compiler/nnc/tests/acl_soft_backend/AclCppOperations.cpp @@ -157,7 +157,7 @@ static void runAclSystemTest(const string &name) // Copy the model input HDF5 file to the remote device. ASSERT_TRUE( - copyToOdroid(binDir + "/" + name + "/in_" + name + "_caffe.hdf5", dir_name + "/in.hdf5")); + copyToOdroid(binDir + "/" + name + "/in_" + name + "_caffe.hdf5", dir_name + "/in.hdf5")); // Switch to the artifact directory on the remote device and run the artifact. ASSERT_TRUE(runOnOdroid("cd " + dir_name + "; ./nnc_test")); diff --git a/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp b/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp index c326b390b..ea4bddac8 100644 --- a/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp +++ b/compiler/nnc/tests/acl_soft_backend/artifact_cmake/main.cpp @@ -31,12 +31,13 @@ static unique_ptr<char[]> getTensorData(CLTensor &tensor) Iterator i(&tensor, window); char *ptr = &buf[0]; - execute_window_loop(window, - [&i, &ptr](const Coordinates &) { - memcpy(ptr, i.ptr(), sizeof(float)); - ptr += sizeof(float); - }, - i); + execute_window_loop( + window, + [&i, &ptr](const Coordinates &) { + memcpy(ptr, i.ptr(), sizeof(float)); + ptr += sizeof(float); + }, + i); tensor.unmap(); return buf; @@ -52,12 +53,13 @@ static void readTensor(CLTensor &tensor, H5::DataSet &dataset) Iterator i(&tensor, window); char *ptr = &buf[0]; - execute_window_loop(window, - [&i, &ptr](const Coordinates &) { - memcpy(i.ptr(), ptr, sizeof(float)); - ptr += sizeof(float); - }, - i); + execute_window_loop( + window, + [&i, &ptr](const Coordinates &) { + memcpy(i.ptr(), ptr, sizeof(float)); + ptr += sizeof(float); + }, + i); tensor.unmap(); } diff --git a/compiler/nnc/tests/soft_backend/CompileCPP.cpp b/compiler/nnc/tests/soft_backend/CompileCPP.cpp index 63aeb4a1b..4ede0cf05 100644 --- a/compiler/nnc/tests/soft_backend/CompileCPP.cpp +++ b/compiler/nnc/tests/soft_backend/CompileCPP.cpp @@ -101,7 +101,7 @@ int main() string target_compiler = "g++ -Wall --std=c++11"; string compiler_command = - target_compiler + " -I" + output_dir + " " + main_path + " " + code_path; + target_compiler + " -I" + output_dir + " " + main_path + " " + code_path; // call compiler int res = system(compiler_command.c_str()); diff --git a/compiler/nnc/unittests/acl_backend/DOMToText.cpp b/compiler/nnc/unittests/acl_backend/DOMToText.cpp index be0e6713c..aaf0c2055 100644 --- a/compiler/nnc/unittests/acl_backend/DOMToText.cpp +++ b/compiler/nnc/unittests/acl_backend/DOMToText.cpp @@ -148,9 +148,9 @@ TEST(acl_backend_dom_to_text, ArtifactUnaryExpr) const char *var_name = "id"; shared_ptr<ArtifactId> var = AF::id(var_name); pair<ArtifactUnOp, const char *> test_cases[] = { - {ArtifactUnOp::preIncr, "++id"}, {ArtifactUnOp::preDecr, "--id"}, - {ArtifactUnOp::heapNew, "new id"}, {ArtifactUnOp::heapFree, "delete id"}, - {ArtifactUnOp::postIncr, "id++"}, {ArtifactUnOp::postDecr, "id--"}}; + {ArtifactUnOp::preIncr, "++id"}, {ArtifactUnOp::preDecr, "--id"}, + {ArtifactUnOp::heapNew, "new id"}, {ArtifactUnOp::heapFree, "delete id"}, + {ArtifactUnOp::postIncr, "id++"}, {ArtifactUnOp::postDecr, "id--"}}; for (auto test : test_cases) { @@ -181,14 +181,14 @@ TEST(acl_backend_dom_to_text, ArtifactBinaryExpr) shared_ptr<ArtifactId> op2 = AF::id(op2_name); pair<ArtifactBinOp, const char *> test_cases[] = { - {ArtifactBinOp::eq, "a == b"}, {ArtifactBinOp::notEq, "a != b"}, - {ArtifactBinOp::less, "a < b"}, {ArtifactBinOp::lessOrEq, "a <= b"}, - {ArtifactBinOp::great, "a > b"}, {ArtifactBinOp::greatOrEq, "a >= b"}, - {ArtifactBinOp::assign, "a = b"}, {ArtifactBinOp::plus, "a + b"}, - {ArtifactBinOp::minus, "a - b"}, {ArtifactBinOp::mult, "a * b"}, - {ArtifactBinOp::div, "a / b"}, {ArtifactBinOp::plusAssign, "a += b"}, - {ArtifactBinOp::minusAssign, "a -= b"}, {ArtifactBinOp::multAssign, "a *= b"}, - {ArtifactBinOp::divAssign, "a /= b"}}; + {ArtifactBinOp::eq, "a == b"}, {ArtifactBinOp::notEq, "a != b"}, + {ArtifactBinOp::less, "a < b"}, {ArtifactBinOp::lessOrEq, "a <= b"}, + {ArtifactBinOp::great, "a > b"}, {ArtifactBinOp::greatOrEq, "a >= b"}, + {ArtifactBinOp::assign, "a = b"}, {ArtifactBinOp::plus, "a + b"}, + {ArtifactBinOp::minus, "a - b"}, {ArtifactBinOp::mult, "a * b"}, + {ArtifactBinOp::div, "a / b"}, {ArtifactBinOp::plusAssign, "a += b"}, + {ArtifactBinOp::minusAssign, "a -= b"}, {ArtifactBinOp::multAssign, "a *= b"}, + {ArtifactBinOp::divAssign, "a /= b"}}; for (auto test : test_cases) { @@ -286,12 +286,12 @@ TEST(acl_backend_dom_to_text, ArtifactForLoop) shared_ptr<ArtifactVariable> iter = AF::var(var_type, var_name, {}, {AF::lit("0")}); shared_ptr<ArtifactExpr> step = - AF::bin(ArtifactBinOp::plusAssign, AF::id(var_name), AF::lit("1")); + AF::bin(ArtifactBinOp::plusAssign, AF::id(var_name), AF::lit("1")); shared_ptr<ArtifactExpr> cond = - AF::bin(ArtifactBinOp::lessOrEq, AF::id(var_name), AF::lit("123")); + AF::bin(ArtifactBinOp::lessOrEq, AF::id(var_name), AF::lit("123")); shared_ptr<ArtifactBinaryExpr> expr = - AF::bin(ArtifactBinOp::plusAssign, AF::id("hello"), AF::id("world")); + AF::bin(ArtifactBinOp::plusAssign, AF::id("hello"), AF::id("world")); ArtifactForLoop loop(iter, cond, step); @@ -308,10 +308,10 @@ TEST(acl_backend_dom_to_text, ArtifactIf) const char *var_name = "i"; shared_ptr<ArtifactExpr> cond = - AF::bin(ArtifactBinOp::lessOrEq, AF::id(var_name), AF::lit("123")); + AF::bin(ArtifactBinOp::lessOrEq, AF::id(var_name), AF::lit("123")); shared_ptr<ArtifactBinaryExpr> expr = - AF::bin(ArtifactBinOp::plusAssign, AF::id("hello"), AF::id("world")); + AF::bin(ArtifactBinOp::plusAssign, AF::id("hello"), AF::id("world")); ArtifactIf if_stmt(cond); @@ -415,7 +415,7 @@ static shared_ptr<ArtifactClassVariable> createClsVariable(ArtifactClass &cls, c list<shared_ptr<ArtifactExpr>> dims{dim1, dim2}; list<shared_ptr<ArtifactExpr>> initializers{AF::lit("123")}; shared_ptr<ArtifactClassVariable> var_decl = - cls.var(is_public, var_type, var_name, dims, initializers); + cls.var(is_public, var_type, var_name, dims, initializers); return var_decl; } @@ -483,8 +483,8 @@ TEST(acl_backend_dom_to_text, ArtifactModule) const char *code_prefix = "#include \"module.h\"\n\n#include <list>\n\n#include \"bar.h\"\n\n"; const char *code_suffix = "\nClass::Class() {\n}\n\n"; - string ref_data = string(code_prefix) + - string(AclArtifactUtilities, sizeof(AclArtifactUtilities)) + code_suffix; + string ref_data = + string(code_prefix) + string(AclArtifactUtilities, sizeof(AclArtifactUtilities)) + code_suffix; m.accept(&code_gen); ASSERT_EQ(code_out.str(), ref_data); diff --git a/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp b/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp index a9b36a145..f411fde42 100644 --- a/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp +++ b/compiler/nnc/unittests/acl_backend/MIRToDOM.cpp @@ -117,12 +117,12 @@ void checkDomIncludes(const ArtifactModule &m) // check ordinary includes, like '#include "artifact_data.h"' checkHeadersSetsEqual( - m.headerIncludes(), - {"arm_compute/core/Types.h", "arm_compute/runtime/BlobLifetimeManager.h", - "arm_compute/runtime/CL/CLBufferAllocator.h", "arm_compute/runtime/CL/CLFunctions.h", - "arm_compute/runtime/CL/CLScheduler.h", "arm_compute/runtime/MemoryManagerOnDemand.h", - "arm_compute/runtime/PoolManager.h"}, - "system header includes diverged"); + m.headerIncludes(), + {"arm_compute/core/Types.h", "arm_compute/runtime/BlobLifetimeManager.h", + "arm_compute/runtime/CL/CLBufferAllocator.h", "arm_compute/runtime/CL/CLFunctions.h", + "arm_compute/runtime/CL/CLScheduler.h", "arm_compute/runtime/MemoryManagerOnDemand.h", + "arm_compute/runtime/PoolManager.h"}, + "system header includes diverged"); checkHeadersSetsEqual(m.sourceSysIncludes(), {}, "system source includes diverged"); } @@ -287,10 +287,10 @@ TEST(acl_backend_mir_to_dom, conv2d) Graph g; OpConstructor op_generator = - [kernel_tensor](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { - auto kernel = g.create<mir::ops::ConstantOp>(kernel_tensor)->getOutput(0); - return g.create<mir::ops::Conv2DOp>(inputs[0], kernel, mir::Conv2DOpAttributes()); - }; + [kernel_tensor](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { + auto kernel = g.create<mir::ops::ConstantOp>(kernel_tensor)->getOutput(0); + return g.create<mir::ops::Conv2DOp>(inputs[0], kernel, mir::Conv2DOpAttributes()); + }; vector<Shape> input_shapes{{1, 10, 10, channels}}; @@ -312,11 +312,11 @@ TEST(acl_backend_mir_to_dom, depthwise_conv) Graph g; OpConstructor op_generator = - [kernel_tensor](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { - Conv2DOpAttributes attributes; - auto kernel = g.create<mir::ops::ConstantOp>(kernel_tensor)->getOutput(0); - return g.create<mir::ops::DepthwiseConv2DOp>(inputs[0], kernel, attributes); - }; + [kernel_tensor](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { + Conv2DOpAttributes attributes; + auto kernel = g.create<mir::ops::ConstantOp>(kernel_tensor)->getOutput(0); + return g.create<mir::ops::DepthwiseConv2DOp>(inputs[0], kernel, attributes); + }; vector<Shape> input_shapes{{1, 10, 10, channels}}; diff --git a/compiler/nnc/unittests/optimizations/SinkTest.cpp b/compiler/nnc/unittests/optimizations/SinkTest.cpp index 8c5b2767e..be171d1cb 100644 --- a/compiler/nnc/unittests/optimizations/SinkTest.cpp +++ b/compiler/nnc/unittests/optimizations/SinkTest.cpp @@ -103,7 +103,7 @@ TEST(OptPass, sinkTrConcat) Operation *tr1 = g.create<ops::TransposeOp>(in1->getOutput(0), vector<size_t>{0, 3, 1, 2}); Operation *tr2 = g.create<ops::TransposeOp>(in2->getOutput(0), vector<size_t>{0, 3, 1, 2}); Operation *conc = - g.create<ops::ConcatOp>(vector<Operation::Output *>{tr1->getOutput(0), tr2->getOutput(0)}, 1); + g.create<ops::ConcatOp>(vector<Operation::Output *>{tr1->getOutput(0), tr2->getOutput(0)}, 1); Operation *tanh = g.create<ops::TanhOp>(conc->getOutput(0)); Operation *out = g.create<ops::OutputOp>(tanh->getOutput(0)); (void)out; @@ -141,7 +141,7 @@ TEST(OptPass, sinkReluConcat) Operation *relu1 = g.create<ops::ReluOp>(in1->getOutput(0)); Operation *relu2 = g.create<ops::ReluOp>(in2->getOutput(0)); Operation *conc = g.create<ops::ConcatOp>( - vector<Operation::Output *>{relu1->getOutput(0), relu2->getOutput(0)}, 1); + vector<Operation::Output *>{relu1->getOutput(0), relu2->getOutput(0)}, 1); Operation *tanh = g.create<ops::TanhOp>(conc->getOutput(0)); Operation *out = g.create<ops::OutputOp>(tanh->getOutput(0)); (void)out; diff --git a/compiler/nnc/unittests/soft_backend/CPPOperations.cpp b/compiler/nnc/unittests/soft_backend/CPPOperations.cpp index 508ee954d..e593333fa 100644 --- a/compiler/nnc/unittests/soft_backend/CPPOperations.cpp +++ b/compiler/nnc/unittests/soft_backend/CPPOperations.cpp @@ -120,11 +120,10 @@ namespace * @brief Creates graph with one operation generated by opGen function and returns this operation * node */ -mir::Operation * -fillGraph(mir::Graph &g, - const function<mir::Operation *(mir::Graph &g, vector<mir::Operation::Output *> &inputs)> - &op_gen, - const vector<unique_ptr<mir::TensorVariant>> &input_ntensors) +mir::Operation *fillGraph( + mir::Graph &g, + const function<mir::Operation *(mir::Graph &g, vector<mir::Operation::Output *> &inputs)> &op_gen, + const vector<unique_ptr<mir::TensorVariant>> &input_ntensors) { // Create operation inputs. vector<mir::Operation::Output *> inputs; @@ -295,8 +294,8 @@ void compareResults(const mir::TensorVariant &ref_nnc_tensor, const Tensor &test float ref_data = mir::Tensor<float>(ref_nnc_tensor).at(nnc_idx); float test_data = test_art_tensor.at(artifact_idx); ASSERT_TRUE(areFloatsNear(ref_data, test_data, 32, 1e-5)) - << "Tensor element " << nnc_idx << " diverged, reference: " << ref_data - << " test result: " << test_data; + << "Tensor element " << nnc_idx << " diverged, reference: " << ref_data + << " test result: " << test_data; } } @@ -306,10 +305,10 @@ void compareResults(const mir::TensorVariant &ref_nnc_tensor, const Tensor &test */ template <typename TestFunc, typename... Args> void createAndRunTestGraph( - function<mir::Operation *(mir::Graph &, const std::vector<mir::Operation::Output *> &inputs)> - op_generator, - TestFunc artifactOperation, const vector<unique_ptr<mir::TensorVariant>> &input_ntensors, - Args &... input_atensors) + function<mir::Operation *(mir::Graph &, const std::vector<mir::Operation::Output *> &inputs)> + op_generator, + TestFunc artifactOperation, const vector<unique_ptr<mir::TensorVariant>> &input_ntensors, + Args &... input_atensors) { mir::Graph g; mir::Operation *actual_operation = fillGraph(g, op_generator, input_ntensors); @@ -657,7 +656,7 @@ TEST(cpp_operations_test, resize_NN_test) auto op_generator = [&res_shape](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { return g.create<mir::ops::ResizeOp>( - inputs[0], mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, res_shape); + inputs[0], mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, res_shape); }; createAndRunTestGraph(op_generator, resize, input_ntensors, input_atensor); @@ -668,7 +667,7 @@ TEST(cpp_operations_test, resize_NN_test_scales) { cout << "\n"; std::vector<float> test_scales[] = { - {1, 2, 2, 1}, {1, 2, 3, 1}, {1, 3, 2, 1}, {1, 2.5, 2, 1}, {1, 3, 9, 1}}; + {1, 2, 2, 1}, {1, 2, 3, 1}, {1, 3, 2, 1}, {1, 2.5, 2, 1}, {1, 3, 9, 1}}; for (const std::vector<float> &scales : test_scales) { vector<int> input_shape_data{1, 4, 4, 1}; @@ -678,7 +677,7 @@ TEST(cpp_operations_test, resize_NN_test_scales) auto op_generator = [&scales](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { return g.create<mir::ops::ResizeOp>( - inputs[0], mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales); + inputs[0], mir::ops::ResizeOp::ResizeMethod::nearestNeighbor, scales); }; createAndRunTestGraph(op_generator, resize, input_ntensors, input_atensor); } @@ -711,10 +710,10 @@ TEST(cpp_operations_test, avgpool) for (const auto include_pad : {false, true}) { attributes.include_pad = include_pad; - auto op_generator = [&attributes]( - mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { - return g.create<mir::ops::AvgPool2DOp>(inputs[0], attributes); - }; + auto op_generator = + [&attributes](mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { + return g.create<mir::ops::AvgPool2DOp>(inputs[0], attributes); + }; createAndRunTestGraph(op_generator, avgPool, input_ntensors, input_atensor); } @@ -742,8 +741,9 @@ TEST(cpp_operations_test, maxpool) vector<unique_ptr<mir::TensorVariant>> input_ntensors(1); fillTensors(input_ntensors[0], input_atensor, shape_data, 1.0f); - auto op_generator = [&window_size, &strides]( - mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { + auto op_generator = [&window_size, + &strides](mir::Graph &g, + const std::vector<mir::Operation::Output *> &inputs) { mir::MaxPool2DOpAttributes attributes; attributes.window = window_size; attributes.strides = strides; @@ -838,7 +838,7 @@ TEST(cpp_operations_test, reduceMeanTst) vector<unique_ptr<mir::TensorVariant>> input_ntensors(1); fillTensors(input_ntensors[0], input_atensor, input_shape_data, 1.0f); auto op_generator = [&axis_list, keep_dims]( - mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { + mir::Graph &g, const std::vector<mir::Operation::Output *> &inputs) { auto op = g.create<mir::ops::ReduceMeanOp>(inputs[0], axis_list, keep_dims); return op; }; @@ -873,7 +873,8 @@ TEST(cpp_operations_test, slice4d) vector<int> shape_data{5, 30, 40, 12}; vector<int> starts[] = {{0, 0, 0, 0}, {1, 1, 1, 1}, {1, 0, 1, 0}, {0, 1, 1, 0}}; vector<int> sizes[] = { - {-1, -1, -1, -1}, {4, -1, 10, -1}, + {-1, -1, -1, -1}, + {4, -1, 10, -1}, }; for (auto st : starts) { diff --git a/compiler/nnc/unittests/support/CommandLineTest.cpp b/compiler/nnc/unittests/support/CommandLineTest.cpp index 73f77aa20..993c4086f 100644 --- a/compiler/nnc/unittests/support/CommandLineTest.cpp +++ b/compiler/nnc/unittests/support/CommandLineTest.cpp @@ -69,8 +69,8 @@ Option<int32_t> NNegOpt(optname("-neg_val"), // test option with default negative value Option<int32_t> - NDefaultNegOpt(optname("-default_neg_val"), - overview("description of integer option with default negative value"), -33); + NDefaultNegOpt(optname("-default_neg_val"), + overview("description of integer option with default negative value"), -33); // test option with positive values Option<uint32_t> NPosOpt(optname("-pos_val"), overview("description of integer option with positive value"), 1, @@ -124,28 +124,28 @@ TEST(SUPPORT_NNC, verify_cl_options) { // create command line const char *argv[] = { - "CLTest", // program name - // string options - "-m", "multiopt_value", // second name for option with several names - "--single", "single_value", // option with single name - "-several_separators:SOME_VALUE1,SOME_VALUE2", // test option with several separators - "--one_separarot=AAA_VALUE", // test option whit one separator - "-default_val_opt", // test option with default value - "--optional_opt", "/home/guest/tmp", // test optional option - "-valid_opt", "value2", // test options with defined values - // integer options - "-neg_val", "-42", // test negative value for integer option - "-default_neg_val", // test integer option with default value - "-pos_val", "33", // test positive value for integer option - // char options - "-char-opt", "b", "-dash_opt", "-", - // bool options - "-bool_opt=false", "-bool-opt2", - // vector of strings options - "-vec_opt1", "1", "c", "222", "ABC", "857", "-vec_opt2", "--vec_opt_with_vals", "abc", "123", - "xxx", "abc", "xxx", - // grouped options - "-group_opt1", "-group_opt2", "abc", "-group_opt3", "11", nullptr}; + "CLTest", // program name + // string options + "-m", "multiopt_value", // second name for option with several names + "--single", "single_value", // option with single name + "-several_separators:SOME_VALUE1,SOME_VALUE2", // test option with several separators + "--one_separarot=AAA_VALUE", // test option whit one separator + "-default_val_opt", // test option with default value + "--optional_opt", "/home/guest/tmp", // test optional option + "-valid_opt", "value2", // test options with defined values + // integer options + "-neg_val", "-42", // test negative value for integer option + "-default_neg_val", // test integer option with default value + "-pos_val", "33", // test positive value for integer option + // char options + "-char-opt", "b", "-dash_opt", "-", + // bool options + "-bool_opt=false", "-bool-opt2", + // vector of strings options + "-vec_opt1", "1", "c", "222", "ABC", "857", "-vec_opt2", "--vec_opt_with_vals", "abc", "123", + "xxx", "abc", "xxx", + // grouped options + "-group_opt1", "-group_opt2", "abc", "-group_opt3", "11", nullptr}; int argc = (sizeof(argv) / sizeof(argv[0])) - 1; // It must be failed if option is not passed and other options are in the same group diff --git a/compiler/nnc/unittests/transformations/Switcher.cpp b/compiler/nnc/unittests/transformations/Switcher.cpp index 049ac44cd..2f4793369 100644 --- a/compiler/nnc/unittests/transformations/Switcher.cpp +++ b/compiler/nnc/unittests/transformations/Switcher.cpp @@ -88,7 +88,7 @@ TEST(TRANSFORMATIONS, Switcher_DWConv2D_NHWC2NCHW) attributes.padding_before = {67, 123}; attributes.padding_after = {32, 356}; auto *dw_conv = - g.create<mir::ops::DepthwiseConv2DOp>(input->getOutput(0), kernel->getOutput(0), attributes); + g.create<mir::ops::DepthwiseConv2DOp>(input->getOutput(0), kernel->getOutput(0), attributes); auto *output = g.create<mir::ops::OutputOp>(dw_conv->getOutput(0)); @@ -138,7 +138,7 @@ TEST(TRANSFORMATIONS, Switcher_DeConv2D_NHWC2NCHW) attributes.padding_before = {31, 72}; attributes.padding_after = {32, 71}; auto *deconv = - g.create<mir::ops::DeConv2DOp>(input->getOutput(0), kernel->getOutput(0), attributes); + g.create<mir::ops::DeConv2DOp>(input->getOutput(0), kernel->getOutput(0), attributes); auto *output = g.create<mir::ops::OutputOp>(deconv->getOutput(0)); diff --git a/compiler/nnkit-caffe/backend/CMakeLists.txt b/compiler/nnkit-caffe/backend/CMakeLists.txt index b18aa4f11..567d95438 100644 --- a/compiler/nnkit-caffe/backend/CMakeLists.txt +++ b/compiler/nnkit-caffe/backend/CMakeLists.txt @@ -1,3 +1,2 @@ add_library(nnkit_caffe_backend SHARED Module.cpp) target_link_libraries(nnkit_caffe_backend nnkit_support_caffe) -target_link_libraries(nnkit_caffe_backend stdex) diff --git a/compiler/nnkit-caffe/backend/Module.cpp b/compiler/nnkit-caffe/backend/Module.cpp index cb24a4e60..0bd39125f 100644 --- a/compiler/nnkit-caffe/backend/Module.cpp +++ b/compiler/nnkit-caffe/backend/Module.cpp @@ -17,11 +17,12 @@ #include "nnkit/support/caffe/Backend.h" #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> + +#include <memory> extern "C" std::unique_ptr<nnkit::Backend> make_backend(const nnkit::CmdlineArguments &args) { - using stdex::make_unique; + using std::make_unique; auto net = make_unique<::caffe::Net<float>>(args.at(0), caffe::TEST); diff --git a/compiler/nnkit-intf/tensor/include/nnkit/TensorContext.h b/compiler/nnkit-intf/tensor/include/nnkit/TensorContext.h index 07d8d154c..87056dd64 100644 --- a/compiler/nnkit-intf/tensor/include/nnkit/TensorContext.h +++ b/compiler/nnkit-intf/tensor/include/nnkit/TensorContext.h @@ -37,8 +37,8 @@ struct TensorContext const nncc::core::ADT::tensor::Reader<T> &)>; template <typename T> - using TypedAccessor = std::function<void(const TensorContext &, uint32_t n, - nncc::core::ADT::tensor::Accessor<T> &)>; + using TypedAccessor = + std::function<void(const TensorContext &, uint32_t n, nncc::core::ADT::tensor::Accessor<T> &)>; virtual ~TensorContext() = default; diff --git a/compiler/nnkit-misc/backend/CMakeLists.txt b/compiler/nnkit-misc/backend/CMakeLists.txt index d351d5ce5..327fbab3c 100644 --- a/compiler/nnkit-misc/backend/CMakeLists.txt +++ b/compiler/nnkit-misc/backend/CMakeLists.txt @@ -4,7 +4,6 @@ add_library(nnkit_support_backend STATIC ${SOURCES}) target_include_directories(nnkit_support_backend PUBLIC include) target_link_libraries(nnkit_support_backend PUBLIC nnkit_intf_backend) target_link_libraries(nnkit_support_backend PUBLIC dl) -target_link_libraries(nnkit_support_backend PUBLIC stdex) find_package(Threads QUIET) diff --git a/compiler/nnkit-misc/backend/src/BackendPlugin.cpp b/compiler/nnkit-misc/backend/src/BackendPlugin.cpp index 54b1fdc83..75e0763c4 100644 --- a/compiler/nnkit-misc/backend/src/BackendPlugin.cpp +++ b/compiler/nnkit-misc/backend/src/BackendPlugin.cpp @@ -17,7 +17,7 @@ #include "nnkit/BackendPlugin.h" #include <cassert> -#include <stdex/Memory.h> +#include <memory> #include <iostream> // NOTE dlfcn.h is not a standard library @@ -82,7 +82,7 @@ std::unique_ptr<BackendPlugin> make_backend_plugin(const std::string &path) exit(1); } - return stdex::make_unique<BackendPlugin>(handle, entry); + return std::make_unique<BackendPlugin>(handle, entry); } } // namespace nnkit diff --git a/compiler/nnkit-mocotf/backend/Backend.cpp b/compiler/nnkit-mocotf/backend/Backend.cpp index 4900684eb..598370635 100644 --- a/compiler/nnkit-mocotf/backend/Backend.cpp +++ b/compiler/nnkit-mocotf/backend/Backend.cpp @@ -17,13 +17,13 @@ #include "nnkit/support/moco/tf/Backend.h" #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> +#include <memory> #include <cassert> extern "C" std::unique_ptr<nnkit::Backend> make_backend(const nnkit::CmdlineArguments &args) { - using stdex::make_unique; + using std::make_unique; assert(args.size() == 2); // args.at[0] : *.pb path, args.at[1]: *.info path diff --git a/compiler/nnkit-mocotf/backend/CMakeLists.txt b/compiler/nnkit-mocotf/backend/CMakeLists.txt index 72e16c75a..3dcd7e564 100644 --- a/compiler/nnkit-mocotf/backend/CMakeLists.txt +++ b/compiler/nnkit-mocotf/backend/CMakeLists.txt @@ -1,3 +1,2 @@ add_library(nnkit_moco_tf_backend SHARED Backend.cpp) target_link_libraries(nnkit_moco_tf_backend nnkit_support_moco_tf) -target_link_libraries(nnkit_moco_tf_backend stdex) diff --git a/compiler/nnkit-mocotf/requires.cmake b/compiler/nnkit-mocotf/requires.cmake index 6949ec808..1461e8443 100644 --- a/compiler/nnkit-mocotf/requires.cmake +++ b/compiler/nnkit-mocotf/requires.cmake @@ -1,4 +1,3 @@ -require("stdex") # To use "nnkit_support_tftestinfo" require("tfinfo") require("loco") diff --git a/compiler/nnkit-mocotf/support/CMakeLists.txt b/compiler/nnkit-mocotf/support/CMakeLists.txt index 76c7c04b1..1b20d946b 100644 --- a/compiler/nnkit-mocotf/support/CMakeLists.txt +++ b/compiler/nnkit-mocotf/support/CMakeLists.txt @@ -10,4 +10,3 @@ target_link_libraries(nnkit_support_moco_tf nnkit_support_tftestinfo) target_link_libraries(nnkit_support_moco_tf locomotiv) target_link_libraries(nnkit_support_moco_tf moco_tf_frontend) target_link_libraries(nnkit_support_moco_tf loco) -target_link_libraries(nnkit_support_moco_tf stdex) diff --git a/compiler/nnkit-mocotf/support/src/Backend.cpp b/compiler/nnkit-mocotf/support/src/Backend.cpp index 2d9e21fd7..89dd73271 100644 --- a/compiler/nnkit-mocotf/support/src/Backend.cpp +++ b/compiler/nnkit-mocotf/support/src/Backend.cpp @@ -25,11 +25,11 @@ #include <moco/tf/Frontend.h> #include <moco/Names.h> -#include <stdex/Memory.h> #include <nncc/core/ADT/tensor/Buffer.h> #include <nncc/core/ADT/tensor/LexicalLayout.h> +#include <memory> #include <utility> // std::move #include <stdexcept> @@ -116,7 +116,7 @@ Backend::Backend(const char *pb_path, const char *info_path) // set member vars _loco_graph = std::move(loco_graph); - _sess = stdex::make_unique<locomotiv::Session>(_loco_graph.get()); + _sess = std::make_unique<locomotiv::Session>(_loco_graph.get()); } void Backend::prepare(const std::function<void(nnkit::TensorContext &)> &f) @@ -131,7 +131,7 @@ void Backend::prepare(const std::function<void(nnkit::TensorContext &)> &f) for (int n = 0; n < _inputs.size(); n++) { auto buf = make_buffer<float, LexicalLayout>(_inputs.at(n)->shape()); - buf_list.emplace_back(stdex::make_unique<nncc::core::ADT::tensor::Buffer<float>>(buf)); + buf_list.emplace_back(std::make_unique<nncc::core::ADT::tensor::Buffer<float>>(buf)); } // fill test input values diff --git a/compiler/nnkit-mocotf/support/src/InputTensorContext.cpp b/compiler/nnkit-mocotf/support/src/InputTensorContext.cpp index 98f500730..25ddc0982 100644 --- a/compiler/nnkit-mocotf/support/src/InputTensorContext.cpp +++ b/compiler/nnkit-mocotf/support/src/InputTensorContext.cpp @@ -37,7 +37,7 @@ void InputTensorContext::getMutableFloatTensor(uint32_t n, } void InputTensorContext::getConstFloatTensor( - uint32_t n, const nnkit::TensorContext::TypedReader<float> &f) const + uint32_t n, const nnkit::TensorContext::TypedReader<float> &f) const { auto buf = _buffers.at(n).get(); f(*this, n, *buf); diff --git a/compiler/nnkit-mocotf/support/src/InputTensorContext.h b/compiler/nnkit-mocotf/support/src/InputTensorContext.h index bbb25adea..4100d229a 100644 --- a/compiler/nnkit-mocotf/support/src/InputTensorContext.h +++ b/compiler/nnkit-mocotf/support/src/InputTensorContext.h @@ -45,7 +45,7 @@ class InputTensorContext final : public TensorContext public: InputTensorContext(const ParsedTensors &parsed_tensors, const Buffers &buffers) - : TensorContext(parsed_tensors), _buffers(buffers) + : TensorContext(parsed_tensors), _buffers(buffers) { /* empty */ } diff --git a/compiler/nnkit-mocotf/support/src/OutputTensorContext.cpp b/compiler/nnkit-mocotf/support/src/OutputTensorContext.cpp index 2b36fc67a..6ef1e4598 100644 --- a/compiler/nnkit-mocotf/support/src/OutputTensorContext.cpp +++ b/compiler/nnkit-mocotf/support/src/OutputTensorContext.cpp @@ -30,7 +30,7 @@ namespace tf { void OutputTensorContext::getConstFloatTensor( - uint32_t n, const nnkit::TensorContext::TypedReader<float> &f) const + uint32_t n, const nnkit::TensorContext::TypedReader<float> &f) const { // for output using nncc::core::ADT::tensor::LexicalLayout; using nncc::core::ADT::tensor::make_overlay; diff --git a/compiler/nnkit-mocotf/support/src/OutputTensorContext.h b/compiler/nnkit-mocotf/support/src/OutputTensorContext.h index 8cb8d8bf0..f825729e9 100644 --- a/compiler/nnkit-mocotf/support/src/OutputTensorContext.h +++ b/compiler/nnkit-mocotf/support/src/OutputTensorContext.h @@ -43,7 +43,7 @@ class OutputTensorContext final : public TensorContext { public: OutputTensorContext(const ParsedTensors &parsed_tensors, locomotiv::Session *sess) - : TensorContext(parsed_tensors), _sess(sess) + : TensorContext(parsed_tensors), _sess(sess) { /* empty */ } diff --git a/compiler/nnkit-onnxrt/backend/Backend.cpp b/compiler/nnkit-onnxrt/backend/Backend.cpp index 9247fbf34..a6c62b7b3 100644 --- a/compiler/nnkit-onnxrt/backend/Backend.cpp +++ b/compiler/nnkit-onnxrt/backend/Backend.cpp @@ -17,13 +17,13 @@ #include "nnkit/support/onnx/Backend.h" #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> +#include <memory> #include <cassert> extern "C" std::unique_ptr<nnkit::Backend> make_backend(const nnkit::CmdlineArguments &args) { assert(args.size() == 1); // args.at[0] : onnx file - return stdex::make_unique<::nnkit::support::onnx::Backend>(args.at(0)); + return std::make_unique<::nnkit::support::onnx::Backend>(args.at(0)); } diff --git a/compiler/nnkit-onnxrt/backend/CMakeLists.txt b/compiler/nnkit-onnxrt/backend/CMakeLists.txt index b00e5593d..ae462de8d 100644 --- a/compiler/nnkit-onnxrt/backend/CMakeLists.txt +++ b/compiler/nnkit-onnxrt/backend/CMakeLists.txt @@ -1,3 +1,2 @@ add_library(nnkit_onnx_backend SHARED Backend.cpp) target_link_libraries(nnkit_onnx_backend nnkit_support_onnx) -target_link_libraries(nnkit_onnx_backend stdex) diff --git a/compiler/nnkit-onnxrt/requires.cmake b/compiler/nnkit-onnxrt/requires.cmake index d370fc17c..be53ae74f 100644 --- a/compiler/nnkit-onnxrt/requires.cmake +++ b/compiler/nnkit-onnxrt/requires.cmake @@ -1,2 +1 @@ -require("stdex") require("nnkit-intf") diff --git a/compiler/nnkit-onnxrt/support/CMakeLists.txt b/compiler/nnkit-onnxrt/support/CMakeLists.txt index 1b51d4ed8..3d3bb2671 100644 --- a/compiler/nnkit-onnxrt/support/CMakeLists.txt +++ b/compiler/nnkit-onnxrt/support/CMakeLists.txt @@ -5,6 +5,5 @@ set_target_properties(nnkit_support_onnx-1.4 PROPERTIES POSITION_INDEPENDENT_COD target_include_directories(nnkit_support_onnx-1.4 PUBLIC include) target_link_libraries(nnkit_support_onnx-1.4 nnkit_intf_backend) target_link_libraries(nnkit_support_onnx-1.4 onnxruntime) -target_link_libraries(nnkit_support_onnx-1.4 stdex) add_library(nnkit_support_onnx ALIAS nnkit_support_onnx-1.4) diff --git a/compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/TensorSet.h b/compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/TensorSet.h index b38fc9bb0..26753fed7 100644 --- a/compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/TensorSet.h +++ b/compiler/nnkit-onnxrt/support/include/nnkit/support/onnx/TensorSet.h @@ -37,7 +37,7 @@ class TensorSet final { public: TensorSet(Allocator *allocator, size_t nums) - : _allocator(allocator), _names(nums), _types(nums), _dims(nums), _tensors(nums, nullptr) + : _allocator(allocator), _names(nums), _types(nums), _dims(nums), _tensors(nums, nullptr) { // DO NOTHING } @@ -60,7 +60,7 @@ public: Status status; status = - OrtCreateTensorAsOrtValue(_allocator, dims.data(), dims.size(), type, &_tensors[index]); + OrtCreateTensorAsOrtValue(_allocator, dims.data(), dims.size(), type, &_tensors[index]); status.throwOnError(); assert(OrtIsTensor(_tensors[index])); diff --git a/compiler/nnkit-onnxrt/support/src/Runner.cpp b/compiler/nnkit-onnxrt/support/src/Runner.cpp index bc6a81a5c..8159ed7c2 100644 --- a/compiler/nnkit-onnxrt/support/src/Runner.cpp +++ b/compiler/nnkit-onnxrt/support/src/Runner.cpp @@ -17,7 +17,7 @@ #include "nnkit/support/onnx/Runner.h" #include "nnkit/support/onnx/Status.h" -#include <stdex/Memory.h> +#include <memory> #include <cassert> namespace nnkit @@ -27,7 +27,7 @@ namespace support namespace onnx { -Runner::Runner(const std::string &path) : _allocator(stdex::make_unique<Allocator>()) +Runner::Runner(const std::string &path) : _allocator(std::make_unique<Allocator>()) { Status status; @@ -61,7 +61,7 @@ void Runner::prepareInputs(void) status = OrtSessionGetInputCount(_session, &num_input_nodes); status.throwOnError(); - _inputs = stdex::make_unique<TensorSet>(_allocator.get(), num_input_nodes); + _inputs = std::make_unique<TensorSet>(_allocator.get(), num_input_nodes); for (size_t i = 0; i < num_input_nodes; ++i) { @@ -113,7 +113,7 @@ void Runner::prepareOutputs(void) status = OrtSessionGetOutputCount(_session, &num_output_nodes); status.throwOnError(); - _outputs = stdex::make_unique<TensorSet>(_allocator.get(), num_output_nodes); + _outputs = std::make_unique<TensorSet>(_allocator.get(), num_output_nodes); for (size_t i = 0; i < num_output_nodes; ++i) { diff --git a/compiler/nnkit-tf/backend/Backend.cpp b/compiler/nnkit-tf/backend/Backend.cpp index ee0476469..99c857e46 100644 --- a/compiler/nnkit-tf/backend/Backend.cpp +++ b/compiler/nnkit-tf/backend/Backend.cpp @@ -17,13 +17,13 @@ #include "nnkit/support/tf/Backend.h" #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> +#include <memory> #include <cassert> extern "C" std::unique_ptr<nnkit::Backend> make_backend(const nnkit::CmdlineArguments &args) { - using stdex::make_unique; + using std::make_unique; assert(args.size() == 2); // args.at[0] : test.pb path, argas.at[1]: test.info path diff --git a/compiler/nnkit-tf/backend/CMakeLists.txt b/compiler/nnkit-tf/backend/CMakeLists.txt index dd2e469e8..d0078453e 100644 --- a/compiler/nnkit-tf/backend/CMakeLists.txt +++ b/compiler/nnkit-tf/backend/CMakeLists.txt @@ -1,3 +1,2 @@ add_library(nnkit_tf_backend SHARED Backend.cpp) target_link_libraries(nnkit_tf_backend nnkit_support_tf) -target_link_libraries(nnkit_tf_backend stdex) diff --git a/compiler/nnkit-tf/requires.cmake b/compiler/nnkit-tf/requires.cmake index 4b9fd68b2..a757bdda4 100644 --- a/compiler/nnkit-tf/requires.cmake +++ b/compiler/nnkit-tf/requires.cmake @@ -1,3 +1,2 @@ -require("stdex") require("tfinfo") require("nnkit-intf") diff --git a/compiler/nnkit-tf/support/CMakeLists.txt b/compiler/nnkit-tf/support/CMakeLists.txt index 0f5c0a6dd..d064131ea 100644 --- a/compiler/nnkit-tf/support/CMakeLists.txt +++ b/compiler/nnkit-tf/support/CMakeLists.txt @@ -3,7 +3,7 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") add_library(nnkit_support_tf-1.13 STATIC ${SOURCES}) set_target_properties(nnkit_support_tf-1.13 PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(nnkit_support_tf-1.13 PUBLIC include) -target_link_libraries(nnkit_support_tf-1.13 nnkit_intf_backend stdex nnkit_support_tftestinfo) +target_link_libraries(nnkit_support_tf-1.13 nnkit_intf_backend nnkit_support_tftestinfo) target_link_libraries(nnkit_support_tf-1.13 tensorflow-1.13) add_library(nnkit_support_tf ALIAS nnkit_support_tf-1.13) diff --git a/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorContext.h b/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorContext.h index f1ecd6c9c..fec614733 100644 --- a/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorContext.h +++ b/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorContext.h @@ -36,7 +36,7 @@ class TensorContext final : public nnkit::TensorContext { public: TensorContext(const std::vector<std::unique_ptr<ParsedTensor>> &tensors, TensorDataMap &data_map) - : _tensors(tensors), _data_map(data_map) + : _tensors(tensors), _data_map(data_map) { // empty } diff --git a/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorDataMap.h b/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorDataMap.h index daa1a95b3..5b12aa9a7 100644 --- a/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorDataMap.h +++ b/compiler/nnkit-tf/support/include/nnkit/support/tf/TensorDataMap.h @@ -41,7 +41,9 @@ using nnkit::support::tftestinfo::ParsedTensor; class TensorDataMap { public: - TensorDataMap() { /* empty */} + TensorDataMap() + { /* empty */ + } uint8_t *allocate(const ParsedTensor *parsed_tensor) { diff --git a/compiler/nnkit-tf/support/src/Backend.cpp b/compiler/nnkit-tf/support/src/Backend.cpp index f28e05f74..54bc4984d 100644 --- a/compiler/nnkit-tf/support/src/Backend.cpp +++ b/compiler/nnkit-tf/support/src/Backend.cpp @@ -50,7 +50,7 @@ Backend::Backend(const char *pb_path, const char *info_path) : _tf_runner(pb_pat angkor::TensorShape shape; if (!_tf_runner.getTensorShapeFromGraphDef(parsed_tensor, shape)) throw oops::UserExn( - "Info you provided may be wrong or not enough. Please check the info file."); + "Info you provided may be wrong or not enough. Please check the info file."); parsed_tensor->mutable_shape().resize(shape.rank()); for (int r = 0; r < shape.rank(); r++) diff --git a/compiler/nnkit-tf/support/src/Runner.cpp b/compiler/nnkit-tf/support/src/Runner.cpp index 0d36ee2f4..d2c37cd29 100644 --- a/compiler/nnkit-tf/support/src/Runner.cpp +++ b/compiler/nnkit-tf/support/src/Runner.cpp @@ -263,8 +263,8 @@ void Runner::prepareInputs(const std::vector<std::unique_ptr<ParsedTensor>> &inp throw std::runtime_error("Not supported tensor type"); TF_Tensor *input_tensor = - create_tensor(TF_FLOAT, shape.data(), shape.size(), data_map.data(tensor.get()), - num_elements(tensor->shape()) * size); + create_tensor(TF_FLOAT, shape.data(), shape.size(), data_map.data(tensor.get()), + num_elements(tensor->shape()) * size); _input_ops.emplace_back(input_op); _input_tensors.emplace_back(input_tensor); @@ -308,7 +308,7 @@ void Runner::run() 0, // Target operations, number of targets. nullptr, // Run metadata. _status // Output status. - ); + ); if (TF_GetCode(_status) != TF_OK) throw std::runtime_error(TF_Message(_status)); diff --git a/compiler/nnkit-tflite/backend/Backend.cpp b/compiler/nnkit-tflite/backend/Backend.cpp index 08ba338e8..b84c5076e 100644 --- a/compiler/nnkit-tflite/backend/Backend.cpp +++ b/compiler/nnkit-tflite/backend/Backend.cpp @@ -51,12 +51,13 @@ private: std::unique_ptr<::tflite::FlatBufferModel> _model; std::unique_ptr<::tflite::Interpreter> _interp; }; -} +} // namespace #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> + +#include <memory> extern "C" std::unique_ptr<nnkit::Backend> make_backend(const nnkit::CmdlineArguments &args) { - return stdex::make_unique<GenericBackend>(args.at(0)); + return std::make_unique<GenericBackend>(args.at(0)); } diff --git a/compiler/nnkit-tflite/backend/CMakeLists.txt b/compiler/nnkit-tflite/backend/CMakeLists.txt index 3f4a8ca53..31606b15e 100644 --- a/compiler/nnkit-tflite/backend/CMakeLists.txt +++ b/compiler/nnkit-tflite/backend/CMakeLists.txt @@ -4,4 +4,3 @@ endif(NOT TARGET nnkit_support_tflite) add_library(nnkit_tflite_backend SHARED Backend.cpp) target_link_libraries(nnkit_tflite_backend nnkit_support_tflite) -target_link_libraries(nnkit_tflite_backend stdex) diff --git a/compiler/nnkit-tflite/requires.cmake b/compiler/nnkit-tflite/requires.cmake index d370fc17c..be53ae74f 100644 --- a/compiler/nnkit-tflite/requires.cmake +++ b/compiler/nnkit-tflite/requires.cmake @@ -1,2 +1 @@ -require("stdex") require("nnkit-intf") diff --git a/compiler/nnkit/actions/HDF5/CMakeLists.txt b/compiler/nnkit/actions/HDF5/CMakeLists.txt index 63d3320c5..0b1e2e516 100644 --- a/compiler/nnkit/actions/HDF5/CMakeLists.txt +++ b/compiler/nnkit/actions/HDF5/CMakeLists.txt @@ -12,10 +12,8 @@ add_library(nnkit_HDF5_export_action SHARED Export.cpp) target_include_directories(nnkit_HDF5_export_action PRIVATE ${HDF5_INCLUDE_DIRS}) target_link_libraries(nnkit_HDF5_export_action nnkit_intf_action) target_link_libraries(nnkit_HDF5_export_action nnkit_HDF5_common) -target_link_libraries(nnkit_HDF5_export_action stdex) add_library(nnkit_HDF5_import_action SHARED Import.cpp) target_include_directories(nnkit_HDF5_import_action PRIVATE ${HDF5_INCLUDE_DIRS}) target_link_libraries(nnkit_HDF5_import_action nnkit_intf_action) target_link_libraries(nnkit_HDF5_import_action nnkit_HDF5_common) -target_link_libraries(nnkit_HDF5_import_action stdex) diff --git a/compiler/nnkit/actions/HDF5/Export.cpp b/compiler/nnkit/actions/HDF5/Export.cpp index 389f5c050..f21a7ff4e 100644 --- a/compiler/nnkit/actions/HDF5/Export.cpp +++ b/compiler/nnkit/actions/HDF5/Export.cpp @@ -58,7 +58,7 @@ public: H5::DataSpace dataspace(rank, dims); auto dataset = - _value_grp.createDataSet(value_filename(n), H5::PredType::IEEE_F32BE, dataspace); + _value_grp.createDataSet(value_filename(n), H5::PredType::IEEE_F32BE, dataspace); float *data = new float[nncc::core::ADT::tensor::num_elements(shape)]; @@ -84,7 +84,7 @@ public: H5::StrType name_datatype(H5::PredType::C_S1, name.size()); auto name_attr = - _name_grp.createAttribute(value_filename(n), name_datatype, name_dataspace); + _name_grp.createAttribute(value_filename(n), name_datatype, name_dataspace); name_attr.write(name_datatype, name); } @@ -101,9 +101,10 @@ private: }; #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> + +#include <memory> extern "C" std::unique_ptr<nnkit::Action> make_action(const nnkit::CmdlineArguments &args) { - return stdex::make_unique<HD5ExportAction>(args.at(0)); + return std::make_unique<HD5ExportAction>(args.at(0)); } diff --git a/compiler/nnkit/actions/HDF5/Import.cpp b/compiler/nnkit/actions/HDF5/Import.cpp index bba5ab701..069f42f56 100644 --- a/compiler/nnkit/actions/HDF5/Import.cpp +++ b/compiler/nnkit/actions/HDF5/Import.cpp @@ -92,9 +92,10 @@ private: }; #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> + +#include <memory> extern "C" std::unique_ptr<nnkit::Action> make_action(const nnkit::CmdlineArguments &args) { - return stdex::make_unique<HD5ImportAction>(args.at(0)); + return std::make_unique<HD5ImportAction>(args.at(0)); } diff --git a/compiler/nnkit/actions/builtin/CMakeLists.txt b/compiler/nnkit/actions/builtin/CMakeLists.txt index 910e12ea9..4de70dfc3 100644 --- a/compiler/nnkit/actions/builtin/CMakeLists.txt +++ b/compiler/nnkit/actions/builtin/CMakeLists.txt @@ -1,7 +1,5 @@ add_library(nnkit_show_action SHARED Show.cpp) target_link_libraries(nnkit_show_action nnkit_intf_action) -target_link_libraries(nnkit_show_action stdex) add_library(nnkit_randomize_action SHARED Randomize.cpp) target_link_libraries(nnkit_randomize_action nnkit_intf_action) -target_link_libraries(nnkit_randomize_action stdex) diff --git a/compiler/nnkit/actions/builtin/Randomize.cpp b/compiler/nnkit/actions/builtin/Randomize.cpp index 9b023ef3b..b6e17c7c3 100644 --- a/compiler/nnkit/actions/builtin/Randomize.cpp +++ b/compiler/nnkit/actions/builtin/Randomize.cpp @@ -52,9 +52,10 @@ struct RandomizeAction final : public nnkit::Action }; #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> + +#include <memory> extern "C" std::unique_ptr<nnkit::Action> make_action(const nnkit::CmdlineArguments &args) { - return stdex::make_unique<RandomizeAction>(); + return std::make_unique<RandomizeAction>(); } diff --git a/compiler/nnkit/actions/builtin/Show.cpp b/compiler/nnkit/actions/builtin/Show.cpp index 2630177ef..0be15a8cd 100644 --- a/compiler/nnkit/actions/builtin/Show.cpp +++ b/compiler/nnkit/actions/builtin/Show.cpp @@ -63,9 +63,10 @@ void ShowAction::run(nnkit::TensorContext &ctx) } #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> + +#include <memory> extern "C" std::unique_ptr<nnkit::Action> make_action(const nnkit::CmdlineArguments &args) { - return stdex::make_unique<ShowAction>(); + return std::make_unique<ShowAction>(); } diff --git a/compiler/nnkit/tools/benchmark/CMakeLists.txt b/compiler/nnkit/tools/benchmark/CMakeLists.txt index c2cde00f4..7f01f8bd1 100644 --- a/compiler/nnkit/tools/benchmark/CMakeLists.txt +++ b/compiler/nnkit/tools/benchmark/CMakeLists.txt @@ -11,4 +11,3 @@ file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(nnkit-benchmark ${SOURCES}) target_link_libraries(nnkit-benchmark nnkit_support_cmdline) target_link_libraries(nnkit-benchmark nnkit_support_backend) -target_link_libraries(nnkit-benchmark stdex) diff --git a/compiler/nnkit/tools/benchmark/src/Benchmark.cpp b/compiler/nnkit/tools/benchmark/src/Benchmark.cpp index 6c3ebc90b..632c989bd 100644 --- a/compiler/nnkit/tools/benchmark/src/Benchmark.cpp +++ b/compiler/nnkit/tools/benchmark/src/Benchmark.cpp @@ -18,8 +18,7 @@ #include <nnkit/VectorArguments.h> #include <nnkit/BackendPlugin.h> -#include <stdex/Memory.h> - +#include <memory> #include <map> #include <string> @@ -28,7 +27,7 @@ #include <iostream> #include <iomanip> -using stdex::make_unique; +using std::make_unique; using std::chrono::milliseconds; using std::chrono::microseconds; diff --git a/compiler/nnkit/tools/run/CMakeLists.txt b/compiler/nnkit/tools/run/CMakeLists.txt index 5f42ed941..d1b716090 100644 --- a/compiler/nnkit/tools/run/CMakeLists.txt +++ b/compiler/nnkit/tools/run/CMakeLists.txt @@ -19,4 +19,3 @@ target_link_libraries(nnkit-run nnkit_intf_action) target_link_libraries(nnkit-run nnkit_intf_backend) target_link_libraries(nnkit-run nnkit_support_cmdline) target_link_libraries(nnkit-run nnkit_support_backend) -target_link_libraries(nnkit-run stdex) diff --git a/compiler/nnkit/tools/run/nnkit-run.cpp b/compiler/nnkit/tools/run/nnkit-run.cpp index e60e5797a..cc5a337bd 100644 --- a/compiler/nnkit/tools/run/nnkit-run.cpp +++ b/compiler/nnkit/tools/run/nnkit-run.cpp @@ -35,7 +35,7 @@ public: private: nnkit::VectorArguments _args; }; -} +} // namespace namespace { @@ -59,7 +59,7 @@ private: std::string _path; std::unique_ptr<nnkit::BackendPlugin> _plugin; }; -} +} // namespace // TODO Extract Action-related helpers #include <nnkit/Action.h> @@ -120,7 +120,7 @@ private: void *_handle; Entry _entry; }; -} +} // namespace namespace { @@ -139,10 +139,9 @@ public: private: ActionBinder _binder; }; -} - -#include <stdex/Memory.h> +} // namespace +#include <memory> #include <map> #include <iostream> @@ -170,7 +169,7 @@ int main(int argc, char **argv) std::map<std::string, std::function<void(const std::string &arg)>> argparse; argparse["--backend"] = [§ions](const std::string &tag) { - sections.backend = stdex::make_unique<BackendSection>(tag); + sections.backend = std::make_unique<BackendSection>(tag); }; argparse["--backend-arg"] = [§ions](const std::string &arg) { diff --git a/compiler/nnop/include/nnop/PadInfo.h b/compiler/nnop/include/nnop/PadInfo.h index 228f08514..d17a33abf 100644 --- a/compiler/nnop/include/nnop/PadInfo.h +++ b/compiler/nnop/include/nnop/PadInfo.h @@ -26,7 +26,7 @@ class PadInfo { public: PadInfo(uint32_t top, uint32_t bottom, uint32_t left, uint32_t right) - : _top{top}, _bottom{bottom}, _left{left}, _right{right} + : _top{top}, _bottom{bottom}, _left{left}, _right{right} { // DO NOTHING } diff --git a/compiler/nnop/include/nnop/StrideInfo.h b/compiler/nnop/include/nnop/StrideInfo.h index e47489fa7..653603d6c 100644 --- a/compiler/nnop/include/nnop/StrideInfo.h +++ b/compiler/nnop/include/nnop/StrideInfo.h @@ -39,6 +39,6 @@ private: uint32_t _vertical; }; -} // namespace nncc +} // namespace nnop #endif // __NNOP_STRIDE_INFO_H__ diff --git a/compiler/nnsuite/conv/model/src/RandomModel.cpp b/compiler/nnsuite/conv/model/src/RandomModel.cpp index 7b15d4c96..6d4a6147d 100644 --- a/compiler/nnsuite/conv/model/src/RandomModel.cpp +++ b/compiler/nnsuite/conv/model/src/RandomModel.cpp @@ -28,8 +28,8 @@ namespace conv { RandomModel::RandomModel(int32_t seed) - : _ifm_shape{1, 8, 8}, _ifm_name{"ifm"}, _ofm_name{"ofm"}, _ofm_shape{2, 6, 6}, - _ker_buffer{kernel::Shape{2, 1, 3, 3}, kernel::NCHWLayout{}} + : _ifm_shape{1, 8, 8}, _ifm_name{"ifm"}, _ofm_name{"ofm"}, _ofm_shape{2, 6, 6}, + _ker_buffer{kernel::Shape{2, 1, 3, 3}, kernel::NCHWLayout{}} { std::default_random_engine gen{static_cast<uint32_t>(seed)}; std::normal_distribution<float> dist{0.0f, 1.0f}; diff --git a/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt b/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt index 6445cc6fb..7e860f874 100644 --- a/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt +++ b/compiler/nnsuite/conv/nnkit-caffe/CMakeLists.txt @@ -9,7 +9,6 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_library(nnsuite_conv_caffe SHARED ${SOURCES}) target_link_libraries(nnsuite_conv_caffe nnsuite_conv) target_link_libraries(nnsuite_conv_caffe nnkit_support_caffe) -target_link_libraries(nnsuite_conv_caffe stdex) nnas_find_package(GTest QUIET) diff --git a/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.cpp b/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.cpp index 31d2b33fc..664ca94f3 100644 --- a/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.cpp +++ b/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.cpp @@ -23,9 +23,9 @@ #include <nncc/core/ADT/kernel/Overlay.h> #include <nncc/core/ADT/kernel/NCHWLayout.h> -#include <stdex/Memory.h> +#include <memory> -using stdex::make_unique; +using std::make_unique; std::unique_ptr<nnkit::Backend> ConvBackend::create(const nnsuite::conv::Model &model) { diff --git a/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.test.cpp b/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.test.cpp index 776bf186b..20c42385a 100644 --- a/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.test.cpp +++ b/compiler/nnsuite/conv/nnkit-caffe/ConvBackend.test.cpp @@ -35,8 +35,8 @@ public: TestModel(const std::string &ifm_name, const feature::Shape &ifm_shape, const std::string &ofm_name, const feature::Shape &ofm_shape, const kernel::Shape &ker_shape, const kernel::Layout &ker_layout, float *ker_data) - : _ifm_name(ifm_name), _ifm_shape(ifm_shape), _ofm_name(ofm_name), _ofm_shape(ofm_shape), - _ker{ker_shape, ker_layout, ker_data} + : _ifm_name(ifm_name), _ifm_shape(ifm_shape), _ofm_name(ofm_name), + _ofm_shape(ofm_shape), _ker{ker_shape, ker_layout, ker_data} { // DO NOTHING } diff --git a/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt b/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt index c1cf88812..8e870490e 100644 --- a/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt +++ b/compiler/nnsuite/conv/nnkit-tflite/CMakeLists.txt @@ -9,7 +9,6 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_library(nnsuite_conv_tflite SHARED ${SOURCES}) target_link_libraries(nnsuite_conv_tflite nnsuite_conv) target_link_libraries(nnsuite_conv_tflite nnkit_support_tflite-1.7) -target_link_libraries(nnsuite_conv_tflite stdex) nnas_find_package(GTest QUIET) diff --git a/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.cpp b/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.cpp index 8ec9ce491..ea189ff6e 100644 --- a/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.cpp +++ b/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.cpp @@ -74,7 +74,7 @@ static inline std::vector<int> as_dims(const nncc::core::ADT::kernel::Shape &sha } ConvBackend::ConvBackend(const nnsuite::conv::Model &model) - : _ifm_name{model.ifm_name()}, _ofm_name{model.ofm_name()} + : _ifm_name{model.ifm_name()}, _ofm_name{model.ofm_name()} { using nncc::core::ADT::kernel::Overlay; using nncc::core::ADT::kernel::NHWCLayout; @@ -123,12 +123,12 @@ ConvBackend::ConvBackend(const nnsuite::conv::Model &model) as_dims(model.ifm_shape()), quantization); _interp.SetTensorParametersReadOnly( - 2, kTfLiteFloat32 /* type */, "kernel" /* name */, as_dims(model.ker_shape()), quantization, - reinterpret_cast<const char *>(_kernel.data()), _kernel.size() * sizeof(float)); + 2, kTfLiteFloat32 /* type */, "kernel" /* name */, as_dims(model.ker_shape()), quantization, + reinterpret_cast<const char *>(_kernel.data()), _kernel.size() * sizeof(float)); _interp.SetTensorParametersReadOnly( - 3, kTfLiteFloat32 /* type */, "bias" /* name */, {static_cast<int>(_bias.size())}, - quantization, reinterpret_cast<const char *>(_bias.data()), _bias.size() * sizeof(float)); + 3, kTfLiteFloat32 /* type */, "bias" /* name */, {static_cast<int>(_bias.size())}, quantization, + reinterpret_cast<const char *>(_bias.data()), _bias.size() * sizeof(float)); auto param = typed_malloc<TfLiteConvParams>(); diff --git a/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.test.cpp b/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.test.cpp index db82f0cf9..98ac78fc2 100644 --- a/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.test.cpp +++ b/compiler/nnsuite/conv/nnkit-tflite/ConvBackend.test.cpp @@ -38,8 +38,8 @@ public: TestModel(const std::string &ifm_name, const feature::Shape &ifm_shape, const std::string &ofm_name, const feature::Shape &ofm_shape, const kernel::Shape &ker_shape, const kernel::Layout &ker_layout, float *ker_data) - : _ifm_name(ifm_name), _ifm_shape(ifm_shape), _ofm_name(ofm_name), _ofm_shape(ofm_shape), - _ker{ker_shape, ker_layout, ker_data} + : _ifm_name(ifm_name), _ifm_shape(ifm_shape), _ofm_name(ofm_name), + _ofm_shape(ofm_shape), _ker{ker_shape, ker_layout, ker_data} { // DO NOTHING } diff --git a/compiler/nnsuite/conv/nnkit-tflite/Entry.cpp b/compiler/nnsuite/conv/nnkit-tflite/Entry.cpp index 2c84f72e6..c1e013767 100644 --- a/compiler/nnsuite/conv/nnkit-tflite/Entry.cpp +++ b/compiler/nnsuite/conv/nnkit-tflite/Entry.cpp @@ -21,8 +21,7 @@ #include <nnkit/Backend.h> #include <nnkit/CmdlineArguments.h> -#include <stdex/Memory.h> - +#include <memory> #include <chrono> #include <iostream> @@ -40,5 +39,5 @@ extern "C" std::unique_ptr<nnkit::Backend> make_backend(const nnkit::CmdlineArgu const nnsuite::conv::RandomModel model{seed}; - return stdex::make_unique<ConvBackend>(model); + return std::make_unique<ConvBackend>(model); } diff --git a/compiler/one-cmds/CMakeLists.txt b/compiler/one-cmds/CMakeLists.txt index 1472295c3..7d7a28fe1 100644 --- a/compiler/one-cmds/CMakeLists.txt +++ b/compiler/one-cmds/CMakeLists.txt @@ -4,6 +4,7 @@ set(ONE_COMMAND_FILES one-import-bcq one-import-tf one-import-tflite + one-import-onnx one-optimize one-quantize one-pack @@ -78,4 +79,6 @@ if(NOT ENABLE_TEST) return() endif(NOT ENABLE_TEST) +add_subdirectory(dummy-driver) add_subdirectory(tests) +add_subdirectory(validate-onnx2circle) diff --git a/compiler/one-cmds/dummy-driver/CMakeLists.txt b/compiler/one-cmds/dummy-driver/CMakeLists.txt new file mode 100644 index 000000000..8e122ccf3 --- /dev/null +++ b/compiler/one-cmds/dummy-driver/CMakeLists.txt @@ -0,0 +1,21 @@ +# dummy driver for interface test +set(DUMMY_DRIVER_SRC src/dummy-compile.cpp) +set(HELP_DRIVER_SRC src/help-compile.cpp) + +add_executable(dummy-compile ${DUMMY_DRIVER_SRC}) +add_executable(help-compile ${HELP_DRIVER_SRC}) + +set(DUMMY_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/dummy-compile") +set(HELP_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/help-compile") + +install(FILES ${DUMMY_DRIVER} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) + +install(FILES ${HELP_DRIVER} + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) diff --git a/compiler/one-cmds/dummy-driver/src/dummy-compile.cpp b/compiler/one-cmds/dummy-driver/src/dummy-compile.cpp new file mode 100644 index 000000000..2ad09a3dd --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/dummy-compile.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 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. + */ + +/** + * dummy-compile only tests its interface rather than its functionality. + * + * ./dummy-compile -o ${OUTPUT_NAME} ${INPUT_NAME} + * + * NOTE argv[3](INPUT_NAME) is not used here. + */ + +#include <iostream> +#include <fstream> +#include <string> + +int main(int argc, char **argv) +{ + if (argc != 4) + return EXIT_FAILURE; + + std::string opt_o{"-o"}; + std::string argv_1{argv[1]}; + + if (opt_o != argv_1) + return EXIT_FAILURE; + + std::string output_name{argv[2]}; + std::ofstream outfile(output_name); + + outfile << "dummy-compile dummy output!!" << std::endl; + + outfile.close(); + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/dummy-driver/src/help-compile.cpp b/compiler/one-cmds/dummy-driver/src/help-compile.cpp new file mode 100644 index 000000000..9be9018f3 --- /dev/null +++ b/compiler/one-cmds/dummy-driver/src/help-compile.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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. + */ + +/** + * help-compile prints dummy help message. + * + * $ ./help-compile -h + * HELP MESSAGE!! + */ + +#include <iostream> +#include <fstream> +#include <string> + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + std::string opt_h{"-h"}; + std::string argv_1{argv[1]}; + + if (opt_h != argv_1) + return EXIT_FAILURE; + + std::cout << "HELP MESSAGE!!" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/compiler/one-cmds/how-to-prepare-virtualenv.txt b/compiler/one-cmds/how-to-prepare-virtualenv.txt index f3dcf704b..6d846c081 100644 --- a/compiler/one-cmds/how-to-prepare-virtualenv.txt +++ b/compiler/one-cmds/how-to-prepare-virtualenv.txt @@ -9,6 +9,9 @@ This document explains about 'one-prepare-venv' command. version 2.3.0, recommanded 2.x version as of now, so that 'one-import-tf' command can execute properly. +'one-prepare-venv' will also prepare onnx and onnx-tensorflow version 1.7.0 so +that 'one-import-onnx' command can execute properly. + Prerequisite ------------ diff --git a/compiler/one-cmds/how-to-use-one-commands.txt b/compiler/one-cmds/how-to-use-one-commands.txt index d4e3269e8..d034fa9a2 100644 --- a/compiler/one-cmds/how-to-use-one-commands.txt +++ b/compiler/one-cmds/how-to-use-one-commands.txt @@ -149,8 +149,16 @@ one-optimize one-optimize provides network or operator transformation shown below. Current transformation options are +- disable_validation : This will turn off operator validations. +- fold_add_v2 : This removes AddV2 operation which can be folded +- fold_cast : This removes Cast operation which can be folded - fold_dequantize : This removes Dequantize operation which can be folded +- fold_sparse_to_dense : This removes SparseToDense operation which can be folded +- forward_reshape_to_unaryop: This will move Reshape after UnaryOp for centain condition - fuse_add_with_tconv: This fuses Add operator with the preceding TConv operator if possible +- fuse_batchnorm_with_conv : This fuses BatchNorm operator to convolution operator +- fuse_batchnorm_with_dwconv : This fuses BatchNorm operator to depthwise convolution operator +- fuse_batchnorm_with_tconv : This fuses BatchNorm operator to transpose convolution operator - fuse_bcq: This enables Binary-Coded-bases Quantized DNNs - read https://arxiv.org/abs/2005.09904 for detailed information - fuse_instnorm: This will convert instance normalization related operators to @@ -161,12 +169,30 @@ Current transformation options are - make_batchnorm_gamma_positive: This makes negative gamma of batch normalization into a small positive value (1e-10). Note that this pass can change the execution result of the model. So, use it only when the impact is known to be acceptable. +- mute_warnings : This will turn off warning messages. +- generate_profile_data : This will turn on profiling data generation. +- remove_redundant_reshape : This fuses or removes redundant reshape operators. +- remove_redundant_transpose : This fuses or removes redundant transpose operators. +- remove_unnecessary_reshape : This removes unnecessary reshape operators. +- remove_unnecessary_slice : This removes unnecessary slice operators. +- remove_unnecessary_strided_slice : This removes unnecessary strided slice operators. +- remove_unnecessary_split : This removes unnecessary split operators. - replace_cw_mul_add_with_depthwise_conv: This will replace channel-wise Mul/Add with DepthwiseConv2D. - resolve_customop_add: This will convert Custom(Add) to normal Add operator - resolve_customop_batchmatmul: This will convert Custom(BatchMatMul) to normal BatchMatMul operator - resolve_customop_matmul: This will convert Custom(MatMul) to normal MatMul operator +- shuffle_weight_to_16x1float32 : This will convert weight format of FullyConnected to SHUFFLED16x1FLOAT32. + Note that it only converts weights whose row is a multiple of 16. +- substitute_pack_to_reshape : This will convert single input Pack to Reshape. +- substitute_squeeze_to_reshape : This will convert certain condition Squeeze to Reshape. +- substitute_transpose_to_reshape : This will convert certain condition Transpose to Reshape. +- transform_min_max_to_relu6: This will transform Minimum-Maximum pattern to Relu6 operator. + +There are options to enable multiple options at once for convenience. +- O1: fuse_bcq, fuse_instnorm, resolve_customop_add, resolve_customop_batchmatmul, + resolve_customop_matmul, remove_redundant_transpose, substitute_pack_to_reshape one-quantize diff --git a/compiler/one-cmds/one-build b/compiler/one-cmds/one-build index 82b193f9e..34ce42fca 100644 --- a/compiler/one-cmds/one-build +++ b/compiler/one-cmds/one-build @@ -3,6 +3,7 @@ ''''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) 2020 Samsung Electronics Co., Ltd. All Rights Reserved # @@ -57,7 +58,9 @@ def _get_driver_name(driver_name): 'one-import-bcq': 'one-import-bcq', 'one-import-tf': 'one-import-tf', 'one-import-tflite': 'one-import-tflite', + 'one-import-onnx': 'one-import-onnx', 'one-optimize': 'one-optimize', + 'one-quantize': 'one-quantize', 'one-pack': 'one-pack', 'one-codegen': 'one-codegen' }[driver_name] @@ -78,7 +81,7 @@ def _is_available_driver(config, driver_name): def _verify_cfg(driver_list, config): if not config.has_section('one-build'): - raise ImportError('\'one-build\' section is required in configuraion file') + raise ImportError('[one-build] section is required in configuraion file') import_driver_cnt = 0 if _is_available_driver(config, 'one-import-tf'): @@ -87,6 +90,8 @@ def _verify_cfg(driver_list, config): import_driver_cnt += 1 if _is_available_driver(config, 'one-import-bcq'): import_driver_cnt += 1 + if _is_available_driver(config, 'one-import-onnx'): + import_driver_cnt += 1 if import_driver_cnt > 1: raise AssertionError('Only one import-* driver can be executed') @@ -106,8 +111,8 @@ def main(): # verify configuration file drivers = [ - 'one-import-tf', 'one-import-tflite', 'one-import-bcq', 'one-optimize', - 'one-quantize', 'one-pack', 'one-codegen' + 'one-import-tf', 'one-import-tflite', 'one-import-bcq', 'one-import-onnx', + 'one-optimize', 'one-quantize', 'one-pack', 'one-codegen' ] _verify_cfg(drivers, config) diff --git a/compiler/one-cmds/one-build.template.cfg b/compiler/one-cmds/one-build.template.cfg index ab6ac81d7..52d860813 100644 --- a/compiler/one-cmds/one-build.template.cfg +++ b/compiler/one-cmds/one-build.template.cfg @@ -2,6 +2,7 @@ one-import-tf=True one-import-tflite=False one-import-bcq=False +one-import-onnx=False one-optimize=True one-quantize=False one-pack=True @@ -18,6 +19,7 @@ converter_version=v1 [one-optimize] input_path=inception_v3.circle output_path=inception_v3.opt.circle +generate_profile_data=False [one-pack] input_path=inception_v3.opt.circle diff --git a/compiler/one-cmds/one-codegen b/compiler/one-cmds/one-codegen index fbe3d52d2..ebd8ad7e5 100644 --- a/compiler/one-cmds/one-codegen +++ b/compiler/one-cmds/one-codegen @@ -20,6 +20,8 @@ # limitations under the License. import argparse +import copy +import itertools import os import subprocess import sys @@ -40,7 +42,9 @@ def _get_backends_list(): def _get_parser(): - parser = argparse.ArgumentParser(description='command line tool for code generation') + codegen_usage = 'one-codegen [-h] [-v] [-C CONFIG] [-b BACKEND] [--] [COMMANDS FOR BACKEND]' + parser = argparse.ArgumentParser( + description='command line tool for code generation', usage=codegen_usage) _utils._add_default_arg(parser) @@ -68,18 +72,35 @@ def _verify_arg(parser, args): def _parse_arg(parser): - args, unknown_args = parser.parse_known_args() + codegen_args = [] + backend_args = [] + unknown_args = [] + argv = copy.deepcopy(sys.argv) + # delete file name + del argv[0] + # split by '--' + args = [list(y) for x, y in itertools.groupby(argv, lambda z: z == '--') if not x] + # one-codegen has two interfaces + # 1. one-codegen [-h] [-v] [-C CONFIG] [-b BACKEND] [COMMANDS FOR BACKEND] + if len(args) == 1: + codegen_args = args[0] + codegen_args, unknown_args = parser.parse_known_args(codegen_args) + # 2. one-codegen [-h] [-v] [-C CONFIG] [-b BACKEND] -- [COMMANDS FOR BACKEND] + if len(args) == 2: + codegen_args = args[0] + backend_args = args[1] + codegen_args = parser.parse_args(codegen_args) # print version - if args.version: + if len(args) and codegen_args.version: _utils._print_version_and_exit(__file__) - return args, unknown_args + return codegen_args, backend_args, unknown_args def main(): # parse arguments parser = _get_parser() - args, unknown_args = _parse_arg(parser) + args, backend_args, unknown_args = _parse_arg(parser) # parse configuration file _utils._parse_cfg(args, 'one-codegen') @@ -90,7 +111,7 @@ def main(): # make a command to run given backend driver dir_path = os.path.dirname(os.path.realpath(__file__)) codegen_path = os.path.join(dir_path, getattr(args, 'backend') + '-compile') - codegen_cmd = [codegen_path] + unknown_args + codegen_cmd = [codegen_path] + backend_args + unknown_args if _utils._is_valid_attr(args, 'command'): codegen_cmd += getattr(args, 'command').split() @@ -100,6 +121,8 @@ def main(): bufsize=1) as p: for line in p.stdout: sys.stdout.buffer.write(line) + if p.returncode != 0: + sys.exit(p.returncode) if __name__ == '__main__': diff --git a/compiler/one-cmds/one-import-onnx b/compiler/one-cmds/one-import-onnx new file mode 100644 index 000000000..1bcf2e838 --- /dev/null +++ b/compiler/one-cmds/one-import-onnx @@ -0,0 +1,161 @@ +#!/usr/bin/env bash +''''export SCRIPT_PATH="$(cd "$(dirname "${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) 2021 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 os +import subprocess +import sys +import tempfile +import onnx +import onnx_tf + +import utils as _utils + + +def _get_parser(): + parser = argparse.ArgumentParser( + description='command line tool to convert ONNX to circle') + + _utils._add_default_arg(parser) + + ## tf2tfliteV2 arguments + tf2tfliteV2_group = parser.add_argument_group('converter arguments') + + # input and output path. + tf2tfliteV2_group.add_argument( + '-i', '--input_path', type=str, help='full filepath of the input file') + tf2tfliteV2_group.add_argument( + '-o', '--output_path', type=str, help='full filepath of the output file') + + # input and output arrays. + tf2tfliteV2_group.add_argument( + '-I', + '--input_arrays', + type=str, + help='names of the input arrays, comma-separated') + tf2tfliteV2_group.add_argument( + '-O', + '--output_arrays', + type=str, + help='names of the output arrays, comma-separated') + + # fixed options + tf2tfliteV2_group.add_argument('--model_format', default='saved_model') + tf2tfliteV2_group.add_argument('--converter_version', default='v2') + + return parser + + +def _verify_arg(parser, args): + """verify given arguments""" + # check if required arguments is given + missing = [] + if not _utils._is_valid_attr(args, 'input_path'): + missing.append('-i/--input_path') + if not _utils._is_valid_attr(args, 'output_path'): + missing.append('-o/--output_path') + if len(missing): + parser.error('the following arguments are required: ' + ' '.join(missing)) + + +def _parse_arg(parser): + args = parser.parse_args() + # print version + if args.version: + _utils._print_version_and_exit(__file__) + + return args + + +def _convert(args): + # get file path to log + dir_path = os.path.dirname(os.path.realpath(__file__)) + logfile_path = os.path.realpath(args.output_path) + '.log' + + with open(logfile_path, 'wb') as f, tempfile.TemporaryDirectory() as tmpdir: + # convert onnx to tf saved model + onnx_model = onnx.load(getattr(args, 'input_path')) + tf_savedmodel = onnx_tf.backend.prepare(onnx_model) + + savedmodel_name = os.path.splitext(os.path.basename( + args.output_path))[0] + '.savedmodel' + savedmodel_output_path = os.path.join(tmpdir, savedmodel_name) + tf_savedmodel.export_graph(savedmodel_output_path) + + # make a command to convert from tf to tflite + tf2tfliteV2_path = os.path.join(dir_path, 'tf2tfliteV2.py') + tf2tfliteV2_output_name = os.path.splitext(os.path.basename( + args.output_path))[0] + '.tflite' + tf2tfliteV2_output_path = os.path.join(tmpdir, tf2tfliteV2_output_name) + + tf2tfliteV2_cmd = _utils._make_tf2tfliteV2_cmd( + args, tf2tfliteV2_path, savedmodel_output_path, tf2tfliteV2_output_path) + + f.write((' '.join(tf2tfliteV2_cmd) + '\n').encode()) + + # convert tf to tflite + with subprocess.Popen( + tf2tfliteV2_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + bufsize=1) as p: + for line in p.stdout: + sys.stdout.buffer.write(line) + f.write(line) + if p.returncode != 0: + sys.exit(p.returncode) + + # make a command to convert from tflite to circle + tflite2circle_path = os.path.join(dir_path, 'tflite2circle') + tflite2circle_cmd = _utils._make_tflite2circle_cmd(tflite2circle_path, + tf2tfliteV2_output_path, + getattr(args, 'output_path')) + + f.write((' '.join(tflite2circle_cmd) + '\n').encode()) + + # convert tflite to circle + with subprocess.Popen( + tflite2circle_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + bufsize=1) as p: + for line in p.stdout: + sys.stdout.buffer.write(line) + f.write(line) + if p.returncode != 0: + sys.exit(p.returncode) + + +def main(): + # parse arguments + parser = _get_parser() + args = _parse_arg(parser) + + # parse configuration file + _utils._parse_cfg(args, 'one-import-onnx') + + # verify arguments + _verify_arg(parser, args) + + # convert + _convert(args) + + +if __name__ == '__main__': + main() diff --git a/compiler/one-cmds/one-import-tflite b/compiler/one-cmds/one-import-tflite index fba697f24..9b80f304b 100644 --- a/compiler/one-cmds/one-import-tflite +++ b/compiler/one-cmds/one-import-tflite @@ -90,6 +90,8 @@ def _convert(args): for line in p.stdout: sys.stdout.buffer.write(line) f.write(line) + if p.returncode != 0: + sys.exit(p.returncode) def main(): diff --git a/compiler/one-cmds/one-optimize b/compiler/one-cmds/one-optimize index f03bb8dcc..8ce79d432 100644 --- a/compiler/one-cmds/one-optimize +++ b/compiler/one-cmds/one-optimize @@ -34,6 +34,15 @@ def _get_parser(): _utils._add_default_arg(parser) + ## utility arguments + utility_group = parser.add_argument_group('arguments for utility') + + utility_group.add_argument( + '-p', + '--generate_profile_data', + action='store_true', + help='generate profiling data') + ## circle2circle arguments circle2circle_group = parser.add_argument_group('arguments for optimization') @@ -44,50 +53,9 @@ def _get_parser(): '-o', '--output_path', type=str, help='full filepath of the output file') # optimization pass - circle2circle_group.add_argument( - '--all', action='store_true', help='enable all optimization pass') - circle2circle_group.add_argument( - '--fold_dequantize', action='store_true', help='fold Dequantize op') - circle2circle_group.add_argument( - '--fuse_add_with_tconv', action='store_true', help='fuse Add op to Transposed') - circle2circle_group.add_argument( - '--fuse_batchnorm_with_tconv', - action='store_true', - help='fuse BatchNorm op to Transposed Convolution op') - circle2circle_group.add_argument( - '--fuse_bcq', action='store_true', help='apply Binary Coded Quantization') - circle2circle_group.add_argument( - '--fuse_preactivation_batchnorm', - action='store_true', - help='fuse BatchNorm operators of pre-activations to Convolution op') - circle2circle_group.add_argument( - '--make_batchnorm_gamma_positive', - action='store_true', - help="""make negative gamma of BatchNorm to a small positive value (1e-10). - Note that this pass can change the execution result of the model. - So, use it only when the impact is known to be acceptable.""") - circle2circle_group.add_argument( - '--fuse_activation_function', - action='store_true', - help='fuse Activation function to a preceding operator') - circle2circle_group.add_argument( - '--fuse_instnorm', action='store_true', help='fuse ops to InstanceNorm operator') - circle2circle_group.add_argument( - '--replace_cw_mul_add_with_depthwise_conv', - action='store_true', - help='replace channel-wise Mul/Add with DepthwiseConv2D') - circle2circle_group.add_argument( - '--resolve_customop_add', - action='store_true', - help='convert Custom(Add) op to Add op') - circle2circle_group.add_argument( - '--resolve_customop_batchmatmul', - action='store_true', - help='convert Custom(BatchMatmul) op to BatchMatmul op') - circle2circle_group.add_argument( - '--resolve_customop_matmul', - action='store_true', - help='convert Custom(Matmul) op to Matmul op') + for opt in _utils._CONSTANT.OPTIMIZATION_OPTS: + # opt = (option_name, help_message) + circle2circle_group.add_argument('--' + opt[0], action='store_true', help=opt[1]) return parser diff --git a/compiler/one-cmds/one-prepare-venv b/compiler/one-cmds/one-prepare-venv index e5c88411f..bb3616574 100644 --- a/compiler/one-cmds/one-prepare-venv +++ b/compiler/one-cmds/one-prepare-venv @@ -51,6 +51,21 @@ python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host file python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host files.pythonhost.org \ install Pillow==6.2.2 +# Install PyTorch and ONNX related +python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host files.pythonhost.org \ + --trusted-host download.pytorch.org \ + install torch==1.7.0+cpu -f https://download.pytorch.org/whl/torch_stable.html + +# NOTE Latest onnx 1.8.1 has compatibility issue with onnx-tf 1.7.0 +# MUST install with onnx==1.8.0 +# Provide install of custom onnx-tf +if [ -n "${EXT_ONNX_TF_WHL}" ]; then + python -m pip --default-timeout=1000 install onnx==1.8.0 ${EXT_ONNX_TF_WHL} +else + python -m pip --default-timeout=1000 --trusted-host pypi.org --trusted-host files.pythonhost.org \ + install onnx==1.8.0 onnx-tf==1.7.0 +fi + # Create python symoblic link rm -f ${DRIVER_PATH}/python ln -s venv/bin/python ${DRIVER_PATH}/python diff --git a/compiler/one-cmds/one-quantize b/compiler/one-cmds/one-quantize index 9bdfea8b8..475f44a49 100644 --- a/compiler/one-cmds/one-quantize +++ b/compiler/one-cmds/one-quantize @@ -38,10 +38,22 @@ def _get_parser(): parser.add_argument( '-i', '--input_path', type=str, help='full filepath of the input file') parser.add_argument( - '-d', '--input_data', type=str, help='full filepath of the input data file') + '-d', + '--input_data', + type=str, + help= + 'full filepath of the input data file. if not specified, run with random input data.' + ) parser.add_argument( '-o', '--output_path', type=str, help='full filepath of the output file') + # argument for profiling + parser.add_argument( + '-p', + '--generate_profile_data', + action='store_true', + help='generate profiling data') + ## arguments for quantization quantization_group = parser.add_argument_group('arguments for quantization') @@ -66,26 +78,30 @@ def _get_parser(): type=str, help='record mode (supported: percentile/moving_average, default=percentile)') - # set default values - quantization_group.set_defaults( - input_dtype='float32', - quantized_dtype='uint8', - granularity='layer', - min_percentile='1.0', - max_percentile='99.0', - mode='percentile') - return parser +def _set_default_values(args): + if not _utils._is_valid_attr(args, 'input_dtype'): + setattr(args, 'input_dtype', 'float32') + if not _utils._is_valid_attr(args, 'quantized_dtype'): + setattr(args, 'quantized_dtype', 'uint8') + if not _utils._is_valid_attr(args, 'granularity'): + setattr(args, 'granularity', 'layer') + if not _utils._is_valid_attr(args, 'mode'): + setattr(args, 'mode', 'percentile') + if not _utils._is_valid_attr(args, 'min_percentile'): + setattr(args, 'min_percentile', '1.0') + if not _utils._is_valid_attr(args, 'max_percentile'): + setattr(args, 'max_percentile', '99.0') + + def _verify_arg(parser, args): """verify given arguments""" # check if required arguments is given missing = [] if not _utils._is_valid_attr(args, 'input_path'): missing.append('-i/--input_path') - if not _utils._is_valid_attr(args, 'input_data'): - missing.append('-d/--input_data') if not _utils._is_valid_attr(args, 'output_path'): missing.append('-o/--output_path') if len(missing): @@ -128,6 +144,9 @@ def _quantize(args): tmpdir, os.path.splitext(os.path.basename(args.input_path))[0]) + '1.circle' circle_quantizer_cmd.append(tmp_output_path_1) + # profiling + if _utils._is_valid_attr(args, 'generate_profile_data'): + circle_quantizer_cmd.append('--generate_profile_data') f.write((' '.join(circle_quantizer_cmd) + '\n').encode()) @@ -168,6 +187,9 @@ def _quantize(args): if _utils._is_valid_attr(args, 'mode'): circle_record_minmax_cmd.append('--mode') circle_record_minmax_cmd.append(getattr(args, 'mode')) + # profiling + if _utils._is_valid_attr(args, 'generate_profile_data'): + circle_record_minmax_cmd.append('--generate_profile_data') f.write((' '.join(circle_record_minmax_cmd) + '\n').encode()) @@ -197,6 +219,9 @@ def _quantize(args): circle_quantizer_cmd.append(tmp_output_path_2) if _utils._is_valid_attr(args, 'output_path'): circle_quantizer_cmd.append(getattr(args, 'output_path')) + # profiling + if _utils._is_valid_attr(args, 'generate_profile_data'): + circle_quantizer_cmd.append('--generate_profile_data') f.write((' '.join(circle_quantizer_cmd) + '\n').encode()) @@ -221,6 +246,9 @@ def main(): # parse configuration file _utils._parse_cfg(args, 'one-quantize') + # set default values + _set_default_values(args) + # verify arguments _verify_arg(parser, args) diff --git a/compiler/one-cmds/tests/CMakeLists.txt b/compiler/one-cmds/tests/CMakeLists.txt index 412787a64..6f9f2847e 100644 --- a/compiler/one-cmds/tests/CMakeLists.txt +++ b/compiler/one-cmds/tests/CMakeLists.txt @@ -17,6 +17,10 @@ file(APPEND "${DRIVER_SCRIPT}" " USER_PATH=$1\n") file(APPEND "${DRIVER_SCRIPT}" " export PATH=$USER_PATH:$PATH\n") file(APPEND "${DRIVER_SCRIPT}" "fi\n") file(APPEND "${DRIVER_SCRIPT}" "\n") +file(APPEND "${DRIVER_SCRIPT}" "# refer https://github.com/Samsung/ONE/issues/6286\n") +file(APPEND "${DRIVER_SCRIPT}" "set -o pipefail\n\n") +file(APPEND "${DRIVER_SCRIPT}" "fail_count=0\n") +file(APPEND "${DRIVER_SCRIPT}" "trap \"(( fail_count++ ))\" ERR\n\n") foreach(TESTITEM IN ITEMS ${TESTITEMS}) get_filename_component(ITEM_PREFIX ${TESTITEM} NAME_WE) @@ -35,7 +39,16 @@ foreach(CONFIGITEM IN ITEMS ${CONFIGITEMS}) install(FILES ${CONFIGITEM} DESTINATION test) endforeach(CONFIGITEM) -file(APPEND "${DRIVER_SCRIPT}" "popd> /dev/null") +file(APPEND "${DRIVER_SCRIPT}" "popd > /dev/null\n\n") + +file(APPEND "${DRIVER_SCRIPT}" +"if [[ $fail_count != 0 ]]; then + echo \"$fail_count TESTS FAILED\" + exit 255 +else + echo \"ALL TESTS PASSED!\" +fi\n +") set(PREPARE_TEST_MATERIALS_SH "${CMAKE_CURRENT_SOURCE_DIR}/prepare_test_materials.sh") set(PREPROCESS_IMAGES_PY "${CMAKE_CURRENT_SOURCE_DIR}/preprocess_images.py") diff --git a/compiler/one-cmds/tests/one-build_001.test b/compiler/one-cmds/tests/one-build_001.test index fb4877344..ebbb3235b 100644 --- a/compiler/one-cmds/tests/one-build_001.test +++ b/compiler/one-cmds/tests/one-build_001.test @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# one-import-tf -> one-optimize + filename_ext="$(basename -- $0)" filename="${filename_ext%.*}" diff --git a/compiler/one-cmds/tests/one-build_002.test b/compiler/one-cmds/tests/one-build_002.test index fdfd607e2..43fce4e6f 100644 --- a/compiler/one-cmds/tests/one-build_002.test +++ b/compiler/one-cmds/tests/one-build_002.test @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# one-import-tf -> one-optimize -> one-pack + filename_ext="$(basename -- $0)" filename="${filename_ext%.*}" diff --git a/compiler/one-cmds/tests/one-build_003.cfg b/compiler/one-cmds/tests/one-build_003.cfg new file mode 100644 index 000000000..6aec3cab6 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_003.cfg @@ -0,0 +1,21 @@ +[one-build] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=False +one-quantize=True +one-pack=False +one-codegen=False + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v1 + +[one-quantize] +input_path=inception_v3.circle +output_path=inception_v3.quantized.circle +input_data=inception_v3_test_data.h5 diff --git a/compiler/one-cmds/tests/one-build_003.test b/compiler/one-cmds/tests/one-build_003.test new file mode 100644 index 000000000..d835be470 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_003.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2020 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 + +configfile="one-build_003.cfg" +outputfile="inception_v3.quantized.circle" + +rm -rf ${outputfile} + +# run test +one-build -C ${configfile} > /dev/null + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-build_004.cfg b/compiler/one-cmds/tests/one-build_004.cfg new file mode 100644 index 000000000..c23405bea --- /dev/null +++ b/compiler/one-cmds/tests/one-build_004.cfg @@ -0,0 +1,20 @@ +[one-build] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=True + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v1 + +[one-codegen] +backend=dummy +command=-o sample.tvn inception_v3.circle diff --git a/compiler/one-cmds/tests/one-build_004.test b/compiler/one-cmds/tests/one-build_004.test new file mode 100644 index 000000000..f4174bd73 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_004.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2020 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-codegen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-build_004.cfg" +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +one-build -C ${configfile} > /dev/null + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-build_005.cfg b/compiler/one-cmds/tests/one-build_005.cfg new file mode 100644 index 000000000..841b37234 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_005.cfg @@ -0,0 +1,20 @@ +[one-build] +one-import-tf=False +one-import-tflite=True +one-import-bcq=False +one-optimize=True +one-quantize=False +one-pack=False +one-codegen=True + +[one-import-tflite] +input_path=inception_v3.tflite +output_path=inception_v3.circle + +[one-optimize] +input_path=inception_v3.circle +output_path=inception_v3.opt.circle + +[one-codegen] +backend=dummy +command=-o sample.tvn inception_v3.opt.circle diff --git a/compiler/one-cmds/tests/one-build_005.test b/compiler/one-cmds/tests/one-build_005.test new file mode 100644 index 000000000..772483ddc --- /dev/null +++ b/compiler/one-cmds/tests/one-build_005.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2020 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-tflite -> one-optimize -> one-codgen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-build_005.cfg" +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +one-build -C ${configfile} > /dev/null + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-build_006.cfg b/compiler/one-cmds/tests/one-build_006.cfg new file mode 100644 index 000000000..e754bdeca --- /dev/null +++ b/compiler/one-cmds/tests/one-build_006.cfg @@ -0,0 +1,29 @@ +[one-build] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=True +one-quantize=True +one-pack=False +one-codegen=True + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v1 + +[one-optimize] +input_path=inception_v3.circle +output_path=inception_v3.opt.circle + +[one-quantize] +input_path=inception_v3.opt.circle +output_path=inception_v3.quantized.circle +input_data=inception_v3_test_data.h5 + +[one-codegen] +backend=dummy +command=-o sample.tvn inception_v3.quantized.circle diff --git a/compiler/one-cmds/tests/one-build_006.test b/compiler/one-cmds/tests/one-build_006.test new file mode 100644 index 000000000..caf8897b1 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_006.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2020 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-optimize -> one-quantize -> one-codegen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-build_006.cfg" +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +one-build -C ${configfile} > /dev/null + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-build_007.cfg b/compiler/one-cmds/tests/one-build_007.cfg new file mode 100644 index 000000000..52610750d --- /dev/null +++ b/compiler/one-cmds/tests/one-build_007.cfg @@ -0,0 +1,29 @@ +[one-build] +one-import-tf=True +one-import-tflite=False +one-import-bcq=False +one-optimize=False +one-quantize=True +one-pack=True +one-codegen=False + +[one-import-tf] +input_path=inception_v3.pb +output_path=inception_v3.circle +input_arrays=input +input_shapes=1,299,299,3 +output_arrays=InceptionV3/Predictions/Reshape_1 +converter_version=v1 + +[one-optimize] +input_path=inception_v3.circle +output_path=inception_v3.opt.circle + +[one-quantize] +input_path=inception_v3.opt.circle +output_path=inception_v3.quantized.circle +input_data=inception_v3_test_data.h5 + +[one-pack] +input_path=inception_v3.quantized.circle +output_path=inception_v3_pkg diff --git a/compiler/one-cmds/tests/one-build_007.test b/compiler/one-cmds/tests/one-build_007.test new file mode 100644 index 000000000..086187013 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_007.test @@ -0,0 +1,42 @@ +#!/bin/bash + +# Copyright (c) 2020 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-optimize -> one-quantize -> one-pack + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-build_007.cfg" +outputfile="inception_v3_pkg" + +rm -rf ${outputfile} + +# run test +one-build -C ${configfile} > /dev/null + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-build_008.cfg b/compiler/one-cmds/tests/one-build_008.cfg new file mode 100644 index 000000000..615047c86 --- /dev/null +++ b/compiler/one-cmds/tests/one-build_008.cfg @@ -0,0 +1,23 @@ +[one-build] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-import-onnx=True +one-optimize=True +one-quantize=False +one-pack=False +one-codegen=True + +[one-import-onnx] +input_path=test_onnx_model.onnx +output_path=test_onnx_model.circle + +[one-optimize] +input_path=test_onnx_model.circle +output_path=test_onnx_model.opt.circle +all=True +remove_redundant_transpose=True + +[one-codegen] +backend=dummy +command=-o test_onnx_model.bin test_onnx_model.opt.circle diff --git a/compiler/one-cmds/tests/one-build_008.test b/compiler/one-cmds/tests/one-build_008.test new file mode 100644 index 000000000..bfb7666db --- /dev/null +++ b/compiler/one-cmds/tests/one-build_008.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2021 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-optimize -> one-quantize -> one-codegen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-build_008.cfg" +outputfile="test_onnx_model.bin" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +one-build -C ${configfile} > /dev/null + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-build_009.cfg b/compiler/one-cmds/tests/one-build_009.cfg new file mode 100644 index 000000000..66bca250d --- /dev/null +++ b/compiler/one-cmds/tests/one-build_009.cfg @@ -0,0 +1,24 @@ +[one-build] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-import-onnx=True +one-optimize=True +one-quantize=False +one-pack=False +one-codegen=True + +[one-import-onnx] +input_path=onnx_conv2d_conv2d.onnx +output_path=onnx_conv2d_conv2d.circle + +[one-optimize] +input_path=onnx_conv2d_conv2d.circle +output_path=onnx_conv2d_conv2d.opt.circle +all=True +remove_redundant_transpose=True +convert_nchw_to_nhwc=True + +[one-codegen] +backend=dummy +command=-o onnx_conv2d_conv2d.bin onnx_conv2d_conv2d.opt.circle diff --git a/compiler/one-cmds/tests/one-build_009.test b/compiler/one-cmds/tests/one-build_009.test new file mode 100644 index 000000000..0d766261d --- /dev/null +++ b/compiler/one-cmds/tests/one-build_009.test @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (c) 2021 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-onnx -> one-optimize -> one-codegen + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-build_009.cfg" +outputfile="onnx_conv2d_conv2d.bin" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +one-build -C ${configfile} > /dev/null + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-build_neg_003.test b/compiler/one-cmds/tests/one-build_neg_003.test index a8ad24049..bcbd2f98a 100644 --- a/compiler/one-cmds/tests/one-build_neg_003.test +++ b/compiler/one-cmds/tests/one-build_neg_003.test @@ -21,7 +21,7 @@ filename="${filename_ext%.*}" trap_err_onexit() { - if grep -q "'one-build' section is required in configuraion file" "${filename}.log"; then + if grep -q "\[one-build\] section is required in configuraion file" "${filename}.log"; then echo "${filename_ext} SUCCESS" exit 0 fi diff --git a/compiler/one-cmds/tests/one-codegen_001.test b/compiler/one-cmds/tests/one-codegen_001.test new file mode 100644 index 000000000..7c679b38e --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_001.test @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright (c) 2021 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%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# copy help-compile to bin folder +cp help-compile ../bin/help-compile + +# run test +one-codegen -b help -- -h > ${filename}.log + +rm -rf ../bin/help-compile + +if grep -q "HELP MESSAGE!!" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-codegen_002.test b/compiler/one-cmds/tests/one-codegen_002.test new file mode 100644 index 000000000..feb848919 --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_002.test @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (c) 2021 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. + +# run one-codegen with dummy-compile driver + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +one-codegen -b dummy -o ${outputfile} "dummy.circle" + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-codegen_003.test b/compiler/one-cmds/tests/one-codegen_003.test new file mode 100644 index 000000000..47d12a4f1 --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_003.test @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (c) 2021 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. + +# run one-codegen with dummy-compile driver + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + rm -rf ../bin/dummy-compile + exit 255 +} + +trap trap_err_onexit ERR + +outputfile="sample.tvn" + +rm -rf ${outputfile} + +# copy dummy-compile to bin folder +cp dummy-compile ../bin/dummy-compile + +# run test +one-codegen -b dummy -- -o ${outputfile} "dummy.circle" + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +rm -rf ../bin/dummy-compile + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-codegen_004.test b/compiler/one-cmds/tests/one-codegen_004.test new file mode 100644 index 000000000..88f42338d --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_004.test @@ -0,0 +1,38 @@ +#!/bin/bash + +# Copyright (c) 2021 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. + +# print one-codegen's help message + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# run test +one-codegen -h > ${filename}.log + +if grep -q "command line tool for code generation" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 +fi + +trap_err_onexit diff --git a/compiler/one-cmds/tests/one-codegen_neg_001.test b/compiler/one-cmds/tests/one-codegen_neg_001.test new file mode 100644 index 000000000..fd5d0cb30 --- /dev/null +++ b/compiler/one-cmds/tests/one-codegen_neg_001.test @@ -0,0 +1,39 @@ +#!/bin/bash + +# Copyright (c) 2021 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. + +# negative usage with no input + +filename_ext="$(basename -- $0)" +filename="${filename_ext%.*}" + +trap_err_onexit() +{ + if grep -q "error: the following arguments are required" "${filename}.log"; then + echo "${filename_ext} SUCCESS" + exit 0 + fi + + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +# run test +one-codegen > ${filename}.log 2>&1 + +echo "${filename_ext} FAILED" +exit 255 diff --git a/compiler/one-cmds/tests/one-import-onnx_001.test b/compiler/one-cmds/tests/one-import-onnx_001.test new file mode 100644 index 000000000..6119b6882 --- /dev/null +++ b/compiler/one-cmds/tests/one-import-onnx_001.test @@ -0,0 +1,43 @@ +#!/bin/bash + +# Copyright (c) 2021 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%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./test_onnx_model.onnx" +outputfile="./test_onnx_model.circle" + +rm -rf ${outputfile} +rm -rf ${outputfile}.log + +# run test +one-import-onnx \ +--input_path ${inputfile} \ +--output_path ${outputfile} > ${outputfile}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-import_005.cfg b/compiler/one-cmds/tests/one-import_005.cfg new file mode 100644 index 000000000..abe4c7d77 --- /dev/null +++ b/compiler/one-cmds/tests/one-import_005.cfg @@ -0,0 +1,13 @@ +[one-build] +one-import-tf=False +one-import-tflite=False +one-import-bcq=False +one-import-onnx=True +one-optimize=False +one-quantize=False +one-pack=False +one-codegen=False + +[one-import-onnx] +input_path=test_onnx_model.onnx +output_path=test_onnx_model.circle diff --git a/compiler/one-cmds/tests/one-import_005.test b/compiler/one-cmds/tests/one-import_005.test new file mode 100644 index 000000000..ca49db189 --- /dev/null +++ b/compiler/one-cmds/tests/one-import_005.test @@ -0,0 +1,40 @@ +#!/bin/bash + +# Copyright (c) 2021 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%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +configfile="one-import_005.cfg" +outputfile="test_onnx_model.circle" + +rm -f ${outputfile} + +# run test +one-build -C ${configfile} > ${filename}.log 2>&1 + +if [[ ! -s "${outputfile}" ]]; then + trap_err_onexit +fi + +echo "${filename_ext} SUCCESS" diff --git a/compiler/one-cmds/tests/one-optimize_001.test b/compiler/one-cmds/tests/one-optimize_001.test index 240a62506..0d58a6a9e 100644 --- a/compiler/one-cmds/tests/one-optimize_001.test +++ b/compiler/one-cmds/tests/one-optimize_001.test @@ -40,7 +40,7 @@ if [[ ! -s ${inputfile} ]]; then fi # run test -one-optimize --all \ +one-optimize --O1 \ --input_path ${inputfile} \ --output_path ${outputfile} >> /dev/null diff --git a/compiler/one-cmds/tests/one-optimize_neg_001.test b/compiler/one-cmds/tests/one-optimize_neg_001.test index 4ee509697..a30b4164d 100644 --- a/compiler/one-cmds/tests/one-optimize_neg_001.test +++ b/compiler/one-cmds/tests/one-optimize_neg_001.test @@ -39,7 +39,7 @@ rm -rf ${outputfile} rm -rf ${outputfile}.log # run test -one-optimize --all \ +one-optimize --O1 \ --input_path ${inputfile} \ --output_path ${outputfile} > ${filename}.log diff --git a/compiler/one-cmds/tests/one-optimize_neg_002.test b/compiler/one-cmds/tests/one-optimize_neg_002.test index 2c2a29a87..7ccf4a89c 100644 --- a/compiler/one-cmds/tests/one-optimize_neg_002.test +++ b/compiler/one-cmds/tests/one-optimize_neg_002.test @@ -39,7 +39,7 @@ rm -rf ${outputfile} rm -rf ${outputfile}.log # run test -one-optimize --all \ +one-optimize --O1 \ --input_path ${inputfile} \ --output_path ${outputfile} > ${filename}.log diff --git a/compiler/one-cmds/tests/one-optimize_neg_003.test b/compiler/one-cmds/tests/one-optimize_neg_003.test index 95f08fd95..3fe7d330e 100644 --- a/compiler/one-cmds/tests/one-optimize_neg_003.test +++ b/compiler/one-cmds/tests/one-optimize_neg_003.test @@ -44,7 +44,7 @@ if [[ ! -s ${inputfile} ]]; then fi # run test -one-optimize --all \ +one-optimize --O1 \ --input_path "${inputfile}" > "${filename}.log" 2>&1 echo "${filename_ext} FAILED" diff --git a/compiler/one-cmds/tests/one-quantize_002.test b/compiler/one-cmds/tests/one-quantize_002.test new file mode 100644 index 000000000..3704425d4 --- /dev/null +++ b/compiler/one-cmds/tests/one-quantize_002.test @@ -0,0 +1,53 @@ +#!/bin/bash + +# Copyright (c) 2021 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%.*}" + +trap_err_onexit() +{ + echo "${filename_ext} FAILED" + exit 255 +} + +trap trap_err_onexit ERR + +inputfile="./inception_v3.circle" +outputfile="./inception_v3.random.quantized.circle" + +rm -rf ${outputfile} + +# to create inception_v3.circle +if [[ ! -s ${inputfile} ]]; then + /bin/bash one-import_001.test >> /dev/null + return_code=$? + if [[ ${return_code} != 0 ]]; then + trap_err_onexit + fi +fi + +# run test without input data +one-quantize \ +--input_dtype float32 \ +--quantized_dtype uint8 \ +--input_path ./inception_v3.circle \ +--output_path ./inception_v3.random.quantized.circle >> /dev/null + +if [[ ! -s "${outputfile}" ]]; 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 bc3d65d92..694651d74 100644 --- a/compiler/one-cmds/tests/prepare_test_materials.sh +++ b/compiler/one-cmds/tests/prepare_test_materials.sh @@ -77,6 +77,20 @@ if [[ ! -s "test_keras_model.h5" ]]; then # https://github.com/Samsung/ONE/issues/4268#issuecomment-725025805 fi +if [[ ! -s "test_onnx_model.onnx" ]]; then + rm -rf test_onnx_model.zip + wget https://github.com/Samsung/ONE/files/5768243/test_onnx_model.zip + unzip test_onnx_model.zip + # https://github.com/Samsung/ONE/issues/5548#issuecomment-754373360 +fi + +if [[ ! -s "onnx_conv2d_conv2d.onnx" ]]; then + rm -rf onnx_conv2d_conv2d.zip + wget https://github.com/Samsung/ONE/files/5774648/onnx_conv2d_conv2d.zip + unzip onnx_conv2d_conv2d.zip + # https://github.com/Samsung/ONE/issues/5577#issuecomment-755078444 +fi + # prepare 'inception_v3.circle' file used for quantization test inputfile="./inception_v3.pb" outputfile="./inception_v3.circle" diff --git a/compiler/one-cmds/utils.py b/compiler/one-cmds/utils.py index 6eff9d772..1d5c4d4fd 100644 --- a/compiler/one-cmds/utils.py +++ b/compiler/one-cmds/utils.py @@ -21,6 +21,61 @@ import subprocess import sys +class _CONSTANT: + __slots__ = () # This prevents access via __dict__. + OPTIMIZATION_OPTS = ( + # (OPTION_NAME, HELP_MESSAGE) + ('O1', 'enable O1 optimization pass'), + ('convert_nchw_to_nhwc', + 'Experimental: This will convert NCHW operators to NHWC under the assumption that input model is NCHW.' + ), + ('nchw_to_nhwc_preserve_input_shape', + 'preserve the input shape of the model (argument for convert_nchw_to_nhwc)'), + ('nchw_to_nhwc_preserve_output_shape', + 'preserve the output shape of the model (argument for convert_nchw_to_nhwc)'), + ('fold_add_v2', 'fold AddV2 op with constant inputs'), + ('fold_cast', 'fold Cast op with constant input'), + ('fold_dequantize', 'fold Dequantize op'), + ('fold_sparse_to_dense', 'fold SparseToDense op'), + ('forward_reshape_to_unaryop', 'Forward Reshape op'), + ('fuse_add_with_tconv', 'fuse Add op to Transposed'), + ('fuse_batchnorm_with_conv', 'fuse BatchNorm op to Convolution op'), + ('fuse_batchnorm_with_dwconv', 'fuse BatchNorm op to Depthwise Convolution op'), + ('fuse_batchnorm_with_tconv', 'fuse BatchNorm op to Transposed Convolution op'), + ('fuse_bcq', 'apply Binary Coded Quantization'), + ('fuse_preactivation_batchnorm', + 'fuse BatchNorm operators of pre-activations to Convolution op'), + ('make_batchnorm_gamma_positive', + 'make negative gamma of BatchNorm to a small positive value (1e-10).' + ' Note that this pass can change the execution result of the model.' + ' So, use it only when the impact is known to be acceptable.'), + ('fuse_activation_function', 'fuse Activation function to a preceding operator'), + ('fuse_instnorm', 'fuse ops to InstanceNorm operator'), + ('replace_cw_mul_add_with_depthwise_conv', + 'replace channel-wise Mul/Add with DepthwiseConv2D'), + ('remove_redundant_reshape', 'fuse or remove subsequent Reshape ops'), + ('remove_redundant_transpose', 'fuse or remove subsequent Transpose ops'), + ('remove_unnecessary_reshape', 'remove unnecessary reshape ops'), + ('remove_unnecessary_slice', 'remove unnecessary slice ops'), + ('remove_unnecessary_strided_slice', 'remove unnecessary strided slice ops'), + ('remove_unnecessary_split', 'remove unnecessary split ops'), + ('resolve_customop_add', 'convert Custom(Add) op to Add op'), + ('resolve_customop_batchmatmul', + 'convert Custom(BatchMatmul) op to BatchMatmul op'), + ('resolve_customop_matmul', 'convert Custom(Matmul) op to Matmul op'), + ('shuffle_weight_to_16x1float32', + 'convert weight format of FullyConnected op to SHUFFLED16x1FLOAT32.' + ' Note that it only converts weights whose row is a multiple of 16'), + ('substitute_pack_to_reshape', 'convert single input Pack op to Reshape op'), + ('substitute_squeeze_to_reshape', 'convert certain condition Squeeze to Reshape'), + ('substitute_transpose_to_reshape', + 'convert certain condition Transpose to Reshape'), + ('transform_min_max_to_relu6', 'transform Minimum-Maximum pattern to Relu6 op')) + + +_CONSTANT = _CONSTANT() + + def _add_default_arg(parser): # version parser.add_argument( @@ -114,25 +169,13 @@ def _make_tflite2circle_cmd(driver_path, input_path, output_path): def _make_circle2circle_cmd(args, driver_path, input_path, output_path): """make a command for running circle2circle""" cmd = [os.path.expanduser(c) for c in [driver_path, input_path, output_path]] + # profiling + if _is_valid_attr(args, 'generate_profile_data'): + cmd.append('--generate_profile_data') # optimization pass - if _is_valid_attr(args, 'all'): - cmd.append('--all') - if _is_valid_attr(args, 'fold_dequantize'): - cmd.append('--fold_dequantize') - if _is_valid_attr(args, 'fuse_add_with_tconv'): - cmd.append('--fuse_add_with_tconv') - if _is_valid_attr(args, 'fuse_batchnorm_with_tconv'): - cmd.append('--fuse_batchnorm_with_tconv') - if _is_valid_attr(args, 'fuse_bcq'): - cmd.append('--fuse_bcq') - if _is_valid_attr(args, 'fuse_instnorm'): - cmd.append('--fuse_instnorm') - if _is_valid_attr(args, 'resolve_customop_add'): - cmd.append('--resolve_customop_add') - if _is_valid_attr(args, 'resolve_customop_batchmatmul'): - cmd.append('--resolve_customop_batchmatmul') - if _is_valid_attr(args, 'resolve_customop_matmul'): - cmd.append('--resolve_customop_matmul') + for opt in _CONSTANT.OPTIMIZATION_OPTS: + if _is_valid_attr(args, opt[0]): + cmd.append('--' + opt[0]) return cmd diff --git a/compiler/one-cmds/validate-onnx2circle/CMakeLists.txt b/compiler/one-cmds/validate-onnx2circle/CMakeLists.txt new file mode 100644 index 000000000..6727359c9 --- /dev/null +++ b/compiler/one-cmds/validate-onnx2circle/CMakeLists.txt @@ -0,0 +1,5 @@ +install(FILES validate_onnx2circle.py + PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + DESTINATION test) diff --git a/compiler/one-cmds/validate-onnx2circle/README.md b/compiler/one-cmds/validate-onnx2circle/README.md new file mode 100644 index 000000000..341df3d87 --- /dev/null +++ b/compiler/one-cmds/validate-onnx2circle/README.md @@ -0,0 +1,36 @@ +# validate-onnx2circle + +_validate-onnx2circle_ provides validation of onnx to optimized circle conversion +by comparing execution results of original onnx model and optimized circle model. + +This is currently in experimental state. + +## How to run the script + +Install `onnx-runtime` inside virtual environment +``` +source install_path/bin/venv/bin/activate + +python -m pip --default-timeout=1000 --trusted-host pypi.org \ + --trusted-host files.pythonhost.org install onnxruntime==1.6.0 + +deactivate +``` + +Run the sctipt +```bash +cd install_path/test + +driver='one/build/debug/compiler/luci-eval-driver/luci_eval_driver' +onnx_filepath='path_to_onnx_model.onnx' +circle_filepath='path_to_optimized_circle.circle' + +./validate_onnx2circle.py --driver ${driver} --onnx ${onnx_filepath} --circle ${circle_filepath} +``` + +Output will show something like this +``` +Run ONNX... +Run luci-interpreter... +Compare 0 True +``` diff --git a/compiler/one-cmds/validate-onnx2circle/validate_onnx2circle.py b/compiler/one-cmds/validate-onnx2circle/validate_onnx2circle.py new file mode 100644 index 000000000..eac2f6d35 --- /dev/null +++ b/compiler/one-cmds/validate-onnx2circle/validate_onnx2circle.py @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +''''export SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # ''' +''''export PY_PATH=${SCRIPT_PATH}/../bin/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) 2021 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. + +# NOTE This is an experimental script to evaluate onnx-circle conversion +# by running onnxruntime and luci-interpreter. +# Plan is to run this regularly in CI + +import subprocess +import argparse +import numpy as np +import torch +import onnx +import onnxruntime as ort + +parser = argparse.ArgumentParser() +parser.add_argument('--driver', type=str, required=True) +parser.add_argument('--onnx', type=str, required=True) +parser.add_argument('--circle', type=str, required=True) +args = parser.parse_args() + +driver = args.driver +onnx_filepath = args.onnx +circle_filepath = args.circle + + +def to_numpy(tensor): + return tensor.cpu().numpy() + + +def to_nhwc(tensor): + if (tensor.ndim == 4): + return np.transpose(tensor, (0, 2, 3, 1)) + return tensor + + +class OnnxRunner: + def __init__(self, filepath): + self.filepath = filepath + self.session = None + self.inputs = None + self.inputs_size = None + self.inputs_data = None + self.outputs = None + self.outputs_size = None + + def load(self): + model = onnx.load(self.filepath) + onnx.checker.check_model(model) + self.session = ort.InferenceSession(self.filepath) + + def feed_random_inputs(self): + self.inputs = self.session.get_inputs() + self.inputs_size = len(self.inputs) + # reset input dictionary + self.inputs_data = {} + for in_idx in range(self.inputs_size): + input_shape = self.inputs[in_idx].shape + input_type = self.inputs[in_idx].type + if input_type == 'tensor(float)': + torch_type = torch.float32 + else: + # TODO support other dtype + raise SystemExit("Unsupported input dtype") + + x = torch.randn(input_shape, dtype=torch_type) + input_npa = to_numpy(x) + self.inputs_data.update({self.inputs[in_idx].name: input_npa}) + + # save NHWC form of input for luci-interpreter + input_npa_nhwc = to_nhwc(input_npa) + input_npa_nhwc.tofile(circle_filepath + ".input" + str(in_idx)) + + def run(self): + self.outs = self.session.run(None, self.inputs_data) + + def get_outputs(self): + self.outputs = self.session.get_outputs() + self.outputs_size = len(self.outputs) + + +# Run ONNX model +print("Run ONNX...") +onnx_runner = OnnxRunner(onnx_filepath) +onnx_runner.load() +onnx_runner.feed_random_inputs() +onnx_runner.run() +onnx_runner.get_outputs() + +# Execute luci interpreter +print("Run luci-interpreter...") +process = subprocess.run( + [ + driver, circle_filepath, + str(onnx_runner.inputs_size), circle_filepath + ".input", + circle_filepath + ".output" + ], + check=True) + +# Compare results +rtolerance = 1e-03 +atolerance = 1e-04 +result_compare = True +for idx in range(onnx_runner.outputs_size): + output_shape = onnx_runner.outputs[idx].shape + output_type = onnx_runner.outputs[idx].type + if output_type == 'tensor(float)': + output_np_type = np.float32 + else: + # TODO support other dtype + raise SystemExit("Unsupported output dtype") + + # output of luci-interpreter + output_data = np.fromfile(circle_filepath + ".output" + str(idx), output_np_type) + shape_file = open(circle_filepath + ".output" + str(idx) + ".shape", 'r') + output_shape = [int(i) for i in shape_file.read().split(',')] + luci_output_data = np.reshape(output_data, output_shape) + + # output of onnx runtime + output_nchw = onnx_runner.outs[idx] + output_nhwc = to_nhwc(output_nchw) + + # diff has tensor of boolean for each values within tolerance or not + diff = np.isclose(output_nhwc, luci_output_data, rtol=rtolerance, atol=atolerance) + # get one boolean if all are True then True + result_compare_one = np.all(diff) + print("Compare", idx, result_compare_one) + if (not result_compare_one): + diff_val = np.subtract(output_nhwc, luci_output_data) + print("ONNX Result", output_nhwc) + print("Diff", diff_val) + print("Diff Max", np.ndarray.max(diff_val)) + + result_compare = result_compare and result_compare_one + +if (not result_compare): + exit(-1) + +exit(0) diff --git a/compiler/oneco/CMakeLists.txt b/compiler/oneco/CMakeLists.txt index 10f466948..418bc27ac 100644 --- a/compiler/oneco/CMakeLists.txt +++ b/compiler/oneco/CMakeLists.txt @@ -20,7 +20,6 @@ target_include_directories(moco_onnx_frontend PRIVATE src) target_include_directories(moco_onnx_frontend PUBLIC include) target_link_libraries(moco_onnx_frontend PUBLIC moco_onnx_proto) target_link_libraries(moco_onnx_frontend PUBLIC loco) -target_link_libraries(moco_onnx_frontend PRIVATE stdex) target_link_libraries(moco_onnx_frontend PRIVATE cwrap) nnas_find_package(GTest QUIET) diff --git a/compiler/oneco/requires.cmake b/compiler/oneco/requires.cmake index 4e99b0eac..c11a84d9c 100644 --- a/compiler/oneco/requires.cmake +++ b/compiler/oneco/requires.cmake @@ -1,3 +1,2 @@ -require("stdex") require("loco") require("cwrap") diff --git a/compiler/oneco/src/Frontend.cpp b/compiler/oneco/src/Frontend.cpp index d633c1c2e..4b1554ee8 100644 --- a/compiler/oneco/src/Frontend.cpp +++ b/compiler/oneco/src/Frontend.cpp @@ -76,8 +76,8 @@ void load_onnx(const std::string &path, moco::onnx::Frontend::FileType type, // TODO Make comments clear void convert_graph(::onnx::ModelProto &onnx_model_proto, loco::Graph *graph) { - auto nodes = stdex::make_unique<moco::onnx::SymbolTable>(); - auto input_names = stdex::make_unique<moco::onnx::SymbolTable>(); + auto nodes = std::make_unique<moco::onnx::SymbolTable>(); + auto input_names = std::make_unique<moco::onnx::SymbolTable>(); moco::onnx::GraphBuilderContext gb_context(graph, nodes.get(), input_names.get()); diff --git a/compiler/oneco/src/GraphBuilder.h b/compiler/oneco/src/GraphBuilder.h index 7271eb81a..7e463ce9a 100644 --- a/compiler/oneco/src/GraphBuilder.h +++ b/compiler/oneco/src/GraphBuilder.h @@ -27,9 +27,9 @@ namespace onnx { /** -* @brief Parent class of onnx operation graph builders -* @note GraphBuilder call proper build and validate function according to opset version -*/ + * @brief Parent class of onnx operation graph builders + * @note GraphBuilder call proper build and validate function according to opset version + */ class GraphBuilder { public: diff --git a/compiler/oneco/src/GraphBuilderContext.h b/compiler/oneco/src/GraphBuilderContext.h index f1f394b50..dd368e335 100644 --- a/compiler/oneco/src/GraphBuilderContext.h +++ b/compiler/oneco/src/GraphBuilderContext.h @@ -69,13 +69,13 @@ private: }; /** -* @brief Class to store context to build IR from onnx -*/ + * @brief Class to store context to build IR from onnx + */ class GraphBuilderContext { public: GraphBuilderContext(loco::Graph *g, SymbolTable *nodes, SymbolTable *input_names) - : _g(g), _nodes(nodes), _input_names(input_names) + : _g(g), _nodes(nodes), _input_names(input_names) { // DO NOTHING } diff --git a/compiler/oneco/src/GraphBuilderRegistry.h b/compiler/oneco/src/GraphBuilderRegistry.h index 1bf4d9514..863a6ee3a 100644 --- a/compiler/oneco/src/GraphBuilderRegistry.h +++ b/compiler/oneco/src/GraphBuilderRegistry.h @@ -27,15 +27,15 @@ namespace onnx { /** -* @brief Class to return graph builder for passed onnx Operator -*/ + * @brief Class to return graph builder for passed onnx Operator + */ class GraphBuilderRegistry { public: /** - * @brief Returns registered GraphBuilder pointer for operator or - * nullptr if not registered - */ + * @brief Returns registered GraphBuilder pointer for operator or + * nullptr if not registered + */ const GraphBuilder *lookup(const std::string &op) const { if (_builder_map.find(op) == _builder_map.end()) @@ -63,16 +63,16 @@ private: } // namespace onnx } // namespace moco -#include <stdex/Memory.h> +#include <memory> -#define REGISTER_OP_BUILDER(NAME, BUILDER) \ - namespace \ - { \ - __attribute__((constructor)) void reg_op(void) \ - { \ - std::unique_ptr<moco::onnx::BUILDER> builder = stdex::make_unique<moco::onnx::BUILDER>(); \ - moco::onnx::GraphBuilderRegistry::get().add(#NAME, std::move(builder)); \ - } \ +#define REGISTER_OP_BUILDER(NAME, BUILDER) \ + namespace \ + { \ + __attribute__((constructor)) void reg_op(void) \ + { \ + std::unique_ptr<moco::onnx::BUILDER> builder = std::make_unique<moco::onnx::BUILDER>(); \ + moco::onnx::GraphBuilderRegistry::get().add(#NAME, std::move(builder)); \ + } \ } #endif // __MOCO_FRONTEND_ONNX_GRAPH_BUILDER_REGISTRY_H__ diff --git a/compiler/oneco/src/Op/Constant.h b/compiler/oneco/src/Op/Constant.h index e25441d58..be74cfcdd 100644 --- a/compiler/oneco/src/Op/Constant.h +++ b/compiler/oneco/src/Op/Constant.h @@ -24,8 +24,8 @@ namespace onnx { /** - * @brief GraphBuilder for Constant(since version 1) node - */ + * @brief GraphBuilder for Constant(since version 1) node + */ class Constant_V1 { public: @@ -34,10 +34,10 @@ public: }; /** - * @brief GraphBuilder for Constant(since version 9) node - * @note Until version 1, only FLOAT16, FLOAT, DOUBLE was supported - * Since version 9, all types are supported - */ + * @brief GraphBuilder for Constant(since version 9) node + * @note Until version 1, only FLOAT16, FLOAT, DOUBLE was supported + * Since version 9, all types are supported + */ class Constant_V9 { public: @@ -46,8 +46,8 @@ public: }; /** - * @brief GraphBuilder for Constant node - */ + * @brief GraphBuilder for Constant node + */ class ConstantGraphBuilder : public GraphBuilder { public: diff --git a/compiler/oneco/src/Op/Identity.h b/compiler/oneco/src/Op/Identity.h index 41367bea0..dde614592 100644 --- a/compiler/oneco/src/Op/Identity.h +++ b/compiler/oneco/src/Op/Identity.h @@ -24,8 +24,8 @@ namespace onnx { /** - * @brief GraphBuilder for Identity(since version 1) node - */ + * @brief GraphBuilder for Identity(since version 1) node + */ class Identity_V1 { public: @@ -34,8 +34,8 @@ public: }; /** - * @brief GraphBuilder for Identity node - */ + * @brief GraphBuilder for Identity node + */ class IdentityGraphBuilder : public GraphBuilder { public: diff --git a/compiler/onnx2circle/CMakeLists.txt b/compiler/onnx2circle/CMakeLists.txt index a0d393bd9..1a5a7e093 100644 --- a/compiler/onnx2circle/CMakeLists.txt +++ b/compiler/onnx2circle/CMakeLists.txt @@ -20,7 +20,6 @@ target_link_libraries(onnx2circle PRIVATE moco_log) target_link_libraries(onnx2circle PRIVATE exo) target_link_libraries(onnx2circle PRIVATE locop) target_link_libraries(onnx2circle PRIVATE hermes_std) -target_link_libraries(onnx2circle PRIVATE stdex) target_link_libraries(onnx2circle PRIVATE angkor cwrap) target_link_libraries(onnx2circle PRIVATE mir2loco) target_link_libraries(onnx2circle PRIVATE mir_onnx_importer) diff --git a/compiler/onnx2circle/requires.cmake b/compiler/onnx2circle/requires.cmake index f52e40416..b2268ec8b 100644 --- a/compiler/onnx2circle/requires.cmake +++ b/compiler/onnx2circle/requires.cmake @@ -1,4 +1,3 @@ -require("stdex") require("hermes-std") require("mir2loco") require("mir") diff --git a/compiler/onnx2circle/src/onnx2circle.cpp b/compiler/onnx2circle/src/onnx2circle.cpp index c329ed3d5..1c03fa1fe 100644 --- a/compiler/onnx2circle/src/onnx2circle.cpp +++ b/compiler/onnx2circle/src/onnx2circle.cpp @@ -25,10 +25,8 @@ #include "hermes/ConsoleReporter.h" #include "hermes/EnvConfig.h" -#include "stdex/Memory.h" - #include <cassert> - +#include <memory> #include <iostream> #include <stdexcept> #include <string> @@ -56,8 +54,8 @@ struct LoggingContext if (ctx == nullptr) { ctx = new hermes::Context; - ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>()); - ctx->config(stdex::make_unique<EnvConfig>("ONNX2CIRCLE_Log")); + ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>()); + ctx->config(std::make_unique<EnvConfig>("ONNX2CIRCLE_Log")); } return ctx; @@ -81,7 +79,7 @@ int main(int argc, char **argv) using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>; // This line allows users to control all the exo-circle loggers via ONNX2CIRCLE_Log_Backend - exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("ONNX2CIRCLE_Log_Backend")); + exo::LoggingContext::get()->config(std::make_unique<EnvConfig>("ONNX2CIRCLE_Log_Backend")); LOGGER(l); diff --git a/compiler/onnxkit/CMakeLists.txt b/compiler/onnxkit/CMakeLists.txt index 81c3622c9..9ccc779a8 100644 --- a/compiler/onnxkit/CMakeLists.txt +++ b/compiler/onnxkit/CMakeLists.txt @@ -24,7 +24,6 @@ target_include_directories(onnxkitproto PUBLIC ${ONNX_PROTO_INCLUDE_DIRS}) target_link_libraries(onnxkitproto PUBLIC libprotobuf) add_executable(onnxkit ${SOURCES}) -target_link_libraries(onnxkit PRIVATE stdex) target_link_libraries(onnxkit PRIVATE cli) target_link_libraries(onnxkit PRIVATE onnxkitproto) target_link_libraries(onnxkit PRIVATE nncc_common) diff --git a/compiler/onnxkit/README.md b/compiler/onnxkit/README.md index d2066cf65..0a863950e 100644 --- a/compiler/onnxkit/README.md +++ b/compiler/onnxkit/README.md @@ -58,4 +58,3 @@ nncc$ cat decoded.pbtxt | path_to_onnxkit/onnxkit encode > encoded.pb - onnx - Protobuf - cli -- stdex diff --git a/compiler/onnxkit/src/Main.cpp b/compiler/onnxkit/src/Main.cpp index 3dfd580ec..f97590f7d 100644 --- a/compiler/onnxkit/src/Main.cpp +++ b/compiler/onnxkit/src/Main.cpp @@ -18,14 +18,15 @@ #include "DecodeCommand.hpp" #include <cli/App.h> -#include <stdex/Memory.h> + +#include <memory> int main(int argc, char **argv) { cli::App app{argv[0]}; - app.insert("encode", stdex::make_unique<EncodeCommand>()); - app.insert("decode", stdex::make_unique<DecodeCommand>()); + app.insert("encode", std::make_unique<EncodeCommand>()); + app.insert("decode", std::make_unique<DecodeCommand>()); return app.run(argc - 1, argv + 1); } diff --git a/compiler/onnxkit/src/Support.cpp b/compiler/onnxkit/src/Support.cpp index 8c0774175..151290a00 100644 --- a/compiler/onnxkit/src/Support.cpp +++ b/compiler/onnxkit/src/Support.cpp @@ -16,8 +16,7 @@ #include "Support.hpp" -#include <stdex/Memory.h> - +#include <memory> #include <cassert> #include <fstream> #include <stdexcept> @@ -33,7 +32,7 @@ std::unique_ptr<T> open_fstream(const std::string &path, std::ios_base::openmode return nullptr; } - auto stream = stdex::make_unique<T>(path.c_str(), mode); + auto stream = std::make_unique<T>(path.c_str(), mode); if (!stream->is_open()) { throw std::runtime_error{"ERROR: Failed to open " + path}; @@ -61,7 +60,7 @@ std::string Cmdline::get_or(unsigned int index, const std::string &s) const std::unique_ptr<UI> make_ui(const Cmdline &cmdargs) { - auto iocfg = stdex::make_unique<UI>(); + auto iocfg = std::make_unique<UI>(); auto in = open_fstream<std::ifstream>(cmdargs.get_or(0, "-"), std::ios::in | std::ios::binary); iocfg->in(std::move(in)); diff --git a/compiler/oops/CMakeLists.txt b/compiler/oops/CMakeLists.txt index f12572d54..5cc115598 100644 --- a/compiler/oops/CMakeLists.txt +++ b/compiler/oops/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(oops INTERFACE) target_include_directories(oops INTERFACE include) target_link_libraries(oops INTERFACE pepper_str) +target_link_libraries(oops INTERFACE nncc_coverage) if(NOT ENABLE_TEST) return() @@ -8,5 +9,5 @@ endif(NOT ENABLE_TEST) nnas_find_package(GTest REQUIRED) -GTest_AddTest(oops_test test.cpp) +GTest_AddTest(oops_test src/oops.test.cpp) target_link_libraries(oops_test oops) diff --git a/compiler/oops/include/oops/InternalExn.h b/compiler/oops/include/oops/InternalExn.h index e14332bb2..5da3277b7 100644 --- a/compiler/oops/include/oops/InternalExn.h +++ b/compiler/oops/include/oops/InternalExn.h @@ -40,20 +40,20 @@ class InternalExn : public std::exception { public: InternalExn(const char *filename, const int line, const std::string &msg) - : _filename(filename), _line(to_uint32(line)), _msg(msg) + : _filename(filename), _line(to_uint32(line)), _msg(msg) { construct_full_msg(); } explicit InternalExn(const char *filename, const int line, const std::string &msg, uint32_t val) - : _filename(filename), _line(to_uint32(line)), _msg(msg + ": " + std::to_string(val)) + : _filename(filename), _line(to_uint32(line)), _msg(msg + ": " + std::to_string(val)) { construct_full_msg(); } explicit InternalExn(const char *filename, const int line, const std::string &msg, const std::string &val) - : _filename(filename), _line(to_uint32(line)), _msg(msg + ": " + val) + : _filename(filename), _line(to_uint32(line)), _msg(msg + ": " + val) { construct_full_msg(); } @@ -69,7 +69,7 @@ private: void construct_full_msg() { _full_msg = - "Internal Exception. " + _msg + " [" + _filename + ":" + std::to_string(_line) + "]"; + "Internal Exception. " + _msg + " [" + _filename + ":" + std::to_string(_line) + "]"; } std::string _full_msg; diff --git a/compiler/oops/include/oops/UserExn.h b/compiler/oops/include/oops/UserExn.h index d0138322d..84a6b81eb 100644 --- a/compiler/oops/include/oops/UserExn.h +++ b/compiler/oops/include/oops/UserExn.h @@ -72,7 +72,9 @@ private: out << pepper::str(attr, " = ", val); } - void build_info(std::stringstream &) { /* empty */} + void build_info(std::stringstream &) + { /* empty */ + } // when only one info of string is provided void build_info(std::stringstream &out, const std::string &val) { out << val; } diff --git a/compiler/oops/test.cpp b/compiler/oops/src/oops.test.cpp index 666f62f54..666f62f54 100644 --- a/compiler/oops/test.cpp +++ b/compiler/oops/src/oops.test.cpp diff --git a/compiler/pepper-str/CMakeLists.txt b/compiler/pepper-str/CMakeLists.txt index cbe01b86a..481073af7 100644 --- a/compiler/pepper-str/CMakeLists.txt +++ b/compiler/pepper-str/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(pepper_str INTERFACE) target_include_directories(pepper_str INTERFACE include) +target_link_libraries(pepper_str INTERFACE nncc_coverage) if(NOT ENABLE_TEST) return() @@ -8,5 +9,5 @@ endif(NOT ENABLE_TEST) # Google Test is mandatory for test nnas_find_package(GTest REQUIRED) -GTest_AddTest(pepper_str_test test.cpp) +GTest_AddTest(pepper_str_test src/pepper-str.test.cpp) target_link_libraries(pepper_str_test pepper_str) diff --git a/compiler/pepper-str/include/pepper/str.h b/compiler/pepper-str/include/pepper/str.h index efbc3a9c8..0c74aa85a 100644 --- a/compiler/pepper-str/include/pepper/str.h +++ b/compiler/pepper-str/include/pepper/str.h @@ -47,7 +47,7 @@ inline void str_impl(std::ostream &os, Arg &&arg, Args &&... args) str_impl(os, std::forward<Args>(args)...); } -} // namesapce details +} // namespace details } // namespace pepper namespace pepper diff --git a/compiler/pepper-str/test.cpp b/compiler/pepper-str/src/pepper-str.test.cpp index 222c371c8..222c371c8 100644 --- a/compiler/pepper-str/test.cpp +++ b/compiler/pepper-str/src/pepper-str.test.cpp diff --git a/compiler/plier-tf/src/TestHelper.cpp b/compiler/plier-tf/src/TestHelper.cpp index a551e89f9..c1565b5cc 100644 --- a/compiler/plier-tf/src/TestHelper.cpp +++ b/compiler/plier-tf/src/TestHelper.cpp @@ -40,7 +40,7 @@ struct membuf : std::streambuf struct imemstream : virtual membuf, std::istream { imemstream(char const *base, size_t size) - : membuf(base, size), std::istream(static_cast<std::streambuf *>(this)) + : membuf(base, size), std::istream(static_cast<std::streambuf *>(this)) { } }; diff --git a/compiler/pota-quantization-value-test/compare_tensors.py b/compiler/pota-quantization-value-test/compare_tensors.py index 9c9b639bd..20e92c68b 100755 --- a/compiler/pota-quantization-value-test/compare_tensors.py +++ b/compiler/pota-quantization-value-test/compare_tensors.py @@ -68,7 +68,7 @@ def compare_quantization(tensor, tensor_name, expect_dir): for key in json_load: if key == "weights": expected_weights = np.array(json_load["weights"]) - input_weights = tensor["weights"][:] + input_weights = tensor["weights"][()] abs_tolerance = 1 # We use higher tolerance for int64 data (bias of int16-quantized model) if tensor["weights"].dtype == 'int64': diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ifm.json new file mode 100644 index 000000000..2fb0c68d8 --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ifm.json @@ -0,0 +1,4 @@ +{ + "scale": 0.00014983004075475037, + "zero_point": 0.0 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ofm1.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ofm1.json new file mode 100644 index 000000000..239a3a46d --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ofm1.json @@ -0,0 +1,4 @@ +{ + "scale": 0.00014586378529202193, + "zero_point": 0.0 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ofm2.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ofm2.json new file mode 100644 index 000000000..b4422f49e --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/ofm2.json @@ -0,0 +1,4 @@ +{ + "scale": 0.00014956798986531794, + "zero_point": 0.0 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/split_dim.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/split_dim.json new file mode 100644 index 000000000..ac7cde187 --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/quantization/split_dim.json @@ -0,0 +1,5 @@ +{ + "weights": [ + 0 + ] +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ifm.json new file mode 100644 index 000000000..5e333acde --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ifm.json @@ -0,0 +1,4 @@ +{ + "min": -4.909480743408203, + "max": 4.779518718719482 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ofm1.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ofm1.json new file mode 100644 index 000000000..1d23f8d9a --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ofm1.json @@ -0,0 +1,4 @@ +{ + "min": -4.073143873214722, + "max": 4.779518718719482 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ofm2.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ofm2.json new file mode 100644 index 000000000..ffd7d841d --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/int16/record_minmax/ofm2.json @@ -0,0 +1,4 @@ +{ + "min": -4.9008944129943846, + "max": 4.620573101043701 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ifm.json new file mode 100644 index 000000000..aaba6131c --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ifm.json @@ -0,0 +1,4 @@ +{ + "scale": 0.038689617067575455, + "zero_point": 128.0 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ofm1.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ofm1.json new file mode 100644 index 000000000..3c0134839 --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ofm1.json @@ -0,0 +1,4 @@ +{ + "scale": 0.035256847739219666, + "zero_point": 123.0 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ofm2.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ofm2.json new file mode 100644 index 000000000..20ebde60e --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/ofm2.json @@ -0,0 +1,4 @@ +{ + "scale": 0.0385618582367897, + "zero_point": 129.0 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/split_dim.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/split_dim.json new file mode 100644 index 000000000..ac7cde187 --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/quantization/split_dim.json @@ -0,0 +1,5 @@ +{ + "weights": [ + 0 + ] +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ifm.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ifm.json new file mode 100644 index 000000000..c6dd19469 --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ifm.json @@ -0,0 +1,4 @@ +{ + "min": -4.959668273925781, + "max": 4.906183891296386 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ofm1.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ofm1.json new file mode 100644 index 000000000..4f890dddb --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ofm1.json @@ -0,0 +1,4 @@ +{ + "min": -4.3535110282897955, + "max": 4.636985759735107 +} diff --git a/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ofm2.json b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ofm2.json new file mode 100644 index 000000000..78f9a648f --- /dev/null +++ b/compiler/pota-quantization-value-test/expected_outputs/Split_000/channel/uint8/record_minmax/ofm2.json @@ -0,0 +1,4 @@ +{ + "min": -4.959668273925781, + "max": 4.8736056804656975 +} diff --git a/compiler/pota-quantization-value-test/test.lst b/compiler/pota-quantization-value-test/test.lst index dd1640428..4beec8c0e 100644 --- a/compiler/pota-quantization-value-test/test.lst +++ b/compiler/pota-quantization-value-test/test.lst @@ -26,6 +26,8 @@ addTest(PRelu_001 channel uint8) addTest(PRelu_001 channel int16) addTest(ReLU_000 layer uint8) addTest(ReLU_000 channel int16) +addTest(Split_000 channel uint8) +addTest(Split_000 channel int16) addTest(TransposeConv_001 channel uint8) addTest(TransposeConv_001 channel int16) addTest(TransposeConv_001 layer uint8) diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/0.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/0.txt new file mode 100644 index 000000000..4b999a028 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/0.txt @@ -0,0 +1 @@ + 3.241328 , 2.7033713 ,-2.5329788 ,-4.078369 ,-3.6711028 , 2.8912613 , 0.6188993 , 3.3729403 , 2.9906578 , 0.69040877, 0.6443222 , 1.1676162 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/1.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/1.txt new file mode 100644 index 000000000..7061063b9 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/1.txt @@ -0,0 +1 @@ + 1.572614 , 3.6147017 , 1.4378501 ,-0.81497866, 1.5987366 , 3.7698908 ,-3.8637109 , 4.5728784 ,-0.8706349 , 0.7389268 , 4.64117 ,-0.96047217 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/2.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/2.txt new file mode 100644 index 000000000..c048a8a9f --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/2.txt @@ -0,0 +1 @@ + 0.00864919,-3.1653113 ,-2.125551 , 2.9225516 ,-1.1439148 , 4.6509814 ,-2.097259 , 2.5843353 ,-2.067207 ,-2.5034845 ,-4.9441104 ,-3.9062042 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/3.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/3.txt new file mode 100644 index 000000000..55be3b464 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/3.txt @@ -0,0 +1 @@ + 1.0920542 , 0.5510192 , 1.3465579 ,-2.3510268 , 4.016736 , 4.7848744 ,-0.42403316, 0.00571597, 1.6412207 , 1.7787368 , 2.4728034 ,-3.5900247 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/4.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/4.txt new file mode 100644 index 000000000..04c7a1a8a --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/int16/4.txt @@ -0,0 +1 @@ +-2.9799085,-3.9477375, 0.6402844, 3.304766 , 3.8880465,-3.5069442,-2.3702915, 4.126247 ,-3.1614416, 2.9909244,-2.8755414, 0.2627986 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/0.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/0.txt new file mode 100644 index 000000000..0e8d687b1 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/0.txt @@ -0,0 +1 @@ +-2.327701 , 1.9312059 ,-2.0069487 ,-1.2584914 ,-0.08435626, 0.47685367,-2.7456024 , 2.1275337 ,-4.9685698 , 1.8143541 , 0.52829266,-2.770121 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/1.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/1.txt new file mode 100644 index 000000000..67732e8f5 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/1.txt @@ -0,0 +1 @@ + 0.01133719,-3.3741624 , 3.556686 ,-4.21059 , 0.49977505, 1.768375 , 3.867543 , 2.270572 ,-3.9507272 ,-4.595618 ,-4.7460327 , 0.5856542 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/2.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/2.txt new file mode 100644 index 000000000..7bc7124d6 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/2.txt @@ -0,0 +1 @@ +-2.7181 , 4.6819983 , 2.9022477 ,-0.10716935, 3.6687856 ,-2.5403244 ,-4.477037 , 2.5499978 ,-3.9294813 , 0.08725335,-2.243345 ,-1.4018577 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/3.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/3.txt new file mode 100644 index 000000000..0fac9fb70 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/3.txt @@ -0,0 +1 @@ +-3.920553 , 0.87464577,-1.0319884 , 2.1885726 , 2.755115 ,-1.6436632 ,-4.4507327 , 4.915525 , 2.9331517 , 4.7712016 , 4.676084 ,-1.7715888 diff --git a/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/4.txt b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/4.txt new file mode 100644 index 000000000..df79104c2 --- /dev/null +++ b/compiler/pota-quantization-value-test/test_inputs/Split_000/channel/uint8/4.txt @@ -0,0 +1 @@ +-2.181168 ,-1.6011912 ,-4.359466 ,-1.3662407 ,-0.06876431,-2.9213328 ,-0.5463467 ,-3.7916536 ,-3.751455 ,-2.822578 , 0.8914152 ,-3.0267959 diff --git a/compiler/pp/CMakeLists.txt b/compiler/pp/CMakeLists.txt index 2c25c6406..6d58458ca 100644 --- a/compiler/pp/CMakeLists.txt +++ b/compiler/pp/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(pp STATIC ${SOURCES}) set_target_properties(pp PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(pp PUBLIC include) target_link_libraries(pp PRIVATE nncc_common) +target_link_libraries(pp PUBLIC nncc_coverage) if(NOT ENABLE_TEST) return() diff --git a/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py b/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py index bdf86fe29..d57289abf 100755 --- a/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py +++ b/compiler/record-minmax-conversion-test/gen_h5_random_inputs.py @@ -39,9 +39,16 @@ for i in range(num_data): for j in range(len(input_details)): input_detail = input_details[j] - # Generate random input [-5, 5) - input_data = np.array(10 * np.random.random_sample(input_detail["shape"]) - 5, - input_detail["dtype"]) + print(input_detail["dtype"]) + if input_detail["dtype"] == np.bool_: + # Generate random bool [0, 1] + input_data = np.array( + np.random.random_integers(0, 1, input_detail["shape"]), + input_detail["dtype"]) + elif input_detail["dtype"] == np.float32: + # Generate random input [-5, 5) + input_data = np.array(10 * np.random.random_sample(input_detail["shape"]) - 5, + input_detail["dtype"]) sample.create_dataset(str(j), data=input_data) h5_file.close() diff --git a/compiler/record-minmax-conversion-test/testall.sh b/compiler/record-minmax-conversion-test/testall.sh index 29c9ed3d1..d7fc1de53 100755 --- a/compiler/record-minmax-conversion-test/testall.sh +++ b/compiler/record-minmax-conversion-test/testall.sh @@ -55,6 +55,16 @@ for TESTCASE in "$@"; do --input_data "${BIN_PATH}/${TESTCASE}.tflite.input.h5" \ --output_model "${BIN_PATH}/${TESTCASE}.out.circle" + if [[ $? -ne 0 ]]; then + echo "FAILED TO GENERATE CIRCLE OUTPUT" + continue + fi + + # Run record-minmax with auto generated random input + "${RECORD_MINMAX_PATH}" \ + --input_model "${TESTCASE_FILE}.circle" \ + --output_model "${BIN_PATH}/${TESTCASE}.outr.circle" + if [[ $? -eq 0 ]]; then touch "${PASSED_TAG}" fi diff --git a/compiler/record-minmax/CMakeLists.txt b/compiler/record-minmax/CMakeLists.txt index f8a165bd3..da63bbf5f 100644 --- a/compiler/record-minmax/CMakeLists.txt +++ b/compiler/record-minmax/CMakeLists.txt @@ -17,9 +17,11 @@ target_link_libraries(record-minmax ${HDF5_CXX_LIBRARIES}) target_link_libraries(record-minmax arser) target_link_libraries(record-minmax safemain) target_link_libraries(record-minmax luci_import) +target_link_libraries(record-minmax luci_env) target_link_libraries(record-minmax luci_export) target_link_libraries(record-minmax luci_interpreter) target_link_libraries(record-minmax vconone) +target_link_libraries(record-minmax nncc_coverage) install(TARGETS record-minmax DESTINATION bin) @@ -27,6 +29,9 @@ if(NOT ENABLE_TEST) return() endif(NOT ENABLE_TEST) +file(GLOB_RECURSE TESTS "tests/*.test.cpp") + nnas_find_package(GTest REQUIRED) -GTest_AddTest(record_minmax_function_test "${CMAKE_CURRENT_SOURCE_DIR}/tests/RecordFunction.test.cpp") +GTest_AddTest(record_minmax_function_test "${TESTS}") target_include_directories(record_minmax_function_test PRIVATE include) +target_link_libraries(record_minmax_function_test nncc_coverage) diff --git a/compiler/record-minmax/driver/Driver.cpp b/compiler/record-minmax/driver/Driver.cpp index 8b09498c3..6dbb693b2 100644 --- a/compiler/record-minmax/driver/Driver.cpp +++ b/compiler/record-minmax/driver/Driver.cpp @@ -19,6 +19,8 @@ #include <arser/arser.h> #include <vconone/vconone.h> +#include <luci/UserSettings.h> + void print_version(void) { std::cout << "record-minmax version " << vconone::get_string() << std::endl; @@ -30,47 +32,55 @@ int entry(const int argc, char **argv) using namespace record_minmax; arser::Arser arser( - "Embedding min/max values of activations to the circle model for post-training quantization"); + "Embedding min/max values of activations to the circle model for post-training quantization"); arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); + .nargs(0) + .required(false) + .default_value(false) + .help("Show version information and exit") + .exit_with(print_version); arser.add_argument("--input_model") - .nargs(1) - .type(arser::DataType::STR) - .required(true) - .help("Input model filepath"); + .nargs(1) + .type(arser::DataType::STR) + .required(true) + .help("Input model filepath"); arser.add_argument("--input_data") - .nargs(1) - .type(arser::DataType::STR) - .required(true) - .help("Input data filepath"); + .nargs(1) + .type(arser::DataType::STR) + .required(false) + .help("Input data filepath. If not given, record-minmax will run with randomly generated data. " + "Note that the random dataset does not represent inference workload, leading to poor " + "model accuracy."); arser.add_argument("--output_model") - .nargs(1) - .type(arser::DataType::STR) - .required(true) - .help("Output model filepath"); + .nargs(1) + .type(arser::DataType::STR) + .required(true) + .help("Output model filepath"); arser.add_argument("--min_percentile") - .nargs(1) - .type(arser::DataType::FLOAT) - .help("Record n'th percentile of min"); + .nargs(1) + .type(arser::DataType::FLOAT) + .help("Record n'th percentile of min"); arser.add_argument("--max_percentile") - .nargs(1) - .type(arser::DataType::FLOAT) - .help("Record n'th percentile of max"); + .nargs(1) + .type(arser::DataType::FLOAT) + .help("Record n'th percentile of max"); arser.add_argument("--mode") - .nargs(1) - .type(arser::DataType::STR) - .help("Record mode. percentile (default) or moving_average"); + .nargs(1) + .type(arser::DataType::STR) + .help("Record mode. percentile (default) or moving_average"); + + arser.add_argument("--generate_profile_data") + .nargs(0) + .required(false) + .default_value(false) + .help("This will turn on profiling data generation."); try { @@ -83,8 +93,9 @@ int entry(const int argc, char **argv) return 255; } + auto settings = luci::UserSettings::settings(); + auto input_model_path = arser.get<std::string>("--input_model"); - auto input_data_path = arser.get<std::string>("--input_data"); auto output_model_path = arser.get<std::string>("--output_model"); // Default values @@ -104,13 +115,26 @@ int entry(const int argc, char **argv) if (mode != "percentile" && mode != "moving_average") throw std::runtime_error("Unsupported mode"); + if (arser["--generate_profile_data"]) + settings->set(luci::UserSettings::Key::ProfilingDataGen, true); + RecordMinMax rmm; // Initialize interpreter and observer rmm.initialize(input_model_path); - // Profile min/max while executing the given input data - rmm.profileData(mode, input_data_path, min_percentile, max_percentile); + if (arser["--input_data"]) + { + auto input_data_path = arser.get<std::string>("--input_data"); + + // Profile min/max while executing the given input data + rmm.profileData(mode, input_data_path, min_percentile, max_percentile); + } + else + { + // Profile min/max while executing random input data + rmm.profileDataWithRandomInputs(mode, min_percentile, max_percentile); + } // Save profiled values to the model rmm.saveModel(output_model_path); diff --git a/compiler/record-minmax/include/RecordFunction.h b/compiler/record-minmax/include/RecordFunction.h index b570c6a0a..c34aee0e1 100644 --- a/compiler/record-minmax/include/RecordFunction.h +++ b/compiler/record-minmax/include/RecordFunction.h @@ -53,7 +53,7 @@ float getNthPercentile(std::vector<float> &vector, float percentile) float percent_i = static_cast<float>(index) / static_cast<float>(copy.size() - 1); float fraction = - (percentile / 100.0 - percent_i) / ((index + 1.0) / (copy.size() - 1.0) - percent_i); + (percentile / 100.0 - percent_i) / ((index + 1.0) / (copy.size() - 1.0) - percent_i); float res = copy[index] + fraction * (copy[index + 1] - copy[index]); return res; } diff --git a/compiler/record-minmax/include/RecordMinMax.h b/compiler/record-minmax/include/RecordMinMax.h index ffdb17aec..85ae4cdc7 100644 --- a/compiler/record-minmax/include/RecordMinMax.h +++ b/compiler/record-minmax/include/RecordMinMax.h @@ -39,6 +39,9 @@ public: void profileData(const std::string &mode, const std::string &input_data_path, float min_percentile, float max_percentile); + void profileDataWithRandomInputs(const std::string &mode, float min_percentile, + float max_percentile); + void saveModel(const std::string &output_model_path); private: diff --git a/compiler/record-minmax/requires.cmake b/compiler/record-minmax/requires.cmake index f6804cef1..9cf12591e 100644 --- a/compiler/record-minmax/requires.cmake +++ b/compiler/record-minmax/requires.cmake @@ -1,4 +1,5 @@ require("luci") +require("luci-interpreter") require("safemain") require("arser") require("vconone") diff --git a/compiler/record-minmax/src/HDF5Importer.cpp b/compiler/record-minmax/src/HDF5Importer.cpp index a0e65eeb7..cfb270ce0 100644 --- a/compiler/record-minmax/src/HDF5Importer.cpp +++ b/compiler/record-minmax/src/HDF5Importer.cpp @@ -59,7 +59,30 @@ DataType toInternalDtype(const H5::DataType &h5_type) { return DataType::S64; } - // Only support three datatypes for now + if (h5_type.getClass() == H5T_class_t::H5T_ENUM) + { + // We follow the numpy format + // In numpy 1.19.0, np.bool_ is saved as H5T_ENUM + // - (name, value) -> (FALSE, 0) and (TRUE, 1) + // - value dtype is H5T_STD_I8LE + // TODO Find a general way to recognize BOOL type + char name[10]; + int8_t value[2] = {0, 1}; + if (H5Tenum_nameof(h5_type.getId(), value, name, 10) < 0) + return DataType::Unknown; + + if (std::string(name) != "FALSE") + return DataType::Unknown; + + if (H5Tenum_nameof(h5_type.getId(), value + 1, name, 10) < 0) + return DataType::Unknown; + + if (std::string(name) != "TRUE") + return DataType::Unknown; + + return DataType::BOOL; + } + // TODO Support more datatypes return DataType::Unknown; } @@ -125,6 +148,9 @@ void HDF5Importer::readTensor(int32_t record_idx, int32_t input_idx, DataType *d case DataType::S64: readTensorData(tensor, static_cast<int64_t *>(buffer)); break; + case DataType::BOOL: + readTensorData(tensor, static_cast<uint8_t *>(buffer)); + break; default: throw std::runtime_error{"Unsupported data type for input data (.h5)"}; } diff --git a/compiler/record-minmax/src/MinMaxObserver.cpp b/compiler/record-minmax/src/MinMaxObserver.cpp index c22cb4132..40c9b730d 100644 --- a/compiler/record-minmax/src/MinMaxObserver.cpp +++ b/compiler/record-minmax/src/MinMaxObserver.cpp @@ -18,6 +18,8 @@ #include <luci/IR/CircleOpcode.h> +#include <math.h> + using DataType = luci_interpreter::DataType; namespace record_minmax @@ -51,6 +53,12 @@ void MinMaxObserver::postTensorWrite(const luci::CircleNode *node, return; } + if (node->dtype() == DataType::BOOL) + { + // Bool type tensor is not quantized + return; + } + // Only support recording of float32 values if (tensor->element_type() != DataType::FLOAT32) throw std::runtime_error("Tensor's data type is not float"); @@ -59,9 +67,27 @@ void MinMaxObserver::postTensorWrite(const luci::CircleNode *node, const auto num_elements = tensor->shape().num_elements(); std::vector<float> buf(data, data + num_elements); - auto minmax = std::minmax_element(buf.begin(), buf.end()); - float min = *minmax.first; - float max = *minmax.second; + + float max = std::numeric_limits<float>::lowest(); + float min = std::numeric_limits<float>::max(); + + bool all_nan = true; + for (auto number : buf) + { + if (isnan(number)) + continue; + + all_nan = false; + + if (number > max) + max = number; + + if (number < min) + min = number; + } + + if (all_nan) + throw std::runtime_error("All values are NaN(Not a Number)"); _minmax_data.recordMinMax(node, min, max); } diff --git a/compiler/record-minmax/src/RecordMinMax.cpp b/compiler/record-minmax/src/RecordMinMax.cpp index cd5f29352..333ff5e3b 100644 --- a/compiler/record-minmax/src/RecordMinMax.cpp +++ b/compiler/record-minmax/src/RecordMinMax.cpp @@ -30,6 +30,7 @@ #include <numeric> #include <stdexcept> #include <iostream> +#include <random> using Shape = luci_interpreter::Shape; using DataType = luci_interpreter::DataType; @@ -37,6 +38,18 @@ using DataType = luci_interpreter::DataType; namespace { +std::vector<uint8_t> genRandomBoolData(std::mt19937 &gen, uint32_t num_elements) +{ + std::uniform_int_distribution<> dist(0, 1); + std::vector<uint8_t> input_data(num_elements); + + // Write random data + for (auto &iter : input_data) + iter = static_cast<uint8_t>(dist(gen)); + + return input_data; +} + /** * @brief getTensorSize will return size in bytes */ @@ -68,6 +81,38 @@ void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, } } +void update_quantparam(record_minmax::MinMaxObserver *observer, const std::string &mode, + float min_percentile, float max_percentile) +{ + auto minmax_map = observer->minMaxData()->getMap(); + for (auto iter = minmax_map->begin(); iter != minmax_map->end(); ++iter) + { + auto node = iter->first; + auto minmax = iter->second; + + float min{0.0f}, max{0.0f}; + if (mode == "percentile") + { + min = record_minmax::getNthPercentile(minmax.min_vector, min_percentile); + max = record_minmax::getNthPercentile(minmax.max_vector, max_percentile); + } + else if (mode == "moving_average") + { + min = record_minmax::getMovingAverage(minmax.min_vector, 0.9, 16, true); + max = record_minmax::getMovingAverage(minmax.max_vector, 0.9, 16, false); + } + assert(mode == "percentile" || mode == "moving_average"); + auto quantparam = std::make_unique<luci::CircleQuantParam>(); + quantparam->min.push_back(min); + quantparam->max.push_back(max); + + assert(node->quantparam() == nullptr); + + auto mutable_node = const_cast<luci::CircleNode *>(node); + mutable_node->quantparam(std::move(quantparam)); + } +} + } // namespace namespace record_minmax @@ -169,33 +214,75 @@ void RecordMinMax::profileData(const std::string &mode, const std::string &input throw std::runtime_error("HDF5 error occurred."); } - auto minmax_map = _observer->minMaxData()->getMap(); - for (auto iter = minmax_map->begin(); iter != minmax_map->end(); ++iter) + update_quantparam(_observer.get(), mode, min_percentile, max_percentile); +} + +void RecordMinMax::profileDataWithRandomInputs(const std::string &mode, float min_percentile, + float max_percentile) +{ + // We use three randomly-generated records + const uint32_t num_records = 3; + + const auto input_nodes = loco::input_nodes(_module->graph()); + const auto num_inputs = input_nodes.size(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dist(-5, 5); + + for (int32_t record_idx = 0; record_idx < num_records; record_idx++) { - auto node = iter->first; - auto minmax = iter->second; + std::cout << "Recording " << record_idx << "'th data" << std::endl; - float min{0.0f}, max{0.0f}; - if (mode == "percentile") + for (int32_t input_idx = 0; input_idx < num_inputs; input_idx++) { - min = getNthPercentile(minmax.min_vector, min_percentile); - max = getNthPercentile(minmax.max_vector, max_percentile); - } - else if (mode == "moving_average") - { - min = getMovingAverage(minmax.min_vector, 0.9, 16, true); - max = getMovingAverage(minmax.max_vector, 0.9, 16, false); - } - assert(mode == "percentile" || mode == "moving_average"); - auto quantparam = std::make_unique<luci::CircleQuantParam>(); - quantparam->min.push_back(min); - quantparam->max.push_back(max); + const auto *input_node = loco::must_cast<const luci::CircleInput *>(input_nodes[input_idx]); + assert(input_node->index() == input_idx); + uint32_t num_elements = 1; + for (uint32_t i = 0; i < input_node->rank(); i++) + { + if (!input_node->dim(i).known()) + throw std::runtime_error("Input dimension must be known"); - assert(node->quantparam() == nullptr); + num_elements *= input_node->dim(i).value(); + } - auto mutable_node = const_cast<luci::CircleNode *>(node); - mutable_node->quantparam(std::move(quantparam)); + if (num_elements == 0) + throw std::runtime_error("Only support non-zero sized inputs"); + + // TODO Support more input data types + assert(input_node->dtype() == loco::DataType::FLOAT32 || + input_node->dtype() == loco::DataType::BOOL); + + if (input_node->dtype() == DataType::FLOAT32) + // clang-format off + { + std::vector<float> input_data(num_elements); + + // Write random data + for (auto &iter : input_data) + iter = static_cast<float>(dist(gen)); + + // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs) + // We can redcue the copy by directly writing data from file to interpreter inputs + _interpreter->writeInputTensor(input_node, input_data.data(), + input_data.size() * sizeof(float)); + } + // clang-format on + else if (input_node->dtype() == DataType::BOOL) + { + auto input_data = genRandomBoolData(gen, num_elements); + _interpreter->writeInputTensor(input_node, input_data.data(), + input_data.size() * sizeof(uint8_t)); + } + } + + _interpreter->interpret(); } + + std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; + + update_quantparam(_observer.get(), mode, min_percentile, max_percentile); } void RecordMinMax::saveModel(const std::string &output_model_path) diff --git a/compiler/record-minmax/tests/RecordFunction.test.cpp b/compiler/record-minmax/tests/RecordFunction.test.cpp index e2f135a4e..0d8632254 100644 --- a/compiler/record-minmax/tests/RecordFunction.test.cpp +++ b/compiler/record-minmax/tests/RecordFunction.test.cpp @@ -115,4 +115,12 @@ TEST(GetNthPercentileTest, EmptyVector_NEG) SUCCEED(); } +TEST(GetMovingAverageTest, Simple) +{ + std::vector<float> input{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + EXPECT_NE(0, getMovingAverage(input, 0.5, 4, true)); + EXPECT_NE(0, getMovingAverage(input, 0.5, 4, false)); +} + } // namespace record_minmax diff --git a/compiler/souschef/include/souschef/Data/Gaussian.h b/compiler/souschef/include/souschef/Data/Gaussian.h index 75570e0b8..8093b4c41 100644 --- a/compiler/souschef/include/souschef/Data/Gaussian.h +++ b/compiler/souschef/include/souschef/Data/Gaussian.h @@ -57,6 +57,22 @@ private: float _stddev; }; +class GaussianInt16DataChef final : public DataChef +{ +public: + GaussianInt16DataChef(float mean, float stddev) : _mean{mean}, _stddev{stddev} + { + // DO NOTHING + } + +public: + std::vector<uint8_t> generate(int32_t count) const override; + +private: + float _mean; + float _stddev; +}; + class GaussianUint8DataChef final : public DataChef { public: @@ -83,6 +99,11 @@ struct GaussianInt32DataChefFactory : public DataChefFactory std::unique_ptr<DataChef> create(const Arguments &args) const; }; +struct GaussianInt16DataChefFactory : public DataChefFactory +{ + std::unique_ptr<DataChef> create(const Arguments &args) const; +}; + struct GaussianUint8DataChefFactory : public DataChefFactory { std::unique_ptr<DataChef> create(const Arguments &args) const; diff --git a/compiler/souschef/include/souschef/DataChef.def b/compiler/souschef/include/souschef/DataChef.def index 28901db18..d724d0390 100644 --- a/compiler/souschef/include/souschef/DataChef.def +++ b/compiler/souschef/include/souschef/DataChef.def @@ -7,13 +7,16 @@ DATA_CHEF(FLOAT32, constant, ConstantDataChefFactory<float>) DATA_CHEF(BOOL, constant, ConstantDataChefFactory<bool>) DATA_CHEF(UINT8, constant, ConstantDataChefFactory<uint8_t>) +DATA_CHEF(INT16, constant, ConstantDataChefFactory<int16_t>) DATA_CHEF(INT32, constant, ConstantDataChefFactory<int32_t>) DATA_CHEF(INT64, constant, ConstantDataChefFactory<int64_t>) DATA_CHEF(INT64, explicit, ExplicitDataChefFactory<int64_t>) DATA_CHEF(INT32, explicit, ExplicitDataChefFactory<int32_t>) +DATA_CHEF(INT16, explicit, ExplicitDataChefFactory<int16_t>) DATA_CHEF(UINT8, explicit, ExplicitDataChefFactory<uint8_t>) DATA_CHEF(BOOL, explicit, ExplicitDataChefFactory<bool>) DATA_CHEF(FLOAT32, explicit, ExplicitDataChefFactory<float>) DATA_CHEF(FLOAT32, gaussian, GaussianFloat32DataChefFactory) DATA_CHEF(INT32, gaussian, GaussianInt32DataChefFactory) +DATA_CHEF(INT16, gaussian, GaussianInt16DataChefFactory) DATA_CHEF(UINT8, gaussian, GaussianUint8DataChefFactory) diff --git a/compiler/souschef/src/Gaussian.cpp b/compiler/souschef/src/Gaussian.cpp index 4a5083d8e..32cbcff4d 100644 --- a/compiler/souschef/src/Gaussian.cpp +++ b/compiler/souschef/src/Gaussian.cpp @@ -26,22 +26,25 @@ namespace souschef { -std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const +template <typename T> +static std::vector<uint8_t> generate_gaussian(int32_t count, float mean, float stddev, + std::minstd_rand::result_type seed) { - // TODO Support seed value override - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); - std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)}; - std::normal_distribution<float> dist{_mean, _stddev}; + std::normal_distribution<float> dist{mean, stddev}; std::vector<uint8_t> res; + constexpr float max_cap = std::numeric_limits<T>::max(); + constexpr float min_cap = std::numeric_limits<T>::min(); for (uint32_t n = 0; n < count; ++n) { - auto const value = dist(rand); + float raw_value = dist(rand); + const float capped_value = std::max(min_cap, std::min(max_cap, raw_value)); + auto const value = static_cast<T>(capped_value); auto const arr = reinterpret_cast<const uint8_t *>(&value); - for (uint32_t b = 0; b < sizeof(float); ++b) + for (uint32_t b = 0; b < sizeof(T); ++b) { res.emplace_back(arr[b]); } @@ -50,52 +53,35 @@ std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const return res; } -std::vector<uint8_t> GaussianInt32DataChef::generate(int32_t count) const +template <typename T> +static std::vector<uint8_t> generate_gaussian(int32_t count, float mean, float stddev) { - // TODO Support seed value override - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + auto time_stamp = std::chrono::system_clock::now().time_since_epoch().count(); - std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)}; - std::normal_distribution<float> dist{_mean, _stddev}; + // Note this is implementation defined, change if needed. + auto seed = static_cast<std::minstd_rand::result_type>(time_stamp); - std::vector<uint8_t> res; + return generate_gaussian<T>(count, mean, stddev, seed); +} - for (uint32_t n = 0; n < count; ++n) - { - auto const value = static_cast<int32_t>(dist(rand)); - auto const arr = reinterpret_cast<const uint8_t *>(&value); +std::vector<uint8_t> GaussianFloat32DataChef::generate(int32_t count) const +{ + return generate_gaussian<float>(count, _mean, _stddev); +} - for (uint32_t b = 0; b < sizeof(int32_t); ++b) - { - res.emplace_back(arr[b]); - } - } +std::vector<uint8_t> GaussianInt32DataChef::generate(int32_t count) const +{ + return generate_gaussian<int32_t>(count, _mean, _stddev); +} - return res; +std::vector<uint8_t> GaussianInt16DataChef::generate(int32_t count) const +{ + return generate_gaussian<int16_t>(count, _mean, _stddev); } std::vector<uint8_t> GaussianUint8DataChef::generate(int32_t count) const { - // TODO Support seed value override - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); - - std::minstd_rand rand{static_cast<std::minstd_rand::result_type>(seed)}; - std::normal_distribution<float> dist{_mean, _stddev}; - - std::vector<uint8_t> res; - - for (uint32_t n = 0; n < count; ++n) - { - auto const value = static_cast<uint8_t>(dist(rand)); // uint8_t for data type - auto const arr = reinterpret_cast<const uint8_t *>(&value); // uint8_t for byte streaming - - for (uint32_t b = 0; b < sizeof(uint8_t); ++b) - { - res.emplace_back(arr[b]); - } - } - - return res; + return generate_gaussian<uint8_t>(count, _mean, _stddev); } std::unique_ptr<DataChef> GaussianFloat32DataChefFactory::create(const Arguments &args) const @@ -124,6 +110,19 @@ std::unique_ptr<DataChef> GaussianInt32DataChefFactory::create(const Arguments & return std::unique_ptr<DataChef>{new GaussianInt32DataChef{mean, stddev}}; } +std::unique_ptr<DataChef> GaussianInt16DataChefFactory::create(const Arguments &args) const +{ + if (args.count() != 2) + { + throw std::runtime_error{"invalid argument count: two arguments (mean/stddev) are expected"}; + } + + auto const mean = to_number<float>(args.value(0)); + auto const stddev = to_number<float>(args.value(1)); + + return std::unique_ptr<DataChef>{new GaussianInt16DataChef{mean, stddev}}; +} + std::unique_ptr<DataChef> GaussianUint8DataChefFactory::create(const Arguments &args) const { if (args.count() != 2) diff --git a/compiler/souschef/src/LexicalCast.cpp b/compiler/souschef/src/LexicalCast.cpp index 8e3d4cbbb..1af6e30f9 100644 --- a/compiler/souschef/src/LexicalCast.cpp +++ b/compiler/souschef/src/LexicalCast.cpp @@ -18,12 +18,25 @@ #include <cassert> #include <limits> +#include <stdexcept> namespace souschef { template <> float to_number(const std::string &s) { return std::stof(s); } template <> int to_number(const std::string &s) { return std::stoi(s); } +template <> int16_t to_number(const std::string &s) +{ + // There are no standard function to parse int16_t or short int + // This function simulates behavior similar stoi, stol and stoll + int res = std::stol(s); + // standard does not specify string in error message, this is arbitrary + if (res < std::numeric_limits<int16_t>::min() || res > std::numeric_limits<int16_t>::max()) + { + throw std::out_of_range("to_number<int16_t>"); + } + return res; +} template <> int64_t to_number(const std::string &s) { return std::stoll(s); } template <> uint8_t to_number(const std::string &s) { diff --git a/compiler/stdex/CMakeLists.txt b/compiler/stdex/CMakeLists.txt deleted file mode 100644 index 91f07e69f..000000000 --- a/compiler/stdex/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -file(GLOB_RECURSE TESTS "src/*.test.cpp") - -add_library(stdex INTERFACE) -target_include_directories(stdex INTERFACE include) - -if(NOT ENABLE_TEST) - return() -endif(NOT ENABLE_TEST) - -# Google Test is mandatory for test -nnas_find_package(GTest REQUIRED) - -add_executable(stdex_test ${TESTS}) -target_link_libraries(stdex_test stdex) -target_link_libraries(stdex_test gtest_main) -add_test(stdex_test stdex_test) diff --git a/compiler/stdex/README.md b/compiler/stdex/README.md deleted file mode 100644 index 054d08569..000000000 --- a/compiler/stdex/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# stdex - -`stdex` is an extension over standard C++ libraries. - -# How to use - -Please read each header files. - -One example of `stdex::make_unique(..)` in `compiler/stdex/Memory.h` is as follows: - -```cpp -#include <stdex/Memory.h> - -using stdex::make_unique; - -class A { ... }; - -... - -std::unique_ptr<A> a = make_unique<A>(); // Note: std::make_unique is not supported in C++ 11 - -``` diff --git a/compiler/stdex/include/stdex/Set.h b/compiler/stdex/include/stdex/Set.h deleted file mode 100644 index 2c61e0d01..000000000 --- a/compiler/stdex/include/stdex/Set.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018 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 __STDEX_SET_H__ -#define __STDEX_SET_H__ - -#include <set> - -template <typename T> bool operator==(const std::set<T> &lhs, const std::set<T> &rhs) -{ - if (rhs.size() != lhs.size()) - { - return false; - } - - for (const auto &element : lhs) - { - if (rhs.find(element) == rhs.end()) - { - return false; - } - } - - return true; -} - -template <typename T> std::set<T> operator-(const std::set<T> &lhs, const std::set<T> &rhs) -{ - std::set<T> res; - - for (const auto &element : lhs) - { - if (rhs.find(element) == rhs.end()) - { - res.insert(element); - } - } - - return res; -} - -#endif // __STDEX_SET_H__ diff --git a/compiler/stdex/src/Memory.test.cpp b/compiler/stdex/src/Memory.test.cpp deleted file mode 100644 index 433af4534..000000000 --- a/compiler/stdex/src/Memory.test.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018 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 "stdex/Memory.h" - -#include <gtest/gtest.h> - -namespace -{ - -struct Stat -{ - unsigned allocated = 0; - unsigned freed = 0; -}; - -struct Counter -{ -public: - Counter(Stat *stat) : _stat{stat} { _stat->allocated += 1; } - -public: - ~Counter() { _stat->freed += 1; } - -private: - Stat *_stat; -}; - -} // namespace - -TEST(MemoryTest, make_unique) -{ - Stat stat; - - ASSERT_EQ(stat.allocated, 0); - ASSERT_EQ(stat.freed, 0); - - auto o = stdex::make_unique<::Counter>(&stat); - - ASSERT_EQ(stat.allocated, 1); - ASSERT_EQ(stat.freed, 0); - - o.reset(); - - ASSERT_EQ(stat.allocated, 1); - ASSERT_EQ(stat.freed, 1); -} diff --git a/compiler/tf2circle/CMakeLists.txt b/compiler/tf2circle/CMakeLists.txt index 549f731a4..8678e90b4 100644 --- a/compiler/tf2circle/CMakeLists.txt +++ b/compiler/tf2circle/CMakeLists.txt @@ -40,7 +40,6 @@ target_link_libraries(tf2circle PRIVATE tfinfo) target_link_libraries(tf2circle PRIVATE exo) target_link_libraries(tf2circle PRIVATE locop) target_link_libraries(tf2circle PRIVATE hermes_std) -target_link_libraries(tf2circle PRIVATE stdex) target_link_libraries(tf2circle PRIVATE angkor cwrap) target_link_libraries(tf2circle PRIVATE tf2circle_customop_info_proto) diff --git a/compiler/tf2circle/requires.cmake b/compiler/tf2circle/requires.cmake index 68d45bf3a..87ea50bf7 100644 --- a/compiler/tf2circle/requires.cmake +++ b/compiler/tf2circle/requires.cmake @@ -1,4 +1,3 @@ -require("stdex") require("hermes-std") require("moco-tf") require("exo") diff --git a/compiler/tf2circle/src/tf2circle.cpp b/compiler/tf2circle/src/tf2circle.cpp index a1160e968..b4d21133d 100644 --- a/compiler/tf2circle/src/tf2circle.cpp +++ b/compiler/tf2circle/src/tf2circle.cpp @@ -28,10 +28,8 @@ #include <hermes/ConsoleReporter.h> #include <hermes/EnvConfig.h> -#include <stdex/Memory.h> - #include <cassert> - +#include <memory> #include <iostream> #include <stdexcept> #include <string> @@ -70,8 +68,8 @@ struct LoggingContext if (ctx == nullptr) { ctx = new hermes::Context; - ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>()); - ctx->config(stdex::make_unique<EnvConfig>("TF2CIRCLE_Log")); + ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>()); + ctx->config(std::make_unique<EnvConfig>("TF2CIRCLE_Log")); } return ctx; @@ -133,9 +131,9 @@ int EntryFunctor::operator()(int argc, char **argv) const using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>; // This line allows users to control all the moco-tf loggers via TF2CIRCLE_Log_Frontend - moco::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2CIRCLE_Log_Frontend")); + moco::LoggingContext::get()->config(std::make_unique<EnvConfig>("TF2CIRCLE_Log_Frontend")); // This line allows users to control all the exo-circle loggers via TF2CIRCLE_Log_Backend - exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2CIRCLE_Log_Backend")); + exo::LoggingContext::get()->config(std::make_unique<EnvConfig>("TF2CIRCLE_Log_Backend")); LOGGER(l); diff --git a/compiler/tf2nnpkg/CMakeLists.txt b/compiler/tf2nnpkg/CMakeLists.txt index 8e1edf858..b81f40646 100644 --- a/compiler/tf2nnpkg/CMakeLists.txt +++ b/compiler/tf2nnpkg/CMakeLists.txt @@ -30,6 +30,5 @@ target_link_libraries(tf2nnpkg PRIVATE tfinfo) target_link_libraries(tf2nnpkg PRIVATE exo) target_link_libraries(tf2nnpkg PRIVATE locop) target_link_libraries(tf2nnpkg PRIVATE hermes_std) -target_link_libraries(tf2nnpkg PRIVATE stdex) target_link_libraries(tf2nnpkg PRIVATE angkor cwrap) install(TARGETS tf2nnpkg DESTINATION bin) diff --git a/compiler/tf2nnpkg/requires.cmake b/compiler/tf2nnpkg/requires.cmake index 68d45bf3a..87ea50bf7 100644 --- a/compiler/tf2nnpkg/requires.cmake +++ b/compiler/tf2nnpkg/requires.cmake @@ -1,4 +1,3 @@ -require("stdex") require("hermes-std") require("moco-tf") require("exo") diff --git a/compiler/tf2nnpkg/src/tf2nnpkg.cpp b/compiler/tf2nnpkg/src/tf2nnpkg.cpp index d9a0d9d2f..548cee61f 100644 --- a/compiler/tf2nnpkg/src/tf2nnpkg.cpp +++ b/compiler/tf2nnpkg/src/tf2nnpkg.cpp @@ -28,8 +28,7 @@ #include <hermes/ConsoleReporter.h> #include <hermes/EnvConfig.h> -#include <stdex/Memory.h> - +#include <memory> #include <iostream> #include <fstream> #include <functional> @@ -71,8 +70,8 @@ struct LoggingContext if (ctx == nullptr) { ctx = new hermes::Context; - ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>()); - ctx->config(stdex::make_unique<EnvConfig>("TF2NNPKG_Log")); + ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>()); + ctx->config(std::make_unique<EnvConfig>("TF2NNPKG_Log")); } return ctx; @@ -148,9 +147,9 @@ int EntryFunctor::operator()(int argc, char **argv) const using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>; // This line allows users to control all the moco-tf loggers via TF2NNPKG_Log_Frontend - moco::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2NNPKG_Log_Frontend")); + moco::LoggingContext::get()->config(std::make_unique<EnvConfig>("TF2NNPKG_Log_Frontend")); // This line allows users to control all the exo-circle loggers via TF2NNPKG_Log_Backend - exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2NNPKG_Log_Backend")); + exo::LoggingContext::get()->config(std::make_unique<EnvConfig>("TF2NNPKG_Log_Backend")); LOGGER(l); diff --git a/compiler/tf2tflite/CMakeLists.txt b/compiler/tf2tflite/CMakeLists.txt index 663563e00..e4a723305 100644 --- a/compiler/tf2tflite/CMakeLists.txt +++ b/compiler/tf2tflite/CMakeLists.txt @@ -38,7 +38,6 @@ target_link_libraries(tf2tflite PRIVATE tfinfo) target_link_libraries(tf2tflite PRIVATE exo) target_link_libraries(tf2tflite PRIVATE locop) target_link_libraries(tf2tflite PRIVATE hermes_std) -target_link_libraries(tf2tflite PRIVATE stdex) target_link_libraries(tf2tflite PRIVATE angkor cwrap) target_link_libraries(tf2tflite PRIVATE tf2tflite_customop_info_proto) install(TARGETS tf2tflite DESTINATION bin) diff --git a/compiler/tf2tflite/requires.cmake b/compiler/tf2tflite/requires.cmake index 68d45bf3a..87ea50bf7 100644 --- a/compiler/tf2tflite/requires.cmake +++ b/compiler/tf2tflite/requires.cmake @@ -1,4 +1,3 @@ -require("stdex") require("hermes-std") require("moco-tf") require("exo") diff --git a/compiler/tf2tflite/src/Driver.cpp b/compiler/tf2tflite/src/Driver.cpp index e43d30bb2..12fcbd005 100644 --- a/compiler/tf2tflite/src/Driver.cpp +++ b/compiler/tf2tflite/src/Driver.cpp @@ -28,10 +28,8 @@ #include <hermes/ConsoleReporter.h> #include <hermes/EnvConfig.h> -#include <stdex/Memory.h> - #include <cassert> - +#include <memory> #include <iostream> #include <stdexcept> #include <string> @@ -70,8 +68,8 @@ struct LoggingContext if (ctx == nullptr) { ctx = new hermes::Context; - ctx->sinks()->append(stdex::make_unique<hermes::ConsoleReporter>()); - ctx->config(stdex::make_unique<EnvConfig>("TF2TFLITE_Log")); + ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>()); + ctx->config(std::make_unique<EnvConfig>("TF2TFLITE_Log")); } return ctx; @@ -96,9 +94,9 @@ int main(int argc, char **argv) using EnvConfig = hermes::EnvConfig<hermes::EnvFormat::BooleanNumber>; // This line allows users to control all the moco-tf loggers via TF2TFLITE_Log_Frontend - moco::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2TFLITE_Log_Frontend")); + moco::LoggingContext::get()->config(std::make_unique<EnvConfig>("TF2TFLITE_Log_Frontend")); // This line allows users to control all the exo-tflite loggers via TF2TFLITE_Log_Backend - exo::LoggingContext::get()->config(stdex::make_unique<EnvConfig>("TF2TFLITE_Log_Backend")); + exo::LoggingContext::get()->config(std::make_unique<EnvConfig>("TF2TFLITE_Log_Backend")); LOGGER(l); diff --git a/compiler/tf2tfliteV2/tf2tfliteV2.py b/compiler/tf2tfliteV2/tf2tfliteV2.py index 3fb988102..c6973ff96 100755 --- a/compiler/tf2tfliteV2/tf2tfliteV2.py +++ b/compiler/tf2tfliteV2/tf2tfliteV2.py @@ -180,6 +180,15 @@ def _v2_convert(flags): raise ValueError("--input_arrays must be provided") if not flags.output_arrays: raise ValueError("--output_arrays must be provided") + input_shapes = [] + if flags.input_shapes: + input_shapes = [ + _parse_array(shape, type_fn=int) + for shape in flags.input_shapes.split(":") + ] + if len(input_shapes) != len(_parse_array(flags.input_arrays)): + raise ValueError( + "--input_shapes and --input_arrays must have the same length") file_content = open(flags.input_path, 'rb').read() try: graph_def = tf.compat.v1.GraphDef() @@ -200,6 +209,8 @@ def _v2_convert(flags): _str + ":0" if len(_str.split(":")) == 1 else _str for _str in _parse_array(flags.output_arrays) ]) + for i in range(len(input_shapes)): + wrap_func.inputs[i].set_shape(input_shapes[i]) converter = tf.lite.TFLiteConverter.from_concrete_functions([wrap_func]) if flags.model_format == "saved_model": diff --git a/compiler/tfinfo-v2/CMakeLists.txt b/compiler/tfinfo-v2/CMakeLists.txt index cf438ea29..40df521b9 100644 --- a/compiler/tfinfo-v2/CMakeLists.txt +++ b/compiler/tfinfo-v2/CMakeLists.txt @@ -24,7 +24,6 @@ set_target_properties(tfinfo_v2 PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(tfinfo_v2 PUBLIC include) target_link_libraries(tfinfo_v2 PRIVATE tfinfo_v2_proto) target_link_libraries(tfinfo_v2 PRIVATE oops) -target_link_libraries(tfinfo_v2 PRIVATE stdex) if(NOT ENABLE_TEST) return() diff --git a/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h index f26d0354a..8c014f1fa 100644 --- a/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h +++ b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h @@ -98,7 +98,7 @@ public: } TensorSignature(const Kind kind, const std::string &name, const ShapeHint &shape_hint) - : TensorSignature(kind, name) + : TensorSignature(kind, name) { _shape_hint = shape_hint; } diff --git a/compiler/tfinfo-v2/requires.cmake b/compiler/tfinfo-v2/requires.cmake index e7efab4fb..a1b974421 100644 --- a/compiler/tfinfo-v2/requires.cmake +++ b/compiler/tfinfo-v2/requires.cmake @@ -1,2 +1 @@ require("oops") -require("stdex") diff --git a/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp index 02a2d9199..bcab4ac7f 100644 --- a/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp +++ b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp @@ -54,7 +54,7 @@ const std::vector<std::string> success_cases = name : "relu:0" } ), - // clang-format on + // clang-format on }; } // namespace @@ -221,7 +221,7 @@ const std::vector<std::string> fail_cases = input, a:0, TF_FLOAT, [2, 3 ,4] output, b:0, TF_FLOAT, [2, 3 ,4] )", - // clang-format on + // clang-format on }; } // namespace diff --git a/compiler/tfinfo-v2/src/TensorInfoLoader.cpp b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp index 0bf828773..249bf384a 100644 --- a/compiler/tfinfo-v2/src/TensorInfoLoader.cpp +++ b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp @@ -19,13 +19,13 @@ #include "tfinfo-v2/TensorSignature.h" #include <oops/UserExn.h> -#include <stdex/Memory.h> #include <tfinfo-v2.pb.h> #include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/text_format.h> +#include <memory> #include <fstream> #include <fcntl.h> @@ -107,8 +107,8 @@ void convert(tfinfo_v2_proto::InfoDef &info_def, tfinfo::v2::TensorSignatures &t auto name = input_def.name(); validate_tensor_name(name, path); - auto tensor = stdex::make_unique<tfinfo::v2::TensorSignature>( - tfinfo::v2::TensorSignature::Kind::Input, name); + auto tensor = std::make_unique<tfinfo::v2::TensorSignature>( + tfinfo::v2::TensorSignature::Kind::Input, name); // when there is dim attribute for unknown shape if (input_def.dim_size() > 0) @@ -136,8 +136,8 @@ void convert(tfinfo_v2_proto::InfoDef &info_def, tfinfo::v2::TensorSignatures &t auto name = info_def.output().Get(i).name(); validate_tensor_name(name, path); - auto tensor = stdex::make_unique<tfinfo::v2::TensorSignature>( - tfinfo::v2::TensorSignature::Kind::Output, name); + auto tensor = std::make_unique<tfinfo::v2::TensorSignature>( + tfinfo::v2::TensorSignature::Kind::Output, name); tensors.emplace_back(std::move(tensor)); } } diff --git a/compiler/tfinfo/CMakeLists.txt b/compiler/tfinfo/CMakeLists.txt index 678912e6f..359699e13 100644 --- a/compiler/tfinfo/CMakeLists.txt +++ b/compiler/tfinfo/CMakeLists.txt @@ -5,7 +5,7 @@ list(REMOVE_ITEM SOURCES ${TESTS}) add_library(tfinfo STATIC ${SOURCES}) set_target_properties(tfinfo PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(tfinfo PUBLIC include) -target_link_libraries(tfinfo stdex angkor oops) +target_link_libraries(tfinfo angkor oops) # TODO Remove "nnkit_support_tftestinfo" later add_library(nnkit_support_tftestinfo ALIAS tfinfo) diff --git a/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h b/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h index aec8c5e40..eef206207 100644 --- a/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h +++ b/compiler/tfinfo/include/nnkit/support/tftestinfo/ParsedTensor.h @@ -57,7 +57,7 @@ public: ParsedTensor(const Kind kind, const std::string &name, const DataType &dtype, const std::vector<int32_t> &shape) - : _kind(kind), _dtype(dtype) + : _kind(kind), _dtype(dtype) { _tensor_name.assign(name); @@ -66,7 +66,9 @@ public: _shape.dim(rank) = shape.at(rank); } - ~ParsedTensor() { /* empty */} + ~ParsedTensor() + { /* empty */ + } public: Kind kind() const { return _kind; } diff --git a/compiler/tfinfo/requires.cmake b/compiler/tfinfo/requires.cmake index 3b45c6458..d7ecb2382 100644 --- a/compiler/tfinfo/requires.cmake +++ b/compiler/tfinfo/requires.cmake @@ -1,3 +1,2 @@ -require("stdex") require("angkor") require("oops") diff --git a/compiler/tfinfo/src/TensorInfoParser.cpp b/compiler/tfinfo/src/TensorInfoParser.cpp index 9eb3da296..050da40de 100644 --- a/compiler/tfinfo/src/TensorInfoParser.cpp +++ b/compiler/tfinfo/src/TensorInfoParser.cpp @@ -21,7 +21,6 @@ #include "Compat.h" #include <oops/UserExn.h> -#include <stdex/Memory.h> #include <nncc/core/ADT/tensor/Shape.h> #include <cctype> @@ -197,7 +196,7 @@ std::unique_ptr<ParsedTensor> parse_line(std::string &line) shape.emplace_back(std::stoi(dim)); } - return stdex::make_unique<ParsedTensor>(kind, name, dtype, shape); + return std::make_unique<ParsedTensor>(kind, name, dtype, shape); } #undef CHECK_NOT_NULL diff --git a/compiler/tfkit/CMakeLists.txt b/compiler/tfkit/CMakeLists.txt index b809658b1..2058fbc02 100644 --- a/compiler/tfkit/CMakeLists.txt +++ b/compiler/tfkit/CMakeLists.txt @@ -7,7 +7,6 @@ message(STATUS "Build tfkit: TRUE") file(GLOB_RECURSE SOURCES "src/*.cpp") add_executable(tfkit ${SOURCES}) -target_link_libraries(tfkit PRIVATE stdex) target_link_libraries(tfkit PRIVATE cli) target_link_libraries(tfkit PRIVATE mio_tf) target_link_libraries(tfkit PRIVATE nncc_common) diff --git a/compiler/tfkit/src/ConvertCommand.cpp b/compiler/tfkit/src/ConvertCommand.cpp index 3e417cc78..2b5d077c9 100644 --- a/compiler/tfkit/src/ConvertCommand.cpp +++ b/compiler/tfkit/src/ConvertCommand.cpp @@ -17,8 +17,6 @@ #include "ConvertCommand.hpp" #include "Support.hpp" -#include <stdex/Memory.h> - #include <tensorflow/core/framework/graph.pb.h> #include <google/protobuf/io/coded_stream.h> @@ -26,6 +24,7 @@ #include <google/protobuf/text_format.h> #include <google/protobuf/util/json_util.h> +#include <memory> #include <cassert> #include <map> #include <string> @@ -114,12 +113,12 @@ int ConvertCommand::run(int argc, const char *const *argv) const std::map<std::string, std::unique_ptr<Importer>> importers; - importers["pb"] = stdex::make_unique<ImporterImpl<DataFormat::PBBIN>>(); - importers["pbtxt"] = stdex::make_unique<ImporterImpl<DataFormat::PBTXT>>(); + importers["pb"] = std::make_unique<ImporterImpl<DataFormat::PBBIN>>(); + importers["pbtxt"] = std::make_unique<ImporterImpl<DataFormat::PBTXT>>(); std::map<std::string, std::unique_ptr<Exporter>> exporters; - exporters["json"] = stdex::make_unique<ExporterImpl<DataFormat::JSON>>(); + exporters["json"] = std::make_unique<ExporterImpl<DataFormat::JSON>>(); auto importer = importers.at(input_format).get(); auto exporter = exporters.at(output_format).get(); diff --git a/compiler/tfkit/src/Main.cpp b/compiler/tfkit/src/Main.cpp index 60bd6abfa..a695741dd 100644 --- a/compiler/tfkit/src/Main.cpp +++ b/compiler/tfkit/src/Main.cpp @@ -21,17 +21,18 @@ #include "ConvertCommand.hpp" #include <cli/App.h> -#include <stdex/Memory.h> + +#include <memory> int main(int argc, char **argv) { cli::App app{argv[0]}; - app.insert("encode", stdex::make_unique<tfkit::EncodeCommand>()); - app.insert("decode", stdex::make_unique<tfkit::DecodeCommand>()); - app.insert("unpack", stdex::make_unique<tfkit::UnpackCommand>()); - app.insert("pack", stdex::make_unique<tfkit::PackCommand>()); - app.insert("convert", stdex::make_unique<tfkit::ConvertCommand>()); + app.insert("encode", std::make_unique<tfkit::EncodeCommand>()); + app.insert("decode", std::make_unique<tfkit::DecodeCommand>()); + app.insert("unpack", std::make_unique<tfkit::UnpackCommand>()); + app.insert("pack", std::make_unique<tfkit::PackCommand>()); + app.insert("convert", std::make_unique<tfkit::ConvertCommand>()); return app.run(argc - 1, argv + 1); } diff --git a/compiler/tfkit/src/PackCommand.cpp b/compiler/tfkit/src/PackCommand.cpp index a1c4a6fc8..d854e30db 100644 --- a/compiler/tfkit/src/PackCommand.cpp +++ b/compiler/tfkit/src/PackCommand.cpp @@ -60,7 +60,7 @@ template <> void pack<float>(tensorflow::TensorProto *input_tensor) } input_tensor->set_tensor_content(std::string( - reinterpret_cast<const char *>(tensor_content.data()), sizeof(float) * input_flat_size)); + reinterpret_cast<const char *>(tensor_content.data()), sizeof(float) * input_flat_size)); input_tensor->clear_float_val(); } @@ -99,7 +99,7 @@ template <> void pack<int32_t>(tensorflow::TensorProto *input_tensor) } input_tensor->set_tensor_content(std::string( - reinterpret_cast<const char *>(tensor_content.data()), sizeof(int32_t) * input_flat_size)); + reinterpret_cast<const char *>(tensor_content.data()), sizeof(int32_t) * input_flat_size)); input_tensor->clear_int_val(); } diff --git a/compiler/tfkit/src/Support.cpp b/compiler/tfkit/src/Support.cpp index 40d8705a7..1ce4c4680 100644 --- a/compiler/tfkit/src/Support.cpp +++ b/compiler/tfkit/src/Support.cpp @@ -17,10 +17,9 @@ #include "Support.hpp" -#include <stdex/Memory.h> - #include <tensorflow/core/framework/graph.pb.h> +#include <memory> #include <cassert> #include <fstream> #include <stdexcept> @@ -36,7 +35,7 @@ std::unique_ptr<T> open_fstream(const std::string &path, std::ios_base::openmode return nullptr; } - auto stream = stdex::make_unique<T>(path.c_str(), mode); + auto stream = std::make_unique<T>(path.c_str(), mode); if (!stream->is_open()) { throw std::runtime_error{"ERROR: Failed to open " + path}; @@ -111,7 +110,7 @@ std::string CmdArguments::get_or(unsigned int index, const std::string &s) const std::unique_ptr<IOConfiguration> make_ioconfig(const CmdArguments &cmdargs) { - auto iocfg = stdex::make_unique<IOConfiguration>(); + auto iocfg = std::make_unique<IOConfiguration>(); auto in = open_fstream<std::ifstream>(cmdargs.get_or(0, "-"), std::ios::in | std::ios::binary); iocfg->in(std::move(in)); diff --git a/compiler/tfkit/src/Support.hpp b/compiler/tfkit/src/Support.hpp index a5b954d5e..21726ea57 100644 --- a/compiler/tfkit/src/Support.hpp +++ b/compiler/tfkit/src/Support.hpp @@ -41,7 +41,7 @@ class CmdArguments public: CmdArguments() = delete; CmdArguments(int argc, const char *const *argv) - : _argc(static_cast<unsigned int>(argc)), _argv{argv} + : _argc(static_cast<unsigned int>(argc)), _argv{argv} { } diff --git a/compiler/tfkit/src/UnpackCommand.cpp b/compiler/tfkit/src/UnpackCommand.cpp index a6711f131..b5dd78cbb 100644 --- a/compiler/tfkit/src/UnpackCommand.cpp +++ b/compiler/tfkit/src/UnpackCommand.cpp @@ -52,7 +52,7 @@ template <> void unpack<float>(tensorflow::TensorProto *input_tensor) input_tensor->clear_float_val(); const float *tensor_content = - reinterpret_cast<const float *>(input_tensor->tensor_content().data()); + reinterpret_cast<const float *>(input_tensor->tensor_content().data()); for (int i = 0; i < input_flat_size; i++) { input_tensor->add_float_val(tensor_content[i]); @@ -87,7 +87,7 @@ template <> void unpack<int32_t>(tensorflow::TensorProto *input_tensor) input_tensor->clear_int_val(); const int32_t *tensor_content = - reinterpret_cast<const int32_t *>(input_tensor->tensor_content().data()); + reinterpret_cast<const int32_t *>(input_tensor->tensor_content().data()); for (int i = 0; i < input_flat_size; i++) { input_tensor->add_int_val(tensor_content[i]); @@ -122,7 +122,7 @@ template <> void unpack<int8_t>(tensorflow::TensorProto *input_tensor) input_tensor->clear_int_val(); const int8_t *tensor_content = - reinterpret_cast<const int8_t *>(input_tensor->tensor_content().data()); + reinterpret_cast<const int8_t *>(input_tensor->tensor_content().data()); for (int i = 0; i < input_flat_size; i++) { input_tensor->add_int_val(tensor_content[i]); @@ -157,7 +157,7 @@ template <> void unpack<bool>(tensorflow::TensorProto *input_tensor) input_tensor->clear_bool_val(); const bool *tensor_content = - reinterpret_cast<const bool *>(input_tensor->tensor_content().data()); + reinterpret_cast<const bool *>(input_tensor->tensor_content().data()); for (int i = 0; i < input_flat_size; i++) { input_tensor->add_bool_val(tensor_content[i]); diff --git a/compiler/tfl-inspect/driver/Driver.cpp b/compiler/tfl-inspect/driver/Driver.cpp index a48001169..3e62e0ffb 100644 --- a/compiler/tfl-inspect/driver/Driver.cpp +++ b/compiler/tfl-inspect/driver/Driver.cpp @@ -32,8 +32,8 @@ int entry(int argc, char **argv) "Lite model files"}; arser.add_argument("--operators").nargs(0).help("Dump operators in tflite file"); arser.add_argument("--conv2d_weight") - .nargs(0) - .help("Dump Conv2D series weight operators in tflite file"); + .nargs(0) + .help("Dump Conv2D series weight operators in tflite file"); arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in tflite file"); arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file to inspect"); diff --git a/compiler/tflchef/core/src/Convert.cpp b/compiler/tflchef/core/src/Convert.cpp index 9602faa96..de3ae4ed1 100644 --- a/compiler/tflchef/core/src/Convert.cpp +++ b/compiler/tflchef/core/src/Convert.cpp @@ -70,6 +70,8 @@ tflite::TensorType as_tflite_tensortype(const tflchef::TensorType &value) return tflite::TensorType_INT64; case tflchef::BOOL: return tflite::TensorType_BOOL; + case tflchef::INT16: + return tflite::TensorType_INT16; default: break; } diff --git a/compiler/tflchef/core/src/CustomOp/BroadcastTo.cpp b/compiler/tflchef/core/src/CustomOp/BroadcastTo.cpp new file mode 100644 index 000000000..fc429e2f7 --- /dev/null +++ b/compiler/tflchef/core/src/CustomOp/BroadcastTo.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2015 The TensorFlow Authors. 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 "BroadcastTo.h" + +#include "flatbuffers/flexbuffers.h" + +flatbuffers::Offset<void> BroadcastToChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + return flatbuffers::Offset<void>(); +} + +flatbuffers::Offset<flatbuffers::Vector<uint8_t>> +BroadcastToChef::custom_value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + + assert(operation.type() == "BroadcastTo"); + + /** + * REGISTER_OP("BroadcastTo") + .Input("input: T") + .Input("shape: Tidx") + .Output("output: T") + .Attr("T: type") + .Attr("Tidx: {int32, int64} = DT_INT32") + .SetShapeFn([](InferenceContext* c) + */ + + auto flex_buffers = std::make_unique<flexbuffers::Builder>(); + size_t map_start = flex_buffers->StartMap(); + + // TODO Support more data types + flex_buffers->Int("T", tflite::TensorType_FLOAT32); + flex_buffers->Int("Tidx", tflite::TensorType_INT32); + + flex_buffers->EndMap(map_start); + flex_buffers->Finish(); + + auto circle_custom_options = fbb.CreateVector(flex_buffers->GetBuffer()); + return circle_custom_options; +} + +std::unique_ptr<OpChef> BroadcastToChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr<OpChef>{new BroadcastToChef{operation}}; +} diff --git a/compiler/tflchef/core/src/CustomOp/BroadcastTo.h b/compiler/tflchef/core/src/CustomOp/BroadcastTo.h new file mode 100644 index 000000000..3ed71c511 --- /dev/null +++ b/compiler/tflchef/core/src/CustomOp/BroadcastTo.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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 __OP_BROADCASTTO_H__ +#define __OP_BROADCASTTO_H__ + +#include "OpChef.h" + +class BroadcastToChef final : public OpChef +{ +public: + explicit BroadcastToChef(const tflchef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_CUSTOM; } + + tflite::BuiltinOptions type(void) const override { return tflite::BuiltinOptions_NONE; } + + flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override; + + flatbuffers::Offset<flatbuffers::Vector<uint8_t>> + custom_value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct BroadcastToChefFactory final : public OpChefFactory +{ + std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_BROADCASTTO_H__ diff --git a/compiler/tflchef/core/src/ModelChef.cpp b/compiler/tflchef/core/src/ModelChef.cpp index 2f4d7eeb5..467b0d300 100644 --- a/compiler/tflchef/core/src/ModelChef.cpp +++ b/compiler/tflchef/core/src/ModelChef.cpp @@ -51,7 +51,7 @@ class GeneratedModelImpl final : public tflchef::GeneratedModel::Impl { public: GeneratedModelImpl(std::unique_ptr<flatbuffers::FlatBufferBuilder> &&builder) - : _builder{std::move(builder)} + : _builder{std::move(builder)} { // DO NOTHING } @@ -90,6 +90,7 @@ DataChefRegistry &data_chef_registry(const tflchef::TensorType &type) static DataChefRegistry fp32; static DataChefRegistry u8; static DataChefRegistry boolean; + static DataChefRegistry s16; switch (type) { @@ -103,6 +104,8 @@ DataChefRegistry &data_chef_registry(const tflchef::TensorType &type) return u8; case tflchef::BOOL: return boolean; + case tflchef::INT16: + return s16; default: break; } @@ -197,6 +200,7 @@ struct CookParams std::vector<flatbuffers::Offset<::tflite::SubGraph>> &subgraph_vec; std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder; std::map<tflite::BuiltinOperator, int32_t> &builtin_code_map; + std::vector<std::string> &custom_code_vec; std::string noname; }; @@ -209,6 +213,7 @@ template <typename T> void cook_graph(const T &graph, CookParams &cp) std::vector<flatbuffers::Offset<::tflite::SubGraph>> &subgraph_vec = cp.subgraph_vec; std::unique_ptr<flatbuffers::FlatBufferBuilder> &flatbuffer_builder = cp.flatbuffer_builder; std::map<tflite::BuiltinOperator, int32_t> &builtin_code_map = cp.builtin_code_map; + std::vector<std::string> &custom_code_vec = cp.custom_code_vec; // Operand-related std::vector<flatbuffers::Offset<::tflite::Tensor>> tensor_vec; @@ -399,21 +404,21 @@ template <typename T> void cook_graph(const T &graph, CookParams &cp) { // Create array segments auto tflite_array_segments = - as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_segments()); + as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_segments()); // Create array indices auto tflite_array_indices = - as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_indices()); + as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_indices()); auto tflite_dim_metadata_builder = tflite::DimensionMetadataBuilder{*flatbuffer_builder}; tflite_dim_metadata_builder.add_format(as_tflite_dimensiontype(dm.format())); tflite_dim_metadata_builder.add_dense_size(dm.dense_size()); tflite_dim_metadata_builder.add_array_segments(tflite_array_segments); tflite_dim_metadata_builder.add_array_segments_type( - as_tflite_sparse_idx_vec_type(dm.array_segments().type())); + as_tflite_sparse_idx_vec_type(dm.array_segments().type())); tflite_dim_metadata_builder.add_array_indices(tflite_array_indices); tflite_dim_metadata_builder.add_array_indices_type( - as_tflite_sparse_idx_vec_type(dm.array_indices().type())); + as_tflite_sparse_idx_vec_type(dm.array_indices().type())); auto tflite_dim_metadata = tflite_dim_metadata_builder.Finish(); dim_metadata_vec.emplace_back(tflite_dim_metadata); } @@ -480,11 +485,23 @@ template <typename T> void cook_graph(const T &graph, CookParams &cp) // Create Operator tflite::OperatorBuilder op_builder{*flatbuffer_builder}; - // Get operator code index from builtin_code_set with assumption, order of - // builtin_code_set is same as that of code_vec + // Note that opcode_index is an index into the operator_codes vector. + // operator_codes consists of buildtin_code and custom_code, which is inserted sequentially. + uint32_t opcode_index = 0; auto op_it = builtin_code_map.find(op_chef->code()); - assert(op_it != builtin_code_map.end()); - uint32_t opcode_index = std::distance(builtin_code_map.begin(), op_it); + // builtin operator + if (op_it != builtin_code_map.end()) + { + opcode_index = std::distance(builtin_code_map.begin(), op_it); + } + // custom operator + else + { + auto op_it = std::find(custom_code_vec.begin(), custom_code_vec.end(), operation.type()); + assert(op_it != custom_code_vec.end()); + opcode_index = builtin_code_map.size(); + opcode_index += std::distance(custom_code_vec.begin(), op_it); + } op_builder.add_opcode_index(opcode_index); op_builder.add_inputs(inputs); @@ -538,7 +555,7 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) // Initialize Data Chef Registry #define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \ data_chef_registry(::tflchef::TYPE) \ - .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS())); + .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS())); #include <souschef/DataChef.def> #undef DATA_CHEF @@ -546,7 +563,7 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) // Create FlatBufferBuilder // auto flatbuffer_builder = - std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024)); + std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024)); // Operand-related std::vector<flatbuffers::Offset<::tflite::Buffer>> buffer_vec; @@ -571,11 +588,9 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) // Create OperatorCode with Custom Operator std::set<std::string> custom_code_set = gather_customcode_set(model_recipe); - if (custom_code_set.size() && - builtin_code_map.find(tflite::BuiltinOperator_CUSTOM) == builtin_code_map.end()) - builtin_code_map[tflite::BuiltinOperator_CUSTOM] = 1; + std::vector<std::string> custom_code_vec{custom_code_set.begin(), custom_code_set.end()}; - for (auto opcode : custom_code_set) + for (auto opcode : custom_code_vec) { auto custom_code = flatbuffer_builder->CreateString(opcode); tflite::OperatorCodeBuilder code_builder{*flatbuffer_builder}; @@ -598,7 +613,8 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) // // Create Main graph // - CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, builtin_code_map, "main"}; + CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, + builtin_code_map, custom_code_vec, "main"}; cook_graph<::tflchef::ModelRecipe>(model_recipe, cp); @@ -612,8 +628,8 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) std::ostringstream stringStream; stringStream << "sub_" << (g + 1); - CookParams cp{buffer_vec, code_vec, subgraph_vec, - flatbuffer_builder, builtin_code_map, stringStream.str()}; + CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, + builtin_code_map, custom_code_vec, stringStream.str()}; cook_graph<::tflchef::Graph>(graph, cp); } @@ -640,7 +656,7 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) // Return "GenerateModel" return GeneratedModel{ - std::unique_ptr<GeneratedModelImpl>(new GeneratedModelImpl(std::move(flatbuffer_builder)))}; + std::unique_ptr<GeneratedModelImpl>(new GeneratedModelImpl(std::move(flatbuffer_builder)))}; } } // namespace tflchef diff --git a/compiler/tflchef/core/src/Op/BidirectionalSequenceLSTM.cpp b/compiler/tflchef/core/src/Op/BidirectionalSequenceLSTM.cpp new file mode 100644 index 000000000..1bf2264ab --- /dev/null +++ b/compiler/tflchef/core/src/Op/BidirectionalSequenceLSTM.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 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 "BidirectionalSequenceLSTM.h" +#include "Convert.h" + +#include <cassert> + +flatbuffers::Offset<void> +BidirectionalSequenceLSTMChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + + assert(operation.has_bidirectional_sequence_lstm_options()); + + tflite::BidirectionalSequenceLSTMOptionsBuilder options_builder(fbb); + options_builder.add_fused_activation_function( + as_tflite_activation(operation.bidirectional_sequence_lstm_options().activation())); + options_builder.add_cell_clip(operation.bidirectional_sequence_lstm_options().cell_clip()); + options_builder.add_proj_clip(operation.bidirectional_sequence_lstm_options().proj_clip()); + options_builder.add_time_major(operation.bidirectional_sequence_lstm_options().time_major()); + options_builder.add_asymmetric_quantize_inputs( + operation.bidirectional_sequence_lstm_options().asymmetric_quantize_inputs()); + options_builder.add_merge_outputs( + operation.bidirectional_sequence_lstm_options().merge_outputs()); + + return options_builder.Finish().Union(); +} + +std::unique_ptr<OpChef> +BidirectionalSequenceLSTMChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr<OpChef>{new BidirectionalSequenceLSTMChef{operation}}; +} diff --git a/compiler/tflchef/core/src/Op/BidirectionalSequenceLSTM.h b/compiler/tflchef/core/src/Op/BidirectionalSequenceLSTM.h new file mode 100644 index 000000000..e66917b97 --- /dev/null +++ b/compiler/tflchef/core/src/Op/BidirectionalSequenceLSTM.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 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 __OP_BIDIRECTIONALSEQUENCE_LSTM_H__ +#define __OP_BIDIRECTIONALSEQUENCE_LSTM_H__ + +#include "OpChef.h" + +class BidirectionalSequenceLSTMChef final : public OpChef +{ +public: + explicit BidirectionalSequenceLSTMChef(const tflchef::Operation *operation) + : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override + { + return tflite::BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM; + } + + tflite::BuiltinOptions type(void) const override + { + return tflite::BuiltinOptions_BidirectionalSequenceLSTMOptions; + } + + flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct BidirectionalSequenceLSTMChefFactory final : public OpChefFactory +{ + std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_BIDIRECTIONALSEQUENCE_LSTM_H__ diff --git a/compiler/tflchef/core/src/Op/FakeQuant.cpp b/compiler/tflchef/core/src/Op/FakeQuant.cpp new file mode 100644 index 000000000..e4cbbfe44 --- /dev/null +++ b/compiler/tflchef/core/src/Op/FakeQuant.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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 "FakeQuant.h" +#include "Convert.h" + +#include <cassert> + +flatbuffers::Offset<void> FakeQuantChef::value(flatbuffers::FlatBufferBuilder &fbb) const +{ + auto &operation = (*_operation); + assert(operation.has_fakequant_options()); + + auto options = operation.fakequant_options(); + + tflite::FakeQuantOptionsBuilder fq_options_builder{fbb}; + fq_options_builder.add_min(options.min()); + fq_options_builder.add_max(options.max()); + fq_options_builder.add_num_bits(options.num_bits()); + fq_options_builder.add_narrow_range(options.narrow_range()); + + return fq_options_builder.Finish().Union(); +} + +std::unique_ptr<OpChef> FakeQuantChefFactory::create(const tflchef::Operation *operation) const +{ + return std::unique_ptr<OpChef>{new FakeQuantChef{operation}}; +} diff --git a/compiler/tflchef/core/src/Op/FakeQuant.h b/compiler/tflchef/core/src/Op/FakeQuant.h new file mode 100644 index 000000000..0fbfea315 --- /dev/null +++ b/compiler/tflchef/core/src/Op/FakeQuant.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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 __OP_FAKE_QUANT_H__ +#define __OP_FAKE_QUANT_H__ + +#include "OpChef.h" + +class FakeQuantChef final : public OpChef +{ +public: + explicit FakeQuantChef(const tflchef::Operation *operation) : _operation{operation} + { + // DO NOTHING + } + +public: + tflite::BuiltinOperator code(void) const override { return tflite::BuiltinOperator_FAKE_QUANT; } + + tflite::BuiltinOptions type(void) const override + { + return tflite::BuiltinOptions_FakeQuantOptions; + } + + flatbuffers::Offset<void> value(flatbuffers::FlatBufferBuilder &fbb) const override; + +private: + const tflchef::Operation *_operation; +}; + +struct FakeQuantChefFactory final : public OpChefFactory +{ + std::unique_ptr<OpChef> create(const tflchef::Operation *operation) const override; +}; + +#endif // __OP_FAKE_QUANT_H__ diff --git a/compiler/tflchef/core/src/Op/LocalResponseNormalization.h b/compiler/tflchef/core/src/Op/LocalResponseNormalization.h index 62a2355f2..afc37e6ec 100644 --- a/compiler/tflchef/core/src/Op/LocalResponseNormalization.h +++ b/compiler/tflchef/core/src/Op/LocalResponseNormalization.h @@ -23,7 +23,7 @@ class LocalResponseNormalizationChef final : public OpChef { public: explicit LocalResponseNormalizationChef(const tflchef::Operation *operation) - : _operation{operation} + : _operation{operation} { // DO NOTHING } diff --git a/compiler/tflchef/core/src/Op/Squeeze.cpp b/compiler/tflchef/core/src/Op/Squeeze.cpp index 8d6ef42d6..1c1d99a01 100644 --- a/compiler/tflchef/core/src/Op/Squeeze.cpp +++ b/compiler/tflchef/core/src/Op/Squeeze.cpp @@ -30,7 +30,7 @@ flatbuffers::Offset<void> SqueezeChef::value(flatbuffers::FlatBufferBuilder &fbb // Note: 'CreateVector' should be placed before 'CreateOptions' // Read flatbuffers.h 'void NotNested()' for more information auto fb_squeeze_dims = - fbb.CreateVector(options.squeeze_dim().data(), options.squeeze_dim().size()); + fbb.CreateVector(options.squeeze_dim().data(), options.squeeze_dim().size()); return tflite::CreateSqueezeOptions(fbb, fb_squeeze_dims).Union(); } diff --git a/compiler/tflchef/core/src/Op/StridedSlice.cpp b/compiler/tflchef/core/src/Op/StridedSlice.cpp index 587a95c66..67fd03140 100644 --- a/compiler/tflchef/core/src/Op/StridedSlice.cpp +++ b/compiler/tflchef/core/src/Op/StridedSlice.cpp @@ -29,11 +29,11 @@ flatbuffers::Offset<void> StridedSliceChef::value(flatbuffers::FlatBufferBuilder strided_slice_options_builder.add_begin_mask(operation.strided_slice_options().begin_mask()); strided_slice_options_builder.add_end_mask(operation.strided_slice_options().end_mask()); strided_slice_options_builder.add_ellipsis_mask( - operation.strided_slice_options().ellipsis_mask()); + operation.strided_slice_options().ellipsis_mask()); strided_slice_options_builder.add_new_axis_mask( - operation.strided_slice_options().new_axis_mask()); + operation.strided_slice_options().new_axis_mask()); strided_slice_options_builder.add_shrink_axis_mask( - operation.strided_slice_options().shrink_axis_mask()); + operation.strided_slice_options().shrink_axis_mask()); return strided_slice_options_builder.Finish().Union(); } diff --git a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp index ceabfc13c..2d6becdff 100644 --- a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp +++ b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.cpp @@ -28,12 +28,12 @@ UnidirectionalSequenceLSTMChef::value(flatbuffers::FlatBufferBuilder &fbb) const tflite::UnidirectionalSequenceLSTMOptionsBuilder options_builder(fbb); options_builder.add_fused_activation_function( - as_tflite_activation(operation.unidirectional_sequence_lstm_options().activation())); + as_tflite_activation(operation.unidirectional_sequence_lstm_options().activation())); options_builder.add_cell_clip(operation.unidirectional_sequence_lstm_options().cell_clip()); options_builder.add_proj_clip(operation.unidirectional_sequence_lstm_options().proj_clip()); options_builder.add_time_major(operation.unidirectional_sequence_lstm_options().time_major()); options_builder.add_asymmetric_quantize_inputs( - operation.unidirectional_sequence_lstm_options().asymmetric_quantize_inputs()); + operation.unidirectional_sequence_lstm_options().asymmetric_quantize_inputs()); return options_builder.Finish().Union(); } diff --git a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h index 6811ad378..b8a6d8103 100644 --- a/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h +++ b/compiler/tflchef/core/src/Op/UnidirectionalSequenceLSTM.h @@ -23,7 +23,7 @@ class UnidirectionalSequenceLSTMChef final : public OpChef { public: explicit UnidirectionalSequenceLSTMChef(const tflchef::Operation *operation) - : _operation{operation} + : _operation{operation} { // DO NOTHING } diff --git a/compiler/tflchef/core/src/OpChef.def b/compiler/tflchef/core/src/OpChef.def index 718fffc78..714e8947b 100644 --- a/compiler/tflchef/core/src/OpChef.def +++ b/compiler/tflchef/core/src/OpChef.def @@ -12,6 +12,7 @@ OP_CHEF(ArgMin, ArgMinChefFactory) OP_CHEF(AveragePool2D, AveragePool2DChefFactory) OP_CHEF(BatchMatMul, BatchMatMulChefFactory) OP_CHEF(BatchToSpaceND, BatchToSpaceNDChefFactory) +OP_CHEF(BidirectionalSequenceLSTM, BidirectionalSequenceLSTMChefFactory) OP_CHEF(Cast, CastChefFactory) OP_CHEF(Ceil, CeilChefFactory) OP_CHEF(Concatenation, ConcatenationChefFactory) @@ -25,6 +26,7 @@ OP_CHEF(ELU, ELUChefFactory) OP_CHEF(Equal, EqualChefFactory) OP_CHEF(Exp, ExpChefFactory) OP_CHEF(ExpandDims, ExpandDimsChefFactory) +OP_CHEF(FakeQuant, FakeQuantChefFactory) OP_CHEF(Fill, FillChefFactory) OP_CHEF(Floor, FloorChefFactory) OP_CHEF(FloorDiv, FloorDivChefFactory) @@ -117,6 +119,7 @@ OP_CHEF(ZerosLike, ZerosLikeChefFactory) OP_CHEF(AddV2, AddV2ChefFactory) OP_CHEF(All, AllChefFactory) OP_CHEF(BatchMatMulV2, BatchMatMulV2ChefFactory) +OP_CHEF(BroadcastTo, BroadcastToChefFactory) OP_CHEF(MatMul, MatMulChefFactory) OP_CHEF(MatrixBandPart, MatrixBandPartChefFactory) OP_CHEF(MaxPoolWithArgMax, MaxPoolWithArgMaxChefFactory) diff --git a/compiler/tflchef/core/src/OpChefs.h b/compiler/tflchef/core/src/OpChefs.h index 3527937a0..99f331e37 100644 --- a/compiler/tflchef/core/src/OpChefs.h +++ b/compiler/tflchef/core/src/OpChefs.h @@ -25,6 +25,7 @@ #include "Op/AveragePool2D.h" #include "Op/BatchMatMul.h" #include "Op/BatchToSpaceND.h" +#include "Op/BidirectionalSequenceLSTM.h" #include "Op/Cast.h" #include "Op/Ceil.h" #include "Op/Concatenation.h" @@ -38,6 +39,7 @@ #include "Op/Equal.h" #include "Op/Exp.h" #include "Op/ExpandDims.h" +#include "Op/FakeQuant.h" #include "Op/Fill.h" #include "Op/Floor.h" #include "Op/FloorDiv.h" @@ -129,6 +131,7 @@ #include "CustomOp/AddV2.h" #include "CustomOp/All.h" #include "CustomOp/BatchMatMulV2.h" +#include "CustomOp/BroadcastTo.h" #include "CustomOp/MatMul.h" #include "CustomOp/MatrixBandPart.h" #include "CustomOp/MaxPoolWithArgMax.h" diff --git a/compiler/tflchef/proto/tflchef.proto b/compiler/tflchef/proto/tflchef.proto index 48a682d94..c5e44f68c 100644 --- a/compiler/tflchef/proto/tflchef.proto +++ b/compiler/tflchef/proto/tflchef.proto @@ -19,6 +19,7 @@ enum TensorType { UINT8 = 3; INT64 = 4; BOOL = 6; + INT16 = 7; } enum DimensionType { @@ -110,6 +111,15 @@ enum MirrorPadMode { SYMMETRIC = 1; } +message BidirectionalSequenceLSTMOptions { + optional Activation activation = 1 [default = NONE]; + optional float cell_clip = 2 [default = 0.0]; + optional float proj_clip = 3 [default = 0.0]; + optional bool merge_outputs = 6 [default = false]; + optional bool time_major = 4 [default = true]; + optional bool asymmetric_quantize_inputs = 5 [default = false]; +} + message Conv2DOptions { optional Padding padding = 1 [default = VALID]; @@ -509,6 +519,13 @@ message MaxPoolWithArgMaxOptions { optional bool include_batch_in_index = 7 [default = false]; } +message FakeQuantOptions { + optional float min = 1 [default = 0.0]; + optional float max = 2 [default = 0.0]; + optional int32 num_bits = 3 [default = 0]; + optional bool narrow_range = 4 [default = false]; +} + message Operation { optional string type = 1; repeated string input = 2; @@ -593,8 +610,8 @@ message Operation { optional SparseToDenseOptions sparse_to_dense_options = 175; optional PowOptions pow_options = 176; optional ArgMinOptions argmin_options = 177; - // FakeQuantOptions 178 - // BidirectionalSequenceLSTMOptions 179 + optional FakeQuantOptions fakequant_options = 178; + optional BidirectionalSequenceLSTMOptions bidirectional_sequence_lstm_options = 179; // BidirectionalSequenceRNNOptions 180 optional UnidirectionalSequenceLSTMOptions unidirectional_sequence_lstm_options = 181; optional RangeOptions range_options = 182; diff --git a/compiler/tflchef/tests/short_int_datatype/test.recipe b/compiler/tflchef/tests/short_int_datatype/test.recipe new file mode 100644 index 000000000..1e135d912 --- /dev/null +++ b/compiler/tflchef/tests/short_int_datatype/test.recipe @@ -0,0 +1,44 @@ +operand { + name: "ifm" + type: INT16 + shape { dim: 1 dim: 5 dim: 5 dim: 2 } +} +operand { + name: "ker" + type: INT16 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } + filler { + tag: "gaussian" + arg: "1.0" + arg: "6.0" + } +} +operand { + name: "bias" + type: INT16 + shape { dim: 1 } + filler { + tag: "constant" + arg: "12345" + } +} +operand { + name: "ofm" + type: INT16 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +input: "ker" +output: "ofm" diff --git a/compiler/tflchef/tests/short_int_datatype/test.reverse b/compiler/tflchef/tests/short_int_datatype/test.reverse new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/compiler/tflchef/tests/short_int_datatype/test.reverse diff --git a/compiler/tflchef/tflite/CMakeLists.txt b/compiler/tflchef/tflite/CMakeLists.txt index 83127cb3e..ce8b8c463 100644 --- a/compiler/tflchef/tflite/CMakeLists.txt +++ b/compiler/tflchef/tflite/CMakeLists.txt @@ -5,6 +5,5 @@ target_include_directories(tflchef_tflite PUBLIC include) target_include_directories(tflchef_tflite PRIVATE src) target_link_libraries(tflchef_tflite tflchef_proto) target_link_libraries(tflchef_tflite mio_tflite) -target_link_libraries(tflchef_tflite stdex) target_link_libraries(tflchef_tflite cwrap) target_link_libraries(tflchef_tflite souschef) diff --git a/compiler/tflchef/tflite/src/Convert.cpp b/compiler/tflchef/tflite/src/Convert.cpp index 29276ff94..f47e51d3d 100644 --- a/compiler/tflchef/tflite/src/Convert.cpp +++ b/compiler/tflchef/tflite/src/Convert.cpp @@ -33,10 +33,11 @@ tflchef::TensorType as_tflchef_type(const tflite::TensorType type) return tflchef::UINT8; case tflite::TensorType_BOOL: return tflchef::BOOL; + case tflite::TensorType_INT16: + return tflchef::INT16; // TODO handle other types // TensorType_FLOAT16 // TensorType_STRING - // TensorType_INT16 // TensorType_COMPLEX64 default: throw std::runtime_error{"unsupported tensor type"}; diff --git a/compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.cpp b/compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.cpp new file mode 100644 index 000000000..32548247e --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020 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 "BidirectionalSequenceLSTM.h" + +#include "Convert.h" +#include "FillerHelper.h" + +namespace tflchef +{ + +void TFliteOpBidirectionalSequenceLSTM::filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + const std::vector<int32_t> &inputs = as_index_vector(op->inputs()); + assert(inputs.size() == 48); + + for (int32_t i = 0; i < inputs.size(); i++) + { + // Except for Input 0, 35, 36, 37 and 38. + // Each Input mean Input Tensor, ActivationState Tensor (forward and backward), and CellState + // Tensor (forward and backward). + // This could be updated from previous input or User Given data, so This could not be Const + if (i == 0 || i == 35 || i == 36 || i == 37 || i == 38) + continue; + if (inputs[i] != -1) + fill_tensor_to_import(inputs[i], import); + } +} + +tflchef::Operation * +TFliteOpBidirectionalSequenceLSTM::build(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + auto op_params = op->builtin_options_as_BidirectionalSequenceLSTMOptions(); + assert(op_params != nullptr); + + auto operation = model_recipe->add_operation(); + + operation->set_type("BidirectionalSequenceLSTM"); + + auto op_options = operation->mutable_bidirectional_sequence_lstm_options(); + + op_options->set_activation(as_tflchef_activation(op_params->fused_activation_function())); + op_options->set_cell_clip(op_params->cell_clip()); + op_options->set_proj_clip(op_params->proj_clip()); + op_options->set_time_major(op_params->time_major()); + op_options->set_asymmetric_quantize_inputs(op_params->asymmetric_quantize_inputs()); + op_options->set_merge_outputs(op_params->merge_outputs()); + + return operation; +} + +} // namespace tflchef diff --git a/compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.h b/compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.h new file mode 100644 index 000000000..333f542ac --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/BidirectionalSequenceLSTM.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 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 __TFLITE_OP_BIDIRECTIONALSEQUENCE_LSTM_H__ +#define __TFLITE_OP_BIDIRECTIONALSEQUENCE_LSTM_H__ + +#include "TFliteOpChef.h" + +namespace tflchef +{ + +/** + * @brief tflchef operator builder for BidirectionalSequenceLSTM + */ +class TFliteOpBidirectionalSequenceLSTM : public TFliteOpChef +{ +public: + void filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const override; + tflchef::Operation *build(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const override; +}; + +} // namespace tflchef + +#endif // __TFLITE_OP_BIDIRECTIONALSEQUENCE_LSTM_H__ diff --git a/compiler/tflchef/tflite/src/Op/FakeQuant.cpp b/compiler/tflchef/tflite/src/Op/FakeQuant.cpp new file mode 100644 index 000000000..f44b85465 --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/FakeQuant.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 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 "FakeQuant.h" + +#include "Convert.h" + +namespace tflchef +{ + +void TFliteOpFakeQuant::filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + // Nothing to do with filler +} + +tflchef::Operation *TFliteOpFakeQuant::build(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const +{ + auto op_params = op->builtin_options_as_FakeQuantOptions(); + assert(op_params != nullptr); + + auto operation = model_recipe->add_operation(); + + operation->set_type("FakeQuant"); + + auto op_options = operation->mutable_fakequant_options(); + + op_options->set_min(op_params->min()); + op_options->set_max(op_params->max()); + op_options->set_num_bits(op_params->num_bits()); + op_options->set_narrow_range(op_params->narrow_range()); + + return operation; +} + +} // namespace tflchef diff --git a/compiler/tflchef/tflite/src/Op/FakeQuant.h b/compiler/tflchef/tflite/src/Op/FakeQuant.h new file mode 100644 index 000000000..f36e615df --- /dev/null +++ b/compiler/tflchef/tflite/src/Op/FakeQuant.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 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 __TFLITE_OP_FAKE_QUANT_H__ +#define __TFLITE_OP_FAKE_QUANT_H__ + +#include "TFliteOpChef.h" + +namespace tflchef +{ + +/** + * @brief tflchef operator builder for FakeQuant + */ +class TFliteOpFakeQuant : public TFliteOpChef +{ +public: + void filler(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const override; + tflchef::Operation *build(const tflite::Operator *op, TFliteImport *import, + tflchef::ModelRecipe *model_recipe) const override; +}; + +} // namespace tflchef + +#endif // __TFLITE_OP_FAKE_QUANT_H__ diff --git a/compiler/tflchef/tflite/src/Op/Maximum.cpp b/compiler/tflchef/tflite/src/Op/Maximum.cpp index fb977b6ed..d52caf0c2 100644 --- a/compiler/tflchef/tflite/src/Op/Maximum.cpp +++ b/compiler/tflchef/tflite/src/Op/Maximum.cpp @@ -16,13 +16,20 @@ #include "Maximum.h" +#include "Convert.h" +#include "FillerHelper.h" + namespace tflchef { void TFliteOpMaximum::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { - // Nothing to do with filler + const std::vector<int32_t> &inputs = as_index_vector(op->inputs()); + assert(inputs.size() == 2); + + fill_tensor_to_import(inputs[0], import); + fill_tensor_to_import(inputs[1], import); } tflchef::Operation *TFliteOpMaximum::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/Minimum.cpp b/compiler/tflchef/tflite/src/Op/Minimum.cpp index 2bb50cb89..6440f1deb 100644 --- a/compiler/tflchef/tflite/src/Op/Minimum.cpp +++ b/compiler/tflchef/tflite/src/Op/Minimum.cpp @@ -17,6 +17,7 @@ #include "Minimum.h" #include "Convert.h" +#include "FillerHelper.h" namespace tflchef { @@ -24,7 +25,11 @@ namespace tflchef void TFliteOpMinimum::filler(const tflite::Operator *op, TFliteImport *import, tflchef::ModelRecipe *model_recipe) const { - // Nothing to do with filler + const std::vector<int32_t> &inputs = as_index_vector(op->inputs()); + assert(inputs.size() == 2); + + fill_tensor_to_import(inputs[0], import); + fill_tensor_to_import(inputs[1], import); } tflchef::Operation *TFliteOpMinimum::build(const tflite::Operator *op, TFliteImport *import, diff --git a/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp index c2c79285b..b2bc1acbd 100644 --- a/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp +++ b/compiler/tflchef/tflite/src/Op/UnidirectionalSequenceLSTM.cpp @@ -30,11 +30,11 @@ void TFliteOpUnidirectionalSequenceLSTM::filler(const tflite::Operator *op, TFli for (int32_t i = 0; i < inputs.size(); i++) { - // Except for Input 0, 17 and 18. - // Each Input mean Input[0](=Input Tensor), Input[17](=OutputState Tensor) and - // Input[18](=CellState Tensor). + // Except for Input 0, 18 and 19. + // Each Input mean Input[0](=Input Tensor), Input[18](=OutputState Tensor) and + // Input[19](=CellState Tensor). // This could be updated from previous input or User Given data, so This could not be Const - if (i == 0 || i == 17 || i == 18) + if (i == 0 || i == 18 || i == 19) continue; if (inputs[i] != -1) fill_tensor_to_import(inputs[i], import); diff --git a/compiler/tflchef/tflite/src/TFliteOpChefs.h b/compiler/tflchef/tflite/src/TFliteOpChefs.h index 2e4d28051..960ff6e36 100644 --- a/compiler/tflchef/tflite/src/TFliteOpChefs.h +++ b/compiler/tflchef/tflite/src/TFliteOpChefs.h @@ -26,6 +26,7 @@ #include "Op/AveragePool2D.h" #include "Op/BatchMatMul.h" #include "Op/BatchToSpaceND.h" +#include "Op/BidirectionalSequenceLSTM.h" #include "Op/Cast.h" #include "Op/Ceil.h" #include "Op/Concatenation.h" @@ -39,6 +40,7 @@ #include "Op/Equal.h" #include "Op/Exp.h" #include "Op/ExpandDims.h" +#include "Op/FakeQuant.h" #include "Op/Fill.h" #include "Op/Floor.h" #include "Op/FloorDiv.h" diff --git a/compiler/tflchef/tflite/src/TFliteOpRegistry.h b/compiler/tflchef/tflite/src/TFliteOpRegistry.h index 9cc630a97..c240bcf52 100644 --- a/compiler/tflchef/tflite/src/TFliteOpRegistry.h +++ b/compiler/tflchef/tflite/src/TFliteOpRegistry.h @@ -63,6 +63,7 @@ private: REG_TFL_OP(AVERAGE_POOL_2D, TFliteOpAveragePool2D); REG_TFL_OP(BATCH_MATMUL, TFliteOpBatchMatMul); REG_TFL_OP(BATCH_TO_SPACE_ND, TFliteOpBatchToSpaceND); + REG_TFL_OP(BIDIRECTIONAL_SEQUENCE_LSTM, TFliteOpBidirectionalSequenceLSTM); REG_TFL_OP(CAST, TFliteOpCast); REG_TFL_OP(CEIL, TFliteOpCeil); REG_TFL_OP(CONCATENATION, TFliteOpConcatenation); @@ -76,6 +77,7 @@ private: REG_TFL_OP(EQUAL, TFliteOpEqual); REG_TFL_OP(EXP, TFliteOpExp); REG_TFL_OP(EXPAND_DIMS, TFliteOpExpandDims); + REG_TFL_OP(FAKE_QUANT, TFliteOpFakeQuant); REG_TFL_OP(FILL, TFliteOpFill); REG_TFL_OP(FLOOR, TFliteOpFloor); REG_TFL_OP(FLOOR_DIV, TFliteOpFloorDiv); diff --git a/compiler/tflchef/tools/console/CMakeLists.txt b/compiler/tflchef/tools/console/CMakeLists.txt index d9160c3a2..c57e3fdcb 100644 --- a/compiler/tflchef/tools/console/CMakeLists.txt +++ b/compiler/tflchef/tools/console/CMakeLists.txt @@ -1,3 +1,14 @@ add_executable(tflchef Driver.cpp) target_link_libraries(tflchef tflchef_core) target_link_libraries(tflchef safemain) + +install(TARGETS tflchef DESTINATION bin) + +if(NOT ENABLE_TEST) + return() +endif(NOT ENABLE_TEST) + +nnas_find_package(GTest REQUIRED) + +GTest_AddTest(tflchef_test Driver.test.cpp Driver.cpp) +target_link_libraries(tflchef_test tflchef_core) diff --git a/compiler/tflchef/tools/console/Driver.cpp b/compiler/tflchef/tools/console/Driver.cpp index d6f7ba1ae..23f2fff3f 100644 --- a/compiler/tflchef/tools/console/Driver.cpp +++ b/compiler/tflchef/tools/console/Driver.cpp @@ -22,7 +22,7 @@ #include <iostream> -int entry(int argc, char **argv) +int entry_stream(std::istream &is) { int32_t model_version = 1; @@ -30,7 +30,7 @@ int entry(int argc, char **argv) // Read a model recipe from standard input { - google::protobuf::io::IstreamInputStream iis{&std::cin}; + google::protobuf::io::IstreamInputStream iis{&is}; if (!google::protobuf::TextFormat::Parse(&iis, &model_recipe)) { std::cerr << "ERROR: Failed to parse recipe" << std::endl; @@ -56,3 +56,9 @@ int entry(int argc, char **argv) return 0; } + +int entry(int, char **) +{ + // forward to entry_stream + return entry_stream(std::cin); +} diff --git a/compiler/tflchef/tools/console/Driver.test.cpp b/compiler/tflchef/tools/console/Driver.test.cpp new file mode 100644 index 000000000..b3cf2134d --- /dev/null +++ b/compiler/tflchef/tools/console/Driver.test.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> + +// entry function to test from Driver.cpp +int entry_stream(std::istream &is); + +TEST(TFlChefDriverTest, entry_empty_NEG) +{ + std::istringstream empty_input(""); + + ASSERT_EQ(0, entry_stream(empty_input)); +} + +TEST(TFlChefDriverTest, entry_invaid_NEG) +{ + std::istringstream empty_input("invalid: input"); + + ASSERT_NE(0, entry_stream(empty_input)); +} + +TEST(TFlChefDriverTest, entry_invaid_version_NEG) +{ + std::istringstream empty_input("version: 9999"); + + ASSERT_NE(0, entry_stream(empty_input)); +} diff --git a/compiler/tflchef/tools/file/CMakeLists.txt b/compiler/tflchef/tools/file/CMakeLists.txt index f411d60f1..e3b7b2f48 100644 --- a/compiler/tflchef/tools/file/CMakeLists.txt +++ b/compiler/tflchef/tools/file/CMakeLists.txt @@ -2,3 +2,5 @@ add_executable(tflchef-file Driver.cpp) target_link_libraries(tflchef-file arser) target_link_libraries(tflchef-file tflchef_core) target_link_libraries(tflchef-file safemain) + +install(TARGETS tflchef-file DESTINATION bin) diff --git a/compiler/tflchef/tools/file/Driver.cpp b/compiler/tflchef/tools/file/Driver.cpp index 46e5b5583..d4605ced3 100644 --- a/compiler/tflchef/tools/file/Driver.cpp +++ b/compiler/tflchef/tools/file/Driver.cpp @@ -29,8 +29,8 @@ int entry(int argc, char **argv) { arser::Arser arser; arser.add_argument("recipe") - .type(arser::DataType::STR) - .help("Source recipe file path to convert"); + .type(arser::DataType::STR) + .help("Source recipe file path to convert"); arser.add_argument("tflite").type(arser::DataType::STR).help("Target tflite file path"); try @@ -67,8 +67,8 @@ int entry(int argc, char **argv) if (model_version > 1) { - std::cerr << "ERROR: Unsupported recipe version: " << model_version << ", '" << argv[1] << "'" - << std::endl; + std::cerr << "ERROR: Unsupported recipe version: " << model_version << ", '" << recipe_path + << "'" << std::endl; return 255; } diff --git a/compiler/tflchef/tools/reverse/CMakeLists.txt b/compiler/tflchef/tools/reverse/CMakeLists.txt index a5c0f5bca..21700faca 100644 --- a/compiler/tflchef/tools/reverse/CMakeLists.txt +++ b/compiler/tflchef/tools/reverse/CMakeLists.txt @@ -3,3 +3,5 @@ target_link_libraries(tflchef-reverse arser) target_link_libraries(tflchef-reverse tflchef_tflite) target_link_libraries(tflchef-reverse safemain) target_link_libraries(tflchef-reverse foder) + +install(TARGETS tflchef-reverse DESTINATION bin) diff --git a/compiler/tflchef/tools/reverse/Driver.cpp b/compiler/tflchef/tools/reverse/Driver.cpp index 4d795a3d0..1451e8bb8 100644 --- a/compiler/tflchef/tools/reverse/Driver.cpp +++ b/compiler/tflchef/tools/reverse/Driver.cpp @@ -26,8 +26,8 @@ int entry(int argc, char **argv) { arser::Arser arser; arser.add_argument("tflite") - .type(arser::DataType::STR) - .help("Source tflite file path to convert"); + .type(arser::DataType::STR) + .help("Source tflite file path to convert"); arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path"); try diff --git a/compiler/tfldump/README.md b/compiler/tfldump/README.md index 50d003f12..65ad105c2 100644 --- a/compiler/tfldump/README.md +++ b/compiler/tfldump/README.md @@ -63,5 +63,4 @@ O T(3) ofm ### Dependency - safemain -- stdex - FlatBuffers diff --git a/compiler/tfldump/src/OpPrinter.cpp b/compiler/tfldump/src/OpPrinter.cpp index c35848047..4a417cef1 100644 --- a/compiler/tfldump/src/OpPrinter.cpp +++ b/compiler/tfldump/src/OpPrinter.cpp @@ -74,6 +74,26 @@ public: } }; +class BidirectionalSequenceLSTMPrinter : public OpPrinter +{ +public: + void options(const tflite::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_BidirectionalSequenceLSTMOptions()) + { + os << " "; + os << "Activation(" << EnumNameActivationFunctionType(params->fused_activation_function()) + << ") "; + os << "cell_clip(" << params->cell_clip() << ") "; + os << "proj_clip(" << params->proj_clip() << ") "; + os << "time_major(" << params->time_major() << ") "; + os << "asymmetric_quantize_inputs(" << params->asymmetric_quantize_inputs() << ") "; + os << "merge_outputs(" << params->merge_outputs() << ") "; + os << std::endl; + } + } +}; + class CastPrinter : public OpPrinter { public: @@ -277,7 +297,7 @@ public: os << "Stride.H(" << conv_params->stride_h() << ") "; os << "DepthMultiplier(" << conv_params->depth_multiplier() << ") "; os << "Dilation.W(" << conv_params->dilation_w_factor() << ") "; - os << "Dilation.H(" << conv_params->dilation_h_factor() << ")"; + os << "Dilation.H(" << conv_params->dilation_h_factor() << ") "; os << "Activation(" << EnumNameActivationFunctionType(conv_params->fused_activation_function()) << ") "; os << std::endl; @@ -285,6 +305,25 @@ public: } }; +class FakeQuantPrinter : public OpPrinter +{ +public: + void options(const tflite::Operator *op, std::ostream &os) const override + { + if (auto *params = op->builtin_options_as_FakeQuantOptions()) + { + os << " "; + os << "Min(" << params->min() << ") "; + os << "Max(" << params->max() << ") "; + os << "NumBits(" << params->num_bits() << ") "; + os << std::boolalpha; + os << "NarrowRange(" << params->narrow_range() << ") "; + os << std::noboolalpha; + os << std::endl; + } + } +}; + class FullyConnectedPrinter : public OpPrinter { public: @@ -672,6 +711,8 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[tflite::BuiltinOperator_ARG_MAX] = make_unique<ArgMaxPrinter>(); _op_map[tflite::BuiltinOperator_ARG_MIN] = make_unique<ArgMinPrinter>(); _op_map[tflite::BuiltinOperator_AVERAGE_POOL_2D] = make_unique<Pool2DPrinter>(); + _op_map[tflite::BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM] = + make_unique<BidirectionalSequenceLSTMPrinter>(); _op_map[tflite::BuiltinOperator_CAST] = make_unique<CastPrinter>(); // There is no Option for CEIL _op_map[tflite::BuiltinOperator_CONCATENATION] = make_unique<ConcatenationPrinter>(); @@ -680,6 +721,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = make_unique<DepthwiseConv2DPrinter>(); // There is no Option for DEQUANTIZE _op_map[tflite::BuiltinOperator_DIV] = make_unique<DivPrinter>(); + _op_map[tflite::BuiltinOperator_FAKE_QUANT] = make_unique<FakeQuantPrinter>(); // There is no Option for FLOOR // There is no Option for FLOOR_MOD _op_map[tflite::BuiltinOperator_FULLY_CONNECTED] = make_unique<FullyConnectedPrinter>(); @@ -689,7 +731,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[tflite::BuiltinOperator_L2_NORMALIZATION] = make_unique<L2NormPrinter>(); _op_map[tflite::BuiltinOperator_LEAKY_RELU] = make_unique<LeakyReluPrinter>(); _op_map[tflite::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION] = - make_unique<LocalResponseNormalizationPrinter>(); + make_unique<LocalResponseNormalizationPrinter>(); // There is no Option for LOG // There is no Option for LOGISTIC // There is no Option for LOG_SOFTMAX @@ -714,7 +756,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[tflite::BuiltinOperator_RESHAPE] = make_unique<ReshapePrinter>(); _op_map[tflite::BuiltinOperator_RESIZE_BILINEAR] = make_unique<ResizeBilinearPrinter>(); _op_map[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = - make_unique<ResizeNearestNeighborPrinter>(); + make_unique<ResizeNearestNeighborPrinter>(); _op_map[tflite::BuiltinOperator_REVERSE_SEQUENCE] = make_unique<ReverseSequencePrinter>(); // There is no Option for ROUND // There is no Option for SELECT @@ -735,7 +777,7 @@ OpPrinterRegistry::OpPrinterRegistry() _op_map[tflite::BuiltinOperator_TRANSPOSE_CONV] = make_unique<TransposeConvPrinter>(); // There is no Option for TOPK_V2 _op_map[tflite::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM] = - make_unique<UnidirectionalSequenceLSTMPrinter>(); + make_unique<UnidirectionalSequenceLSTMPrinter>(); _op_map[tflite::BuiltinOperator_UNIQUE] = make_unique<UniquePrinter>(); _op_map[tflite::BuiltinOperator_WHILE] = make_unique<WhilePrinter>(); _op_map[tflite::BuiltinOperator_CUSTOM] = make_unique<CustomOpPrinter>(); diff --git a/compiler/tflite2circle/CMakeLists.txt b/compiler/tflite2circle/CMakeLists.txt index b1d1f6149..3e46dd803 100644 --- a/compiler/tflite2circle/CMakeLists.txt +++ b/compiler/tflite2circle/CMakeLists.txt @@ -15,5 +15,6 @@ target_link_libraries(tflite2circle safemain) target_link_libraries(tflite2circle mio_tflite) target_link_libraries(tflite2circle mio_circle) target_link_libraries(tflite2circle vconone) +target_link_libraries(tflite2circle nncc_coverage) install(TARGETS tflite2circle DESTINATION bin) diff --git a/compiler/tflite2circle/driver/Driver.cpp b/compiler/tflite2circle/driver/Driver.cpp index 2f11e0a13..ba7892179 100644 --- a/compiler/tflite2circle/driver/Driver.cpp +++ b/compiler/tflite2circle/driver/Driver.cpp @@ -37,16 +37,16 @@ int entry(int argc, char **argv) arser::Arser arser{"tflite2circle is a Tensorflow lite to circle model converter"}; arser.add_argument("--version") - .nargs(0) - .required(false) - .default_value(false) - .help("Show version information and exit") - .exit_with(print_version); + .nargs(0) + .required(false) + .default_value(false) + .help("Show version information and exit") + .exit_with(print_version); arser.add_argument("tflite") - .nargs(1) - .type(arser::DataType::STR) - .help("Source tflite file path to convert"); + .nargs(1) + .type(arser::DataType::STR) + .help("Source tflite file path to convert"); arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Target circle file path"); try diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions.h index 56a16d4e0..dc6ff086c 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions.h +++ b/compiler/tflite2circle/src/BuildBuiltinOptions.h @@ -26,6 +26,7 @@ #include "BuildBuiltinOptions/ArgMinOptions.h" #include "BuildBuiltinOptions/BatchMatMulOptions.h" #include "BuildBuiltinOptions/BatchToSpaceNDOptions.h" +#include "BuildBuiltinOptions/BidirectionalSequenceLSTMOptions.h" #include "BuildBuiltinOptions/CastOptions.h" #include "BuildBuiltinOptions/ConcatenationOptions.h" #include "BuildBuiltinOptions/Conv2DOptions.h" @@ -36,6 +37,7 @@ #include "BuildBuiltinOptions/EqualOptions.h" #include "BuildBuiltinOptions/ExpandDimsOptions.h" #include "BuildBuiltinOptions/ExpOptions.h" +#include "BuildBuiltinOptions/FakeQuantOptions.h" #include "BuildBuiltinOptions/FillOptions.h" #include "BuildBuiltinOptions/FloorDivOptions.h" #include "BuildBuiltinOptions/FloorModOptions.h" diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp index f93a0f21f..5bdb1020a 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/AddOptions.cpp @@ -29,7 +29,7 @@ flatbuffers::Offset<circle::AddOptions> build_circle_AddOptions(flatbuffers::Fla assert(tflite_builtin_options); circle::AddOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp index 0ccdde4cb..ac0044a8f 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMaxOptions.cpp @@ -29,7 +29,7 @@ build_circle_ArgMaxOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Ope assert(tflite_builtin_options); circle::ArgMaxOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_output_type( - get_circle_tensortype(tflite_builtin_options->output_type())); + get_circle_tensortype(tflite_builtin_options->output_type())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp index 204558df8..3011c8b65 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ArgMinOptions.cpp @@ -29,7 +29,7 @@ build_circle_ArgMinOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Ope assert(tflite_builtin_options); circle::ArgMinOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_output_type( - get_circle_tensortype(tflite_builtin_options->output_type())); + get_circle_tensortype(tflite_builtin_options->output_type())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/BidirectionalSequenceLSTMOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/BidirectionalSequenceLSTMOptions.cpp new file mode 100644 index 000000000..2a6cf171b --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/BidirectionalSequenceLSTMOptions.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 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 "BidirectionalSequenceLSTMOptions.h" +#include "DataLookup.h" + +#include <cassert> + +namespace tflite2circle +{ + +flatbuffers::Offset<circle::BidirectionalSequenceLSTMOptions> +build_circle_BidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &fb, + const tflite::Operator *op) +{ + auto tflite_builtin_options = op->builtin_options_as_BidirectionalSequenceLSTMOptions(); + circle::BidirectionalSequenceLSTMOptionsBuilder builtin_options_builder{fb}; + builtin_options_builder.add_fused_activation_function( + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + builtin_options_builder.add_cell_clip(tflite_builtin_options->cell_clip()); + builtin_options_builder.add_proj_clip(tflite_builtin_options->proj_clip()); + builtin_options_builder.add_time_major(tflite_builtin_options->time_major()); + builtin_options_builder.add_merge_outputs(tflite_builtin_options->merge_outputs()); + builtin_options_builder.add_asymmetric_quantize_inputs( + tflite_builtin_options->asymmetric_quantize_inputs()); + return builtin_options_builder.Finish(); +} + +} // namespace tflite2circle diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/BidirectionalSequenceLSTMOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/BidirectionalSequenceLSTMOptions.h new file mode 100644 index 000000000..7b77b1cea --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/BidirectionalSequenceLSTMOptions.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 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 __BBO_BIDIRECTIONALSEQUENCE_LSTM_OPTIONS_H__ +#define __BBO_BIDIRECTIONALSEQUENCE_LSTM_OPTIONS_H__ + +#include <mio/tflite/schema_generated.h> +#include <mio/circle/schema_generated.h> + +namespace tflite2circle +{ + +flatbuffers::Offset<circle::BidirectionalSequenceLSTMOptions> +build_circle_BidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &fb, + const tflite::Operator *op); + +} // namespace tflite2circle + +#endif // __BBO_BIDIRECTIONALSEQUENCE_LSTM_OPTIONS_H__ diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp index bc1445248..0f2422c05 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/CastOptions.cpp @@ -31,9 +31,9 @@ build_circle_CastOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Opera circle::CastOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_in_data_type( - get_circle_tensortype(tflite_builtin_options->in_data_type())); + get_circle_tensortype(tflite_builtin_options->in_data_type())); builtin_options_builder.add_out_data_type( - get_circle_tensortype(tflite_builtin_options->out_data_type())); + get_circle_tensortype(tflite_builtin_options->out_data_type())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp index 933e7cf66..becc63bf6 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/ConcatenationOptions.cpp @@ -30,7 +30,7 @@ build_circle_ConcatenationOptions(flatbuffers::FlatBufferBuilder &fb, const tfli circle::ConcatenationOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_axis(tflite_builtin_options->axis()); builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp index ace63dd26..ec0cffeda 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/Conv2DOptions.cpp @@ -32,7 +32,7 @@ build_circle_Conv2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Ope builtin_options_builder.add_stride_w(tflite_builtin_options->stride_w()); builtin_options_builder.add_stride_h(tflite_builtin_options->stride_h()); builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); builtin_options_builder.add_dilation_w_factor(tflite_builtin_options->dilation_w_factor()); builtin_options_builder.add_dilation_h_factor(tflite_builtin_options->dilation_h_factor()); return builtin_options_builder.Finish(); diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp index 2aa35abc6..910a6ead9 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DepthwiseConv2DOptions.cpp @@ -33,7 +33,7 @@ build_circle_DepthwiseConv2DOptions(flatbuffers::FlatBufferBuilder &fb, const tf builtin_options_builder.add_stride_h(tflite_builtin_options->stride_h()); builtin_options_builder.add_depth_multiplier(tflite_builtin_options->depth_multiplier()); builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); builtin_options_builder.add_dilation_w_factor(tflite_builtin_options->dilation_w_factor()); builtin_options_builder.add_dilation_h_factor(tflite_builtin_options->dilation_h_factor()); return builtin_options_builder.Finish(); diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp index 4272fe144..3678928a5 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/DivOptions.cpp @@ -29,7 +29,7 @@ flatbuffers::Offset<circle::DivOptions> build_circle_DivOptions(flatbuffers::Fla assert(tflite_builtin_options); circle::DivOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FakeQuantOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/FakeQuantOptions.cpp new file mode 100644 index 000000000..e38600f82 --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FakeQuantOptions.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 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 "FillOptions.h" + +namespace tflite2circle +{ + +flatbuffers::Offset<circle::FakeQuantOptions> +build_circle_FakeQuantOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op) +{ + auto tflite_builtin_options = op->builtin_options_as_FakeQuantOptions(); + assert(tflite_builtin_options); + circle::FakeQuantOptionsBuilder builtin_options_builder{fb}; + builtin_options_builder.add_min(tflite_builtin_options->min()); + builtin_options_builder.add_max(tflite_builtin_options->max()); + builtin_options_builder.add_num_bits(tflite_builtin_options->num_bits()); + builtin_options_builder.add_narrow_range(tflite_builtin_options->narrow_range()); + return builtin_options_builder.Finish(); +} + +} // namespace tflite2circle diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FakeQuantOptions.h b/compiler/tflite2circle/src/BuildBuiltinOptions/FakeQuantOptions.h new file mode 100644 index 000000000..1f5f12b86 --- /dev/null +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FakeQuantOptions.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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 __BBO_FAKEQUANT_OPTIONS_H__ +#define __BBO_FAKEQUANT_OPTIONS_H__ + +#include <mio/tflite/schema_generated.h> +#include <mio/circle/schema_generated.h> + +namespace tflite2circle +{ + +flatbuffers::Offset<circle::FillOptions> +build_circle_FakeQuantOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op); + +} // namespace tflite2circle + +#endif // __BBO_FAKEQUANT_OPTIONS_H__ diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp index 098a96a40..2619b73eb 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/FullyConnectedOptions.cpp @@ -29,14 +29,14 @@ build_circle_FullyConnectedOptions(flatbuffers::FlatBufferBuilder &fb, const tfl assert(tflite_builtin_options); circle::FullyConnectedOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); // Get FullyConnectedOptionsWeightsFormat auto tflite_weight_format = tflite_builtin_options->weights_format(); if (tflite_weight_format == tflite::FullyConnectedOptionsWeightsFormat_DEFAULT) builtin_options_builder.add_weights_format(circle::FullyConnectedOptionsWeightsFormat_DEFAULT); else if (tflite_weight_format == tflite::FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8) builtin_options_builder.add_weights_format( - circle::FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8); + circle::FullyConnectedOptionsWeightsFormat_SHUFFLED4x16INT8); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp index d58aed83d..f5121a811 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/L2NormalizeOptions.cpp @@ -29,7 +29,7 @@ build_circle_L2NormOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Ope assert(tflite_builtin_options); circle::L2NormOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp index 009daea8b..3d4b9deb5 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/MulOptions.cpp @@ -29,7 +29,7 @@ flatbuffers::Offset<circle::MulOptions> build_circle_MulOptions(flatbuffers::Fla assert(tflite_builtin_options); circle::MulOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp index 6b0bd1288..d796eadfa 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/Pool2DOptions.cpp @@ -34,7 +34,7 @@ build_circle_Pool2DOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Ope builtin_options_builder.add_filter_width(tflite_builtin_options->filter_width()); builtin_options_builder.add_filter_height(tflite_builtin_options->filter_height()); builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp index 2e55f4dab..982f3fd68 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/SubOptions.cpp @@ -29,7 +29,7 @@ flatbuffers::Offset<circle::SubOptions> build_circle_SubOptions(flatbuffers::Fla assert(tflite_builtin_options); circle::SubOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp index 64ceb5a74..6e8143be9 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UnidirectionalSequenceLSTMOptions.cpp @@ -29,12 +29,12 @@ build_circle_UnidirectionalSequenceLSTMOptions(flatbuffers::FlatBufferBuilder &f auto tflite_builtin_options = op->builtin_options_as_UnidirectionalSequenceLSTMOptions(); circle::UnidirectionalSequenceLSTMOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_fused_activation_function( - get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); + get_circle_activation_function_type(tflite_builtin_options->fused_activation_function())); builtin_options_builder.add_cell_clip(tflite_builtin_options->cell_clip()); builtin_options_builder.add_proj_clip(tflite_builtin_options->proj_clip()); builtin_options_builder.add_time_major(tflite_builtin_options->time_major()); builtin_options_builder.add_asymmetric_quantize_inputs( - tflite_builtin_options->asymmetric_quantize_inputs()); + tflite_builtin_options->asymmetric_quantize_inputs()); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp b/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp index 96ddc15ad..f7ddeffcb 100644 --- a/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp +++ b/compiler/tflite2circle/src/BuildBuiltinOptions/UniqueOptions.cpp @@ -29,7 +29,7 @@ build_circle_UniqueOptions(flatbuffers::FlatBufferBuilder &fb, const tflite::Ope assert(tflite_builtin_options); circle::UniqueOptionsBuilder builtin_options_builder{fb}; builtin_options_builder.add_idx_out_type( - get_circle_tensortype(tflite_builtin_options->idx_out_type())); + get_circle_tensortype(tflite_builtin_options->idx_out_type())); return builtin_options_builder.Finish(); } diff --git a/compiler/tflite2circle/src/CircleModel.cpp b/compiler/tflite2circle/src/CircleModel.cpp index a95c37089..9ab884e75 100644 --- a/compiler/tflite2circle/src/CircleModel.cpp +++ b/compiler/tflite2circle/src/CircleModel.cpp @@ -126,13 +126,13 @@ Offset<SubGraphLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_fla flatbuffers::Offset<flatbuffers::Vector<int32_t>> traversal_order; flatbuffers::Offset<flatbuffers::Vector<int32_t>> block_map; flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<circle::DimensionMetadata>>> - dim_metadata; + dim_metadata; // traversal_order if (it->sparsity()->traversal_order()) { auto traversal_order_vec = std::vector<int32_t>{ - it->sparsity()->traversal_order()->begin(), it->sparsity()->traversal_order()->end()}; + it->sparsity()->traversal_order()->begin(), it->sparsity()->traversal_order()->end()}; traversal_order = fb->CreateVector(traversal_order_vec); } @@ -152,16 +152,16 @@ Offset<SubGraphLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_fla // array_segments auto tflite_array_segments_type = it->array_segments_type(); auto circle_array_segments = - get_circle_sparse_index_vector(*fb, it->array_segments(), tflite_array_segments_type); + get_circle_sparse_index_vector(*fb, it->array_segments(), tflite_array_segments_type); auto circle_array_segments_type = - get_circle_sparse_index_vector_type(tflite_array_segments_type); + get_circle_sparse_index_vector_type(tflite_array_segments_type); // array_indices auto tflite_array_indices_type = it->array_indices_type(); auto circle_array_indices = - get_circle_sparse_index_vector(*fb, it->array_indices(), tflite_array_indices_type); + get_circle_sparse_index_vector(*fb, it->array_indices(), tflite_array_indices_type); auto circle_array_indices_type = - get_circle_sparse_index_vector_type(tflite_array_indices_type); + get_circle_sparse_index_vector_type(tflite_array_indices_type); auto circle_dim_metadata_builder = circle::DimensionMetadataBuilder{*fb}; @@ -184,7 +184,7 @@ Offset<SubGraphLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite_fla if (it->shape_signature()) { auto shape_signature_vec = - std::vector<int32_t>({it->shape_signature()->begin(), it->shape_signature()->end()}); + std::vector<int32_t>({it->shape_signature()->begin(), it->shape_signature()->end()}); shape_signature = fb->CreateVector(shape_signature_vec); } @@ -297,7 +297,7 @@ Offset<OperatorCodeLink>::Offset(FlatBufBuilder &fb, const TFLFlatBufVec *tflite } CircleModel::CircleModel(FlatBufBuilder &fb, TFLModel &model) - : _version{0}, _description{fb->CreateString("nnpackage")}, _fb{fb} + : _version{0}, _description{fb->CreateString("nnpackage")}, _fb{fb} { const tflite::Model *tfl_model = model.load_model(); // verify flatbuffers @@ -309,11 +309,11 @@ CircleModel::CircleModel(FlatBufBuilder &fb, TFLModel &model) } _operator_codes_offset = - std::make_unique<Offset<OperatorCodeLink>>(fb, tfl_model->operator_codes()); + std::make_unique<Offset<OperatorCodeLink>>(fb, tfl_model->operator_codes()); _subGraphs_offset = std::make_unique<Offset<SubGraphLink>>(fb, tfl_model->subgraphs()); _buffers_offset = std::make_unique<Offset<BufferLink>>(fb, tfl_model->buffers()); _metadata_buffer_offset = - std::make_unique<Offset<MetaDataBufferLink>>(fb, tfl_model->metadata_buffer()); + std::make_unique<Offset<MetaDataBufferLink>>(fb, tfl_model->metadata_buffer()); model_build(); } diff --git a/compiler/tflite2circle/src/DataLookup.cpp b/compiler/tflite2circle/src/DataLookup.cpp index f8dd75f4c..c5ed62e31 100644 --- a/compiler/tflite2circle/src/DataLookup.cpp +++ b/compiler/tflite2circle/src/DataLookup.cpp @@ -148,7 +148,7 @@ get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *v { const tflite::Int32Vector *i32_array = static_cast<const tflite::Int32Vector *>(v_array); auto values_vec_int32 = - std::vector<int32_t>{i32_array->values()->begin(), i32_array->values()->end()}; + std::vector<int32_t>{i32_array->values()->begin(), i32_array->values()->end()}; auto values_int32 = fb.CreateVector(values_vec_int32); circle::Int32VectorBuilder int32_vector_builder{fb}; int32_vector_builder.add_values(values_int32); @@ -158,7 +158,7 @@ get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *v { const tflite::Uint16Vector *u16_array = static_cast<const tflite::Uint16Vector *>(v_array); auto values_vec_uint16 = - std::vector<uint16_t>{u16_array->values()->begin(), u16_array->values()->end()}; + std::vector<uint16_t>{u16_array->values()->begin(), u16_array->values()->end()}; auto values_uint16 = fb.CreateVector(values_vec_uint16); circle::Uint16VectorBuilder uint16_vector_builder{fb}; uint16_vector_builder.add_values(values_uint16); @@ -168,7 +168,7 @@ get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *v { const tflite::Uint8Vector *u8_array = static_cast<const tflite::Uint8Vector *>(v_array); auto values_vec_uint8 = - std::vector<uint8_t>{u8_array->values()->begin(), u8_array->values()->end()}; + std::vector<uint8_t>{u8_array->values()->begin(), u8_array->values()->end()}; auto values_uint8 = fb.CreateVector(values_vec_uint8); circle::Uint8VectorBuilder uint8_vector_builder{fb}; uint8_vector_builder.add_values(values_uint8); diff --git a/compiler/tflite2circle/src/DataLookup.h b/compiler/tflite2circle/src/DataLookup.h index 58a357703..601d014dd 100644 --- a/compiler/tflite2circle/src/DataLookup.h +++ b/compiler/tflite2circle/src/DataLookup.h @@ -27,19 +27,19 @@ namespace tflite2circle * @brief Returns circle builtin_code according to tflite. * * @note You can see a list of currently supported BuiltinOperator in TFLOperator.lst file. -*/ + */ circle::BuiltinOperator get_circle_builtin_code(tflite::BuiltinOperator tfl_bop); /** * @brief Returns circle TensorType according to tflite. * * @note You can see a list of currently supported TensorType in TFLTensorType.lst file. -*/ + */ circle::TensorType get_circle_tensortype(tflite::TensorType tfl_tt); /** * @brief Returns circle Padding enum according to tflite. -*/ + */ circle::Padding get_circle_padding(tflite::Padding tfl_p); /** @@ -47,7 +47,7 @@ circle::Padding get_circle_padding(tflite::Padding tfl_p); * * @note You can see a list of currently supported ActivationFunctionType in * TFLActivationFunctionType.lst file. -*/ + */ circle::ActivationFunctionType get_circle_activation_function_type(tflite::ActivationFunctionType tfl_aft); @@ -60,7 +60,7 @@ get_circle_activation_function_type(tflite::ActivationFunctionType tfl_aft); * This function calls the build_circle_##BuiltinOptions internally(e.g. * build_circle_AbsOptions, build_circle_AddOptions, etc.), so refer to it for a more * detailed implementation. -*/ + */ flatbuffers::Offset<void> get_circle_builtin_options(flatbuffers::FlatBufferBuilder &fb, const tflite::Operator *op); @@ -68,29 +68,29 @@ flatbuffers::Offset<void> get_circle_builtin_options(flatbuffers::FlatBufferBuil * @brief Returns circle builtin_options_type according to tflite. * * @note You can see a list of currently supported BuiltinOptions in TFLBuiltinOptions.lst file. -*/ + */ circle::BuiltinOptions get_circle_builtin_options_type(const tflite::Operator *op); /** * @brief Returns circle MirrorPadMode according to tflite. -*/ + */ circle::MirrorPadMode get_circle_mirrorpad_mode(tflite::MirrorPadMode tfl_mode); /** * @brief Returns circle DimensionType according to tflite. -*/ + */ circle::DimensionType get_circle_dimension_type(tflite::DimensionType tfl_dim_type); /** * @brief Returns circle SparseIndexVector according to tflite. -*/ + */ flatbuffers::Offset<void> get_circle_sparse_index_vector(flatbuffers::FlatBufferBuilder &fb, const void *values, const tflite::SparseIndexVector &tfl_sparse_index_vector_type); /** * @brief Returns circle SparseIndexVector type according to tflite. -*/ + */ circle::SparseIndexVector get_circle_sparse_index_vector_type(const tflite::SparseIndexVector &tfl_sparse_index_vector_type); diff --git a/compiler/tflite2circle/src/TFLBuiltinOptions.lst b/compiler/tflite2circle/src/TFLBuiltinOptions.lst index 4bc101f8e..f2de7e046 100644 --- a/compiler/tflite2circle/src/TFLBuiltinOptions.lst +++ b/compiler/tflite2circle/src/TFLBuiltinOptions.lst @@ -63,7 +63,7 @@ TFL_BUILTIN_OPTIONS(NotEqualOptions) TFL_BUILTIN_OPTIONS(ShapeOptions) TFL_BUILTIN_OPTIONS(PowOptions) TFL_BUILTIN_OPTIONS(ArgMinOptions) -//TFL_BUILTIN_OPTIONS(FakeQuantOptions) +TFL_BUILTIN_OPTIONS(FakeQuantOptions) TFL_BUILTIN_OPTIONS(PackOptions) TFL_BUILTIN_OPTIONS(LogicalOrOptions) TFL_BUILTIN_OPTIONS(OneHotOptions) @@ -74,7 +74,7 @@ TFL_BUILTIN_OPTIONS(FloorDivOptions) TFL_BUILTIN_OPTIONS(SquareOptions) TFL_BUILTIN_OPTIONS(ZerosLikeOptions) TFL_BUILTIN_OPTIONS(FillOptions) -//TFL_BUILTIN_OPTIONS(BidirectionalSequenceLSTMOptions) +TFL_BUILTIN_OPTIONS(BidirectionalSequenceLSTMOptions) //TFL_BUILTIN_OPTIONS(BidirectionalSequenceRNNOptions) TFL_BUILTIN_OPTIONS(UnidirectionalSequenceLSTMOptions) TFL_BUILTIN_OPTIONS(FloorModOptions) diff --git a/compiler/vconone/CMakeLists.txt b/compiler/vconone/CMakeLists.txt index 595bbfd99..882f6507b 100644 --- a/compiler/vconone/CMakeLists.txt +++ b/compiler/vconone/CMakeLists.txt @@ -1,5 +1,5 @@ if (NOT VCONONE_VERSION) - set(VCONONE_VERSION 0x00000000000c0001) + set(VCONONE_VERSION 0x00000000000f0001) # NOTE order is [build patch minor major] # if VCONONE_VERSION is set with -D option, it will be cached # you may have to remove cache file if you remove -D option diff --git a/compiler/vconone/src/version.cpp b/compiler/vconone/src/version.cpp index 9b693c621..d94a7ada6 100644 --- a/compiler/vconone/src/version.cpp +++ b/compiler/vconone/src/version.cpp @@ -54,7 +54,7 @@ std::string get_string(void) std::string get_copyright(void) { std::string str; - str = "Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved\r\n"; + str = "Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All Rights Reserved\r\n"; str += "Licensed under the Apache License, Version 2.0\r\n"; str += "https://github.com/Samsung/ONE"; return str; |