From 0513a79a79b6131020ace0380a0f585ba0b010c0 Mon Sep 17 00:00:00 2001 From: Evgenya Stepyreva Date: Fri, 27 Jan 2023 06:27:59 +0400 Subject: [PATCH] Tensorflow Lite frontend (#14977) * Infrastructure for tflite * Removed submodule flatbuffers * Added flatbuffers submodule. Fixed version to v22.12.06 aka acf39ff * Move headers back * Flatbuffers integration * Small fixes * Started parsing the Model * flatbuffer changes * decoder_flatbuffer changes * Lite Input Model -- not needed as of now but looks cool * Rolled back inherritance from ov::frontend::tensorflow::InputModel * Results are not treated as outputs, but its ok * Fix missplaced input vs output * Refactor * Load model op-by-op. Frontend API finalized * Debugging still, there are prints here and there. Decoder is not sane * Convolution with all attributes is translated and quantization is applied for inputs and constatants. TODO: quantize intermediate tensors, separate decoder specific logic? * Float ssd and posenet models are showing good accuracy * Need to refactor but work flawlessly * Telemetry and lightweight model cutting * Code style and test changes. Extensions supported * Quantization and style * Style refinements * Move onednn back * New portion of operations enabled * TFLite FE doesn't inherrit TF FE * Moved files to another directory * Rename header op_table.hpp to common_op_table.hpp for all files in src/frontends/tensorflow_common/src/op/ * Removed visability macroses * CMake changes * Unit-test execution in .ci * Update labeler.yml * Codeowners * Style check and fix * Static Build arrangement * Addressing the comments * install common headers to previous place * New approach with public decoder and graph_iterator * New approach with public decoder and graph_iterator * Move GraphIterator back * Comments addressed * Comments adressed * Preliminary TF FE README.md changes * Added target_compile_definitions OPENVINO_STATIC_LIBRARY for static build * Fixed conflicts and added TF to common places * Frontends use only openvino::core::dev API * Merged common tensorflow changes and made code build and work on selective number of models * Style * Rollback unnecessary changes from Tensorflow FE * Rollback unnecessary changes from Tensorflow Common * Minor refactor * cmake minor refactoring * Mixed commit * Style and merge fix * Low hanging fruit operations * Fix windows build * Refactor quantization parameters representation * license compliance. approved by OS PDT * copyrights in generic file * dependabot * labeler * Unit Test to be triggered in CI * cmake variables naming. corrected copyright years in copyrights/generic file * library renamed in .ci/ calls * Copyright year update * Set openvino-tf-frontend-maintainers as owner of /src/frontends/tensorflow_lite/ * Fixed flatc corss-compilation * Cleaned flatbuffers header usage * Nitpicks solved * Update cmake/templates/OpenVINOConfig.cmake.in * Compile with flatbuffers headers * Fixed "which is prefixed in the source directory" * Fixed typo in flatbuffers cmake * Removed flatbuffers submodule * Added fork submodule * Fixed static build * Fixed cross-compilatio * Fixed -Wshadow warning * Fixed warning on Windows * Use only headers from flatbuffers library * Added LTO and fixed compilation errors on Windows * Fixed warnings in tensorflow_common * Move ctors implementation to cpp file * Added information about new frontends to common FEm part * Temporaryily disable warnings * Fixed code style using clang-format * Fixed Windows * reverted changes in onnx * Revert changes in onnx_common * Removed pragma once frm cpp Co-authored-by: missjane Co-authored-by: Ilya Lavrenov --- .ci/azure/linux.yml | 3 + .ci/azure/linux_debian.yml | 6 + .ci/azure/mac.yml | 4 - .ci/azure/windows.yml | 3 + .github/CODEOWNERS | 1 + .github/dependabot.yml | 15 + .github/labeler.yml | 9 +- .gitmodules | 4 + .../IEDevScriptsConfig.cmake | 1 + cmake/developer_package/features.cmake | 2 +- .../frontends/frontends.cmake | 24 +- cmake/developer_package/native_compile.cmake | 100 ++ cmake/features.cmake | 4 + cmake/packaging/copyrights/generic | 5 + cmake/packaging/debian.cmake | 13 + cmake/packaging/rpm.cmake | 9 + cmake/templates/OpenVINOConfig.cmake.in | 9 + licensing/third-party-programs.txt | 207 +++ src/bindings/python/wheel/setup.py | 7 + src/cmake/openvino.cmake | 4 +- src/core/CMakeLists.txt | 13 +- src/core/builder/CMakeLists.txt | 2 +- src/frontends/CMakeLists.txt | 9 +- src/frontends/common/src/manager.cpp | 14 +- src/frontends/common/src/plugin_loader.cpp | 2 + src/frontends/onnx/frontend/CMakeLists.txt | 2 +- src/frontends/onnx/frontend/src/editor.cpp | 3 +- src/frontends/paddle/src/CMakeLists.txt | 3 +- src/frontends/pytorch/src/CMakeLists.txt | 2 +- src/frontends/tensorflow/src/CMakeLists.txt | 5 +- .../tests/test_models/gen_scripts/__init__.py | 3 + .../tests/test_models/gen_wrapper.py | 8 +- .../test_models/models_pbtxt/__init__.py | 3 + .../tensorflow_common/CMakeLists.txt | 2 +- .../tensorflow_common/include/place.hpp | 3 + .../tensorflow_common/src/CMakeLists.txt | 19 +- .../tensorflow_common/src/op/bias_add.cpp | 2 +- .../tensorflow_common/src/op/binary_op.cpp | 1 + .../tensorflow_common/src/op/einsum.cpp | 2 +- .../tensorflow_common/src/op/unary_op.cpp | 1 + .../src/pass/transpose_sinking.cpp | 2 +- .../tensorflow_common/tests/CMakeLists.txt | 2 +- src/frontends/tensorflow_lite/CMakeLists.txt | 9 + .../tensorflow_lite/extension/conversion.hpp | 37 + .../frontend/tensorflow_lite/extension/op.hpp | 18 + .../frontend/tensorflow_lite/frontend.hpp | 79 + .../frontend/tensorflow_lite/node_context.hpp | 67 + .../frontend/tensorflow_lite/visibility.hpp | 20 + .../tensorflow_lite/src/CMakeLists.txt | 12 + .../src/decoder_flatbuffer.cpp | 89 ++ .../tensorflow_lite/src/decoder_flatbuffer.h | 68 + .../tensorflow_lite/src/decoder_map.hpp | 87 ++ .../src/extension/conversion.cpp | 9 + .../tensorflow_lite/src/frontend.cpp | 297 ++++ .../src/graph_iterator_flatbuffer.cpp | 88 ++ .../src/graph_iterator_flatbuffer.hpp | 65 + .../tensorflow_lite/src/input_model.cpp | 389 +++++ .../tensorflow_lite/src/input_model.hpp | 56 + .../tensorflow_lite/src/op/batch_matmul.cpp | 28 + src/frontends/tensorflow_lite/src/op/cast.cpp | 27 + .../tensorflow_lite/src/op/concatenation.cpp | 26 + .../tensorflow_lite/src/op/conv2d.cpp | 34 + .../tensorflow_lite/src/op/depth_to_space.cpp | 27 + .../src/op/depthwise_conv2d.cpp | 34 + .../src/op/fully_connected.cpp | 39 + .../tensorflow_lite/src/op/gather.cpp | 30 + .../tensorflow_lite/src/op/leaky_relu.cpp | 27 + .../tensorflow_lite/src/op/mirror_pad.cpp | 27 + .../tensorflow_lite/src/op/one_hot.cpp | 27 + .../src/op/op_translation_utils.cpp | 155 ++ .../src/op/op_translation_utils.hpp | 117 ++ src/frontends/tensorflow_lite/src/op/pack.cpp | 27 + .../tensorflow_lite/src/op/pool2d.cpp | 43 + .../tensorflow_lite/src/op/range.cpp | 26 + .../tensorflow_lite/src/op/reshape.cpp | 41 + .../tensorflow_lite/src/op/resize.cpp | 38 + .../src/op/reverse_sequence.cpp | 29 + .../tensorflow_lite/src/op/shape.cpp | 27 + .../tensorflow_lite/src/op/softmax.cpp | 33 + .../tensorflow_lite/src/op/space_to_depth.cpp | 27 + .../tensorflow_lite/src/op/split.cpp | 26 + .../tensorflow_lite/src/op/squeeze.cpp | 26 + .../tensorflow_lite/src/op/strided_slice.cpp | 32 + .../tensorflow_lite/src/op/unique.cpp | 27 + .../tensorflow_lite/src/op/unpack.cpp | 28 + .../tensorflow_lite/src/op_table.cpp | 194 +++ .../tensorflow_lite/src/op_table.hpp | 63 + .../tensorflow_lite/src/quantization_info.hpp | 69 + .../tensorflow_lite/src/schema/schema.fbs | 1333 +++++++++++++++++ .../tensorflow_lite/src/tensor_lite_place.cpp | 14 + .../tensorflow_lite/src/tensor_lite_place.hpp | 70 + .../tensorflow_lite/src/tensorflow_lite.cpp | 20 + src/frontends/tensorflow_lite/src/utils.cpp | 150 ++ src/frontends/tensorflow_lite/src/utils.hpp | 25 + .../tensorflow_lite/tests/CMakeLists.txt | 75 + .../tensorflow_lite/tests/basic_api.cpp | 23 + .../tensorflow_lite/tests/conversion.cpp | 53 + .../tensorflow_lite/tests/convert_model.cpp | 23 + .../tests/library_extension.cpp | 24 + .../tensorflow_lite/tests/op_extension.cpp | 141 ++ .../tensorflow_lite/tests/requirements.txt | 2 + .../tests/standalone_build/CMakeLists.txt | 13 + .../standalone_build_test.cpp | 10 + .../tensorflow_lite/tests/telemetry.cpp | 31 + .../gen_scripts/generate_2in_2out.py | 52 + .../tests/test_models/gen_wrapper.py | 21 + .../tensorflow_lite/tests/tf_utils.hpp | 9 + .../tests/frontend/shared/src/conversion.cpp | 9 + .../frontend/shared/src/convert_model.cpp | 5 +- .../test_builtin_extensions/CMakeLists.txt | 5 + .../builtin_extensions.cpp | 12 +- .../shape_inference_test/compare_test.cpp | 2 - .../layer_tests_summary/run_parallel.py | 2 +- thirdparty/CMakeLists.txt | 28 +- thirdparty/flatbuffers/CMakeLists.txt | 61 + thirdparty/flatbuffers/flatbuffers | 1 + thirdparty/protobuf/CMakeLists.txt | 93 +- tools/mo/CMakeLists.txt | 2 +- 118 files changed, 5445 insertions(+), 141 deletions(-) create mode 100644 cmake/developer_package/native_compile.cmake create mode 100644 src/frontends/tensorflow/tests/test_models/gen_scripts/__init__.py create mode 100644 src/frontends/tensorflow/tests/test_models/models_pbtxt/__init__.py create mode 100644 src/frontends/tensorflow_lite/CMakeLists.txt create mode 100644 src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/conversion.hpp create mode 100644 src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/op.hpp create mode 100644 src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/frontend.hpp create mode 100644 src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/node_context.hpp create mode 100644 src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/visibility.hpp create mode 100644 src/frontends/tensorflow_lite/src/CMakeLists.txt create mode 100644 src/frontends/tensorflow_lite/src/decoder_flatbuffer.cpp create mode 100644 src/frontends/tensorflow_lite/src/decoder_flatbuffer.h create mode 100644 src/frontends/tensorflow_lite/src/decoder_map.hpp create mode 100644 src/frontends/tensorflow_lite/src/extension/conversion.cpp create mode 100644 src/frontends/tensorflow_lite/src/frontend.cpp create mode 100644 src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.cpp create mode 100644 src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.hpp create mode 100644 src/frontends/tensorflow_lite/src/input_model.cpp create mode 100644 src/frontends/tensorflow_lite/src/input_model.hpp create mode 100644 src/frontends/tensorflow_lite/src/op/batch_matmul.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/cast.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/concatenation.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/conv2d.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/depth_to_space.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/depthwise_conv2d.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/fully_connected.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/gather.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/leaky_relu.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/mirror_pad.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/one_hot.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/op_translation_utils.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/op_translation_utils.hpp create mode 100644 src/frontends/tensorflow_lite/src/op/pack.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/pool2d.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/range.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/reshape.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/resize.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/reverse_sequence.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/shape.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/softmax.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/space_to_depth.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/split.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/squeeze.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/strided_slice.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/unique.cpp create mode 100644 src/frontends/tensorflow_lite/src/op/unpack.cpp create mode 100644 src/frontends/tensorflow_lite/src/op_table.cpp create mode 100644 src/frontends/tensorflow_lite/src/op_table.hpp create mode 100644 src/frontends/tensorflow_lite/src/quantization_info.hpp create mode 100644 src/frontends/tensorflow_lite/src/schema/schema.fbs create mode 100644 src/frontends/tensorflow_lite/src/tensor_lite_place.cpp create mode 100644 src/frontends/tensorflow_lite/src/tensor_lite_place.hpp create mode 100644 src/frontends/tensorflow_lite/src/tensorflow_lite.cpp create mode 100644 src/frontends/tensorflow_lite/src/utils.cpp create mode 100644 src/frontends/tensorflow_lite/src/utils.hpp create mode 100644 src/frontends/tensorflow_lite/tests/CMakeLists.txt create mode 100644 src/frontends/tensorflow_lite/tests/basic_api.cpp create mode 100644 src/frontends/tensorflow_lite/tests/conversion.cpp create mode 100644 src/frontends/tensorflow_lite/tests/convert_model.cpp create mode 100644 src/frontends/tensorflow_lite/tests/library_extension.cpp create mode 100644 src/frontends/tensorflow_lite/tests/op_extension.cpp create mode 100644 src/frontends/tensorflow_lite/tests/requirements.txt create mode 100644 src/frontends/tensorflow_lite/tests/standalone_build/CMakeLists.txt create mode 100644 src/frontends/tensorflow_lite/tests/standalone_build/standalone_build_test.cpp create mode 100644 src/frontends/tensorflow_lite/tests/telemetry.cpp create mode 100644 src/frontends/tensorflow_lite/tests/test_models/gen_scripts/generate_2in_2out.py create mode 100644 src/frontends/tensorflow_lite/tests/test_models/gen_wrapper.py create mode 100644 src/frontends/tensorflow_lite/tests/tf_utils.hpp create mode 100644 thirdparty/flatbuffers/CMakeLists.txt create mode 160000 thirdparty/flatbuffers/flatbuffers diff --git a/.ci/azure/linux.yml b/.ci/azure/linux.yml index 9b4a2325b66..c18d79d0cb2 100644 --- a/.ci/azure/linux.yml +++ b/.ci/azure/linux.yml @@ -397,6 +397,9 @@ jobs: - script: $(RUN_PREFIX) $(INSTALL_TEST_DIR)/ov_tensorflow_common_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-TensorflowCommon.xml displayName: 'TensorFlow Common Unit Tests' + - script: $(RUN_PREFIX) $(INSTALL_TEST_DIR)/ov_tensorflow_lite_frontend_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-TensorflowLite.xml + displayName: 'TensorFlow Lite Frontend Unit Tests' + - script: $(RUN_PREFIX) $(INSTALL_TEST_DIR)/ov_lp_transformations_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-LpTransformations.xml displayName: 'Low Precision Transformations Tests' diff --git a/.ci/azure/linux_debian.yml b/.ci/azure/linux_debian.yml index 2f0bab0600d..f472f6e247a 100644 --- a/.ci/azure/linux_debian.yml +++ b/.ci/azure/linux_debian.yml @@ -331,6 +331,12 @@ jobs: LD_LIBRARY_PATH: $(INSTALL_TEST_DIR) displayName: 'TensorFlow Common Unit Tests' + - script: | + $(INSTALL_TEST_DIR)/ov_tensorflow_lite_frontend_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-TensorflowLite.xml + env: + LD_LIBRARY_PATH: $(INSTALL_TEST_DIR) + displayName: 'TensorFlow Lite Frontend Unit Tests' + - script: $(INSTALL_TEST_DIR)/ov_cpu_unit_tests --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-ov_cpu_unit_tests.xml displayName: 'Intel CPU Unit Tests' diff --git a/.ci/azure/mac.yml b/.ci/azure/mac.yml index 8cadf9965cc..6e74f12becf 100644 --- a/.ci/azure/mac.yml +++ b/.ci/azure/mac.yml @@ -201,10 +201,6 @@ jobs: displayName: 'ONNX Frontend Tests' enabled: 'false' - - script: . $(SETUPVARS) && $(INSTALL_TEST_DIR)/InferenceEngineUnitTests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-InferenceEngineUnitTests.xml - displayName: 'IE UT old' - enabled: 'false' - - script: . $(SETUPVARS) && $(INSTALL_TEST_DIR)/ov_cpu_unit_tests --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-ov_cpu_unit_tests.xml displayName: 'Intel CPU Unit Tests' enabled: 'false' diff --git a/.ci/azure/windows.yml b/.ci/azure/windows.yml index 0ce24827ee6..9f8bb0a8397 100644 --- a/.ci/azure/windows.yml +++ b/.ci/azure/windows.yml @@ -281,6 +281,9 @@ jobs: - script: call $(SETUPVARS) && $(INSTALL_TEST_DIR)\ov_tensorflow_common_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)\TEST-TensorflowCommon.xml displayName: 'TensorFlow Common Unit Tests' + - script: call $(SETUPVARS) && $(INSTALL_TEST_DIR)\ov_tensorflow_lite_frontend_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)\TEST-TensorflowLite.xml + displayName: 'TensorFlow Lite Frontend Unit Tests' + - script: call $(SETUPVARS) && $(INSTALL_TEST_DIR)\ov_lp_transformations_tests --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)\LpTransformations.xml displayName: 'Low Precision Transformations Tests' diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 05f1d38a8d1..4ff308d491e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -79,6 +79,7 @@ /src/frontends/paddle/ @openvinotoolkit/openvino-ie-paddle-maintainers /src/frontends/tensorflow/ @openvinotoolkit/openvino-tf-frontend-maintainers /src/frontends/tensorflow_common/ @openvinotoolkit/openvino-tf-frontend-maintainers +/src/frontends/tensorflow_lite/ @openvinotoolkit/openvino-tf-frontend-maintainers /src/frontends/pytorch/ @openvinotoolkit/openvino-pytorch-frontend-maintainers # OpenVINO ONNX Frontend: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f3b9703c67f..137d5131cdc 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -119,6 +119,21 @@ updates: - "p-wysocki" versioning-strategy: increase-if-necessary + # TensorFlow Lite FE tests requirements + - package-ecosystem: pip + directory: "/src/frontends/tensorflow_lite/tests/" + schedule: + interval: "daily" + time: "09:00" + timezone: "Asia/Dubai" + open-pull-requests-limit: 3 + assignees: + - "jane-intel" + - "rkazants" + - "jiwaszki" + - "p-wysocki" + versioning-strategy: increase-if-necessary + # # Python Samples # diff --git a/.github/labeler.yml b/.github/labeler.yml index 82c19729ca5..c4512c375e7 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -27,9 +27,11 @@ 'category: CPP API': - 'src/inference/include/**/*' - 'src/core/include/**/*' -- 'src/frontends/common/**/*' +- 'src/frontends/common/include/**/*' - 'src/frontends/onnx/frontend/include/**/*' - 'src/frontends/tensorflow/include/**/*' +- 'src/frontends/tensorflow_lite/include/**/*' +- 'src/frontends/pytorch/include/**/*' - 'src/frontends/paddle/include/**/*' 'category: CPU': @@ -121,6 +123,11 @@ - 'src/frontends/tensorflow_common/**/*' - 'tests/layer_tests/tensorflow_tests/**/*' +'category: TFL FE': +- 'src/frontends/tensorflow_lite/**/*' +- 'src/frontends/tensorflow_common/**/*' +- 'tests/layer_tests/tensorflow_lite_tests/**/*' + 'category: PyTorch FE': - 'src/frontends/pytorch/**/*' - 'tests/layer_tests/pytorch_tests/**/*' diff --git a/.gitmodules b/.gitmodules index 213ec4d9224..43382057b0d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -63,3 +63,7 @@ path = thirdparty/json/nlohmann_json url = https://github.com/nlohmann/json.git shallow = true +[submodule "thirdparty/flatbuffers/flatbuffers"] + path = thirdparty/flatbuffers/flatbuffers + url = https://github.com/ilya-lavrenov/flatbuffers.git + branch = cmake-3-13-fixes diff --git a/cmake/developer_package/IEDevScriptsConfig.cmake b/cmake/developer_package/IEDevScriptsConfig.cmake index 59793b8ebaa..6e43c573652 100644 --- a/cmake/developer_package/IEDevScriptsConfig.cmake +++ b/cmake/developer_package/IEDevScriptsConfig.cmake @@ -310,6 +310,7 @@ function(ov_mark_target_as_cc) endfunction() include(python_requirements) +include(native_compile) # Code style utils diff --git a/cmake/developer_package/features.cmake b/cmake/developer_package/features.cmake index 66d0f755afb..d88532f8bf3 100644 --- a/cmake/developer_package/features.cmake +++ b/cmake/developer_package/features.cmake @@ -38,7 +38,7 @@ ie_option (ENABLE_UB_SANITIZER "enable UndefinedBahavior sanitizer" OFF) ie_option (ENABLE_THREAD_SANITIZER "enable checking data races via ThreadSanitizer" OFF) -ie_dependent_option (ENABLE_COVERAGE "enable code coverage" OFF "CMAKE_CXX_COMPILER_ID STREQUAL GNU OR OV_COMPILER_IS_CLANG" OFF) +ie_dependent_option (ENABLE_COVERAGE "enable code coverage" OFF "CMAKE_COMPILER_IS_GNUCXX OR OV_COMPILER_IS_CLANG" OFF) # Defines CPU capabilities diff --git a/cmake/developer_package/frontends/frontends.cmake b/cmake/developer_package/frontends/frontends.cmake index 0fbc3d211cd..ad78058f6a0 100644 --- a/cmake/developer_package/frontends/frontends.cmake +++ b/cmake/developer_package/frontends/frontends.cmake @@ -155,11 +155,27 @@ macro(ov_add_frontend) list(APPEND PROTO_HDRS "${OUTPUT_PB_HEADER}") endforeach() + file(GLOB flatbuffers_schema_files ${frontend_root_dir}/src/schema/*.fbs) + foreach(INFILE IN LISTS flatbuffers_schema_files) + get_filename_component(FILE_WE ${INFILE} NAME_WE) + set(OUTPUT_FC_HEADER ${CMAKE_CURRENT_BINARY_DIR}/${FILE_WE}_generated.h) + set(GENERATED_PROTO ${INFILE}) + add_custom_command( + OUTPUT "${OUTPUT_FC_HEADER}" + COMMAND ${flatbuffers_COMPILER} ARGS -c --gen-mutable -o ${CMAKE_CURRENT_BINARY_DIR} ${INFILE} + DEPENDS ${flatbuffers_DEPENDENCY} ${GENERATED_PROTO} + COMMENT "Running C++ flatbuffers compiler (${flatbuffers_COMPILER}) on ${GENERATED_PROTO}" + VERBATIM + COMMAND_EXPAND_LISTS) + list(APPEND PROTO_HDRS "${OUTPUT_FC_HEADER}") + endforeach() + # Disable all warnings for generated code set_source_files_properties(${PROTO_SRCS} ${PROTO_HDRS} PROPERTIES COMPILE_OPTIONS -w GENERATED TRUE) # Create library - add_library(${TARGET_NAME} ${LIBRARY_SRC} ${LIBRARY_HEADERS} ${LIBRARY_PUBLIC_HEADERS} ${PROTO_SRCS} ${PROTO_HDRS}) + add_library(${TARGET_NAME} ${LIBRARY_SRC} ${LIBRARY_HEADERS} ${LIBRARY_PUBLIC_HEADERS} + ${PROTO_SRCS} ${PROTO_HDRS} ${flatbuffers_schema_files} ${proto_files}) if(OV_FRONTEND_LINKABLE_FRONTEND) # create beautiful alias @@ -234,8 +250,12 @@ macro(ov_add_frontend) endif() endif() + if(flatbuffers_schema_files) + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${flatbuffers_INCLUDE_DIRECTORIES}) + endif() + add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME} - EXCLUDE_PATTERNS ${PROTO_SRCS} ${PROTO_HDRS}) + EXCLUDE_PATTERNS ${PROTO_SRCS} ${PROTO_HDRS} ${flatbuffers_schema_files}) add_dependencies(ov_frontends ${TARGET_NAME}) diff --git a/cmake/developer_package/native_compile.cmake b/cmake/developer_package/native_compile.cmake new file mode 100644 index 00000000000..5024eed1146 --- /dev/null +++ b/cmake/developer_package/native_compile.cmake @@ -0,0 +1,100 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +include(ExternalProject) + +# +# ov_native_compile_external_project( +# TARGET_NAME +# NATIVE_INSTALL_DIR +# NATIVE_TARGETS +# [NATIVE_SOURCE_SUBDIR ] +# [CMAKE_ARGS ] +# ) +# +function(ov_native_compile_external_project) + set(oneValueRequiredArgs NATIVE_INSTALL_DIR TARGET_NAME NATIVE_SOURCE_SUBDIR) + set(multiValueArgs CMAKE_ARGS NATIVE_TARGETS) + cmake_parse_arguments(ARG "" "${oneValueRequiredArgs};${oneValueOptionalArgs}" "${multiValueArgs}" ${ARGN}) + + if(YOCTO_AARCH64) + # need to unset several variables which can set env to cross-environment + foreach(var SDKTARGETSYSROOT CONFIG_SITE OECORE_NATIVE_SYSROOT OECORE_TARGET_SYSROOT + OECORE_ACLOCAL_OPTS OECORE_BASELIB OECORE_TARGET_ARCH OECORE_TARGET_OS CC CXX + CPP AS LD GDB STRIP RANLIB OBJCOPY OBJDUMP READELF AR NM M4 TARGET_PREFIX + CONFIGURE_FLAGS CFLAGS CXXFLAGS LDFLAGS CPPFLAGS KCFLAGS OECORE_DISTRO_VERSION + OECORE_SDK_VERSION ARCH CROSS_COMPILE OE_CMAKE_TOOLCHAIN_FILE OPENSSL_CONF + OE_CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX PKG_CONFIG_SYSROOT_DIR PKG_CONFIG_PATH) + if(DEFINED ENV{${var}}) + list(APPEND cmake_env --unset=${var}) + endif() + endforeach() + + # filter out PATH from yocto locations + string(REPLACE ":" ";" custom_path "$ENV{PATH}") + foreach(path IN LISTS custom_path) + if(NOT path MATCHES "^$ENV{OECORE_NATIVE_SYSROOT}") + list(APPEND clean_path "${path}") + endif() + endforeach() + + find_host_program(NATIVE_CMAKE_COMMAND + NAMES cmake + PATHS ${clean_path} + DOC "Host cmake" + REQUIRED + NO_DEFAULT_PATH) + else() + set(NATIVE_CMAKE_COMMAND "${CMAKE_COMMAND}") + endif() + + # if env has CMAKE_TOOLCHAIN_FILE, we need to skip it + if(DEFINED ENV{CMAKE_TOOLCHAIN_FILE}) + list(APPEND cmake_env --unset=CMAKE_TOOLCHAIN_FILE) + endif() + + # compile flags + if(CMAKE_COMPILER_IS_GNUCXX) + set(compile_flags "-Wno-undef -Wno-error -Wno-deprecated-declarations") + endif() + + if(ARG_NATIVE_SOURCE_SUBDIR) + set(ARG_NATIVE_SOURCE_SUBDIR SOURCE_SUBDIR ${ARG_NATIVE_SOURCE_SUBDIR}) + endif() + + ExternalProject_Add(${ARG_TARGET_NAME} + # Directory Options + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" + PREFIX "${CMAKE_CURRENT_BINARY_DIR}" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + INSTALL_DIR "${ARG_NATIVE_INSTALL_DIR}" + # Configure Step Options: + CMAKE_COMMAND + ${NATIVE_CMAKE_COMMAND} + CMAKE_ARGS + "-DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}" + "-DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}" + "-DCMAKE_CXX_LINKER_LAUNCHER=${CMAKE_CXX_LINKER_LAUNCHER}" + "-DCMAKE_C_LINKER_LAUNCHER=${CMAKE_C_LINKER_LAUNCHER}" + "-DCMAKE_CXX_FLAGS=${compile_flags}" + "-DCMAKE_C_FLAGS=${compile_flags}" + "-DCMAKE_POLICY_DEFAULT_CMP0069=NEW" + "-DCMAKE_INSTALL_PREFIX=${ARG_NATIVE_INSTALL_DIR}" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + ${ARG_CMAKE_ARGS} + CMAKE_GENERATOR "${CMAKE_GENERATOR}" + ${ARG_NATIVE_SOURCE_SUBDIR} + # Build Step Options: + BUILD_COMMAND + ${NATIVE_CMAKE_COMMAND} + --build "${CMAKE_CURRENT_BINARY_DIR}/build" + --config Release + --parallel + -- ${ARG_NATIVE_TARGETS} + # Test Step Options: + TEST_EXCLUDE_FROM_MAIN ON + # Target Options: + EXCLUDE_FROM_ALL ON + ) +endfunction() diff --git a/cmake/features.cmake b/cmake/features.cmake index 6bc2b2c36c1..c1466f36167 100644 --- a/cmake/features.cmake +++ b/cmake/features.cmake @@ -149,11 +149,15 @@ ie_dependent_option (ENABLE_CPU_DEBUG_CAPS "enable CPU debug capabilities at run find_host_package(PythonInterp 3 QUIET) ie_option(ENABLE_OV_ONNX_FRONTEND "Enable ONNX FrontEnd" ${PYTHONINTERP_FOUND}) ie_option(ENABLE_OV_PADDLE_FRONTEND "Enable PaddlePaddle FrontEnd" ON) +ie_option(ENABLE_OV_IR_FRONTEND "Enable IR FrontEnd" ON) ie_option(ENABLE_OV_PYTORCH_FRONTEND "Enable PyTorch FrontEnd" ON) ie_option(ENABLE_OV_TF_FRONTEND "Enable TensorFlow FrontEnd" ON) +ie_option(ENABLE_OV_TF_LITE_FRONTEND "Enable TensorFlow Lite FrontEnd" ON) ie_dependent_option(ENABLE_SYSTEM_PROTOBUF "Use system protobuf" OFF "ENABLE_OV_ONNX_FRONTEND OR ENABLE_OV_PADDLE_FRONTEND OR ENABLE_OV_TF_FRONTEND;BUILD_SHARED_LIBS" OFF) ie_option(ENABLE_OV_IR_FRONTEND "Enable IR FrontEnd" ON) +ie_dependent_option(ENABLE_SYSTEM_FLATBUFFERS "Use system flatbuffers" OFF + "ENABLE_OV_TF_LITE_FRONTEND;BUILD_SHARED_LIBS" OFF) ie_dependent_option(ENABLE_OV_CORE_UNIT_TESTS "Enables OpenVINO core unit tests" ON "ENABLE_TESTS" OFF) ie_option(ENABLE_OPENVINO_DEBUG "Enable output for OPENVINO_DEBUG statements" OFF) diff --git a/cmake/packaging/copyrights/generic b/cmake/packaging/copyrights/generic index d960c4117e6..8c09d518f8e 100644 --- a/cmake/packaging/copyrights/generic +++ b/cmake/packaging/copyrights/generic @@ -760,3 +760,8 @@ Files: \thirdparty\* Comment: onnx Copyright: authors License: Apache + +Files: * +Comment: FlatBuffers +Copyright: Copyright 2014-2022 Google Inc. All rights reserved. +License: Apache diff --git a/cmake/packaging/debian.cmake b/cmake/packaging/debian.cmake index dea5d149cf1..391e6f77e54 100644 --- a/cmake/packaging/debian.cmake +++ b/cmake/packaging/debian.cmake @@ -274,6 +274,19 @@ macro(ov_cpack_settings) set(pytorch_copyright "generic") endif() + if(ENABLE_OV_TF_LITE_FRONTEND) + set(CPACK_COMPONENT_TENSORFLOW_LITE_DESCRIPTION "OpenVINO TensorFlow Lite Frontend") + set(CPACK_COMPONENT_TENSORFLOW_LITE_DEPENDS "${OV_CPACK_COMP_CORE}") + set(CPACK_DEBIAN_TENSORFLOW_LITE_PACKAGE_NAME "libopenvino-tensorflow-lite-frontend-${cpack_name_ver}") + # since we TF Lite FE is linkable target, we need to call ldconfig (i.e. `def_triggers`) + set(CPACK_DEBIAN_TENSORFLOW_LITE_PACKAGE_CONTROL_EXTRA "${def_postinst};${def_postrm};${def_triggers}") + ov_debian_add_lintian_suppression(tensorflow_lite + # we have different package name strategy; it suggests libopenvino-tensorflow-lite-frontend202230 + "package-name-doesnt-match-sonames") + list(APPEND frontends tensorflow_lite) + set(tensorflow_lite_copyright "generic") + endif() + # # core_dev: depends on core and frontends (since frontends don't want to provide its own dev packages) # diff --git a/cmake/packaging/rpm.cmake b/cmake/packaging/rpm.cmake index 5c1b29f605e..4681f7a3f97 100644 --- a/cmake/packaging/rpm.cmake +++ b/cmake/packaging/rpm.cmake @@ -235,6 +235,15 @@ macro(ov_cpack_settings) set(pytorch_copyright "generic") endif() + if(ENABLE_OV_TF_LITE_FRONTEND) + set(CPACK_COMPONENT_TENSORFLOW_LITE_DESCRIPTION "OpenVINO TensorFlow Lite Frontend") + set(CPACK_RPM_TENSORFLOW_LITE_PACKAGE_NAME "libopenvino-tensorflow-lite-frontend-${cpack_name_ver}") + set(CPACK_RPM_TENSORFLOW_LITE_POST_INSTALL_SCRIPT_FILE "${def_triggers}") + set(CPACK_RPM_TENSORFLOW_LITE_POST_UNINSTALL_SCRIPT_FILE "${def_triggers}") + _ov_add_package(frontend_packages tensorflow_lite) + set(tensorflow_lite_copyright "generic") + endif() + # # core_dev: depends on core and frontends (since frontends don't want to provide its own dev packages) # diff --git a/cmake/templates/OpenVINOConfig.cmake.in b/cmake/templates/OpenVINOConfig.cmake.in index 023704f6267..cf1f321ae35 100644 --- a/cmake/templates/OpenVINOConfig.cmake.in +++ b/cmake/templates/OpenVINOConfig.cmake.in @@ -14,6 +14,7 @@ # * `Paddle`: OpenVINO Paddle frontend # * `PyTorch`: OpenVINO PyTorch frontend # * `TensorFlow`: OpenVINO TensorFlow frontend +# * `TensorFlowLite`: OpenVINO TensorFlow Lite frontend # # If no components are specified, `Runtime` component is provided: # @@ -48,6 +49,9 @@ # `openvino::frontend::tensorflow` # TensorFlow FrontEnd target (optional) # +# `openvino::frontend::tensorflow_lite` +# TensorFlow Lite FrontEnd target (optional) +# # Result variables: # ------ # @@ -71,6 +75,9 @@ # `OpenVINO_Frontend_TensorFlow_FOUND` # OpenVINO TensorFlow frontend is available # +# `OpenVINO_Frontend_TensorFlowLite_FOUND` +# OpenVINO TensorFlow Lite frontend is available +# # `OpenVINO_Frontend_IR_FOUND` # OpenVINO IR frontend is available # @@ -299,12 +306,14 @@ set(${CMAKE_FIND_PACKAGE_NAME}_Runtime_FOUND ON) set(${CMAKE_FIND_PACKAGE_NAME}_ONNX_FOUND @ENABLE_OV_ONNX_FRONTEND@) set(${CMAKE_FIND_PACKAGE_NAME}_Paddle_FOUND @ENABLE_OV_PADDLE_FRONTEND@) set(${CMAKE_FIND_PACKAGE_NAME}_TensorFlow_FOUND @ENABLE_OV_TF_FRONTEND@) +set(${CMAKE_FIND_PACKAGE_NAME}_TensorFlowLite_FOUND @ENABLE_OV_TF_LITE_FRONTEND@) set(${CMAKE_FIND_PACKAGE_NAME}_IR_FOUND @ENABLE_OV_IR_FRONTEND@) set(${CMAKE_FIND_PACKAGE_NAME}_PyTorch_FOUND @ENABLE_OV_PYTORCH_FRONTEND@) set(${CMAKE_FIND_PACKAGE_NAME}_Frontend_ONNX_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_ONNX_FOUND}) set(${CMAKE_FIND_PACKAGE_NAME}_Frontend_Paddle_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_Paddle_FOUND}) set(${CMAKE_FIND_PACKAGE_NAME}_Frontend_TensorFlow_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_TensorFlow_FOUND}) +set(${CMAKE_FIND_PACKAGE_NAME}_Frontend_TensorFlowLite_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_TensorFlowLite_FOUND}) set(${CMAKE_FIND_PACKAGE_NAME}_Frontend_IR_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_IR_FOUND}) set(${CMAKE_FIND_PACKAGE_NAME}_Frontend_PyTorch_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_PyTorch_FOUND}) diff --git a/licensing/third-party-programs.txt b/licensing/third-party-programs.txt index afe5e6e1fe2..dc5c2ae4d70 100644 --- a/licensing/third-party-programs.txt +++ b/licensing/third-party-programs.txt @@ -1340,3 +1340,210 @@ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------- + +27. flatbuffers (https://github.com/google/flatbuffers/) + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/src/bindings/python/wheel/setup.py b/src/bindings/python/wheel/setup.py index 43522e224a7..9c54423ef72 100644 --- a/src/bindings/python/wheel/setup.py +++ b/src/bindings/python/wheel/setup.py @@ -140,6 +140,13 @@ LIB_INSTALL_CFG = { "rpath": LIBS_RPATH, "binary_dir": OPENVINO_BUILD_DIR, }, + "tensorflow_lite_libs": { + "name": "tensorflow_lite", + "prefix": "libs.tensorflow_lite", + "install_dir": OV_RUNTIME_LIBS_DIR, + "rpath": LIBS_RPATH, + "binary_dir": OPENVINO_BUILD_DIR, + }, } PY_INSTALL_CFG = { diff --git a/src/cmake/openvino.cmake b/src/cmake/openvino.cmake index 5667f33f0b6..a3477788c15 100644 --- a/src/cmake/openvino.cmake +++ b/src/cmake/openvino.cmake @@ -93,8 +93,6 @@ add_library(${TARGET_NAME}_dev INTERFACE) add_library(openvino::runtime::dev ALIAS ${TARGET_NAME}_dev) target_include_directories(${TARGET_NAME}_dev INTERFACE - $ - $ $ $ $) @@ -102,7 +100,7 @@ target_include_directories(${TARGET_NAME}_dev INTERFACE target_compile_definitions(${TARGET_NAME}_dev INTERFACE $) -target_link_libraries(${TARGET_NAME}_dev INTERFACE ${TARGET_NAME} openvino::itt openvino::util) +target_link_libraries(${TARGET_NAME}_dev INTERFACE ${TARGET_NAME} openvino::core::dev) set_ie_threading_interface_for(${TARGET_NAME}_dev) set_target_properties(${TARGET_NAME}_dev PROPERTIES EXPORT_NAME runtime::dev) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index bf4a4334fa8..c25f61dfd8c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -47,7 +47,13 @@ source_group("include" FILES ${PUBLIC_HEADERS}) add_library(ov_core_dev INTERFACE) add_library(openvino::core::dev ALIAS ov_core_dev) -target_include_directories(ov_core_dev INTERFACE $) +target_include_directories(ov_core_dev INTERFACE + $ + $ + $ + $) + +target_link_libraries(ov_core_dev INTERFACE openvino::itt openvino::util) set_target_properties(ov_core_dev PROPERTIES EXPORT_NAME core::dev) openvino_developer_export_targets(COMPONENT core TARGETS openvino::core::dev) @@ -105,10 +111,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") endif() target_link_options(ngraph_obj ${link_type} "/IGNORE:4217,4286") - -if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") -ie_add_compiler_flags(/wd4267) -endif() + ie_add_compiler_flags(/wd4267) endif() # some sources are located in ngraph, while headers are in inference_engine_transformations diff --git a/src/core/builder/CMakeLists.txt b/src/core/builder/CMakeLists.txt index 7bc8059ff92..508b02ec797 100644 --- a/src/core/builder/CMakeLists.txt +++ b/src/core/builder/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2022 Intel Corporation +# Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/src/frontends/CMakeLists.txt b/src/frontends/CMakeLists.txt index 268424b365f..b1ace92f327 100644 --- a/src/frontends/CMakeLists.txt +++ b/src/frontends/CMakeLists.txt @@ -24,7 +24,14 @@ if(ENABLE_OV_IR_FRONTEND) add_subdirectory(ir) endif() -if(ENABLE_OV_TF_FRONTEND) +if(ENABLE_OV_TF_FRONTEND OR ENABLE_OV_TF_LITE_FRONTEND) add_subdirectory(tensorflow_common) +endif() + +if(ENABLE_OV_TF_FRONTEND) add_subdirectory(tensorflow) endif() + +if (ENABLE_OV_TF_LITE_FRONTEND) + add_subdirectory(tensorflow_lite) +endif() \ No newline at end of file diff --git a/src/frontends/common/src/manager.cpp b/src/frontends/common/src/manager.cpp index 7a9dceecf06..3d372046bf4 100644 --- a/src/frontends/common/src/manager.cpp +++ b/src/frontends/common/src/manager.cpp @@ -143,16 +143,18 @@ private: {".xml", {"ir", "ir"}}, {".onnx", {"onnx", "onnx"}}, {".pb", {"tf", "tensorflow"}}, + {".tflite", {"tflite", "tensorflow_lite"}}, {".pdmodel", {"paddle", "paddle"}}, + // {".ts", {"pytorch", "pytorch"}}, }; // List of prioritized frontends. - std::list priority_list = { - {"ir", "ir"}, - {"onnx", "onnx"}, - {"tf", "tensorflow"}, - {"paddle", "paddle"}, - }; + std::list priority_list = {{"ir", "ir"}, + {"onnx", "onnx"}, + {"tf", "tensorflow"}, + {"tflite", "tensorflow_lite"}, + {"paddle", "paddle"}, + {"pytorch", "pytorch"}}; if (variants.empty()) { return nullptr; } diff --git a/src/frontends/common/src/plugin_loader.cpp b/src/frontends/common/src/plugin_loader.cpp index 927665dd1d9..ca58ef05bc0 100644 --- a/src/frontends/common/src/plugin_loader.cpp +++ b/src/frontends/common/src/plugin_loader.cpp @@ -45,7 +45,9 @@ void load_static_plugins(std::vector& res) { {"ir", "ir"}, {"onnx", "onnx"}, {"tf", "tensorflow"}, + {"tflite", "tensorflow_lite"}, {"paddle", "paddle"}, + {"pytorch", "pytorch"}, }; auto it = predefined_frontends.find(factory.m_name); if (it != predefined_frontends.end()) { diff --git a/src/frontends/onnx/frontend/CMakeLists.txt b/src/frontends/onnx/frontend/CMakeLists.txt index 9dfa3917fa9..67b48262131 100644 --- a/src/frontends/onnx/frontend/CMakeLists.txt +++ b/src/frontends/onnx/frontend/CMakeLists.txt @@ -13,7 +13,7 @@ ov_add_frontend(NAME onnx PROTOBUF_LITE SKIP_NCC_STYLE FILEDESCRIPTION "FrontEnd to load and convert ONNX file format" - LINK_LIBRARIES ngraph::builder openvino::util onnx_common openvino::runtime::dev) + LINK_LIBRARIES ngraph::builder onnx_common openvino::core::dev) set(ONNX_OPSET_VERSION 17 CACHE INTERNAL "Supported version of ONNX operator set") target_compile_definitions(${TARGET_NAME} PRIVATE ONNX_OPSET_VERSION=${ONNX_OPSET_VERSION}) diff --git a/src/frontends/onnx/frontend/src/editor.cpp b/src/frontends/onnx/frontend/src/editor.cpp index eadf1cebea3..eafd5c764c2 100644 --- a/src/frontends/onnx/frontend/src/editor.cpp +++ b/src/frontends/onnx/frontend/src/editor.cpp @@ -15,6 +15,7 @@ #include "ngraph/log.hpp" #include "onnx_common/parser.hpp" #include "onnx_common/utils.hpp" +#include "openvino/util/file_util.hpp" #include "utils/common.hpp" #include "utils/onnx_internal.hpp" @@ -241,7 +242,7 @@ onnx_editor::ONNXModelEditor::ONNXModelEditor(const std::string& model_path, fro #if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32) onnx_editor::ONNXModelEditor::ONNXModelEditor(const std::wstring& model_path, frontend::ExtensionHolder extensions) - : m_model_path{ngraph::file_util::wstring_to_string(model_path)}, + : m_model_path{ov::util::wstring_to_string(model_path)}, m_extensions{std::move(extensions)}, m_pimpl{new ONNXModelEditor::Impl{model_path}, [](Impl* impl) { delete impl; diff --git a/src/frontends/paddle/src/CMakeLists.txt b/src/frontends/paddle/src/CMakeLists.txt index 7f5f0b4c6d0..8ce804f87ce 100644 --- a/src/frontends/paddle/src/CMakeLists.txt +++ b/src/frontends/paddle/src/CMakeLists.txt @@ -5,8 +5,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") ie_add_compiler_flags(/wd4305) endif() + ov_add_frontend(NAME paddle LINKABLE_FRONTEND PROTOBUF_LITE FILEDESCRIPTION "FrontEnd to load and convert PaddlePaddle file format" - LINK_LIBRARIES openvino::util openvino::runtime::dev) + LINK_LIBRARIES openvino::util openvino::core::dev) diff --git a/src/frontends/pytorch/src/CMakeLists.txt b/src/frontends/pytorch/src/CMakeLists.txt index c0f432cf94a..f51dee59b76 100644 --- a/src/frontends/pytorch/src/CMakeLists.txt +++ b/src/frontends/pytorch/src/CMakeLists.txt @@ -6,4 +6,4 @@ ov_add_frontend(NAME pytorch LINKABLE_FRONTEND SHUTDOWN_PROTOBUF FILEDESCRIPTION "FrontEnd to load and convert TorchScript models from PyTorch" - LINK_LIBRARIES openvino::util openvino::runtime::dev) \ No newline at end of file + LINK_LIBRARIES openvino::util openvino::core::dev) \ No newline at end of file diff --git a/src/frontends/tensorflow/src/CMakeLists.txt b/src/frontends/tensorflow/src/CMakeLists.txt index 49f4923051f..42eb26e93c4 100644 --- a/src/frontends/tensorflow/src/CMakeLists.txt +++ b/src/frontends/tensorflow/src/CMakeLists.txt @@ -5,7 +5,4 @@ ov_add_frontend(NAME tensorflow LINKABLE_FRONTEND FILEDESCRIPTION "FrontEnd to load and convert TensorFlow file format" - LINK_LIBRARIES openvino::util openvino::runtime::dev) - -set(TARGET_NAME "${FRONTEND_NAME_PREFIX}tensorflow${FRONTEND_NAME_SUFFIX}") -target_link_libraries(${TARGET_NAME} PRIVATE openvino::frontend::tensorflow_common) + LINK_LIBRARIES openvino::core::dev openvino::frontend::tensorflow_common) diff --git a/src/frontends/tensorflow/tests/test_models/gen_scripts/__init__.py b/src/frontends/tensorflow/tests/test_models/gen_scripts/__init__.py new file mode 100644 index 00000000000..b066bd81675 --- /dev/null +++ b/src/frontends/tensorflow/tests/test_models/gen_scripts/__init__.py @@ -0,0 +1,3 @@ +# do not print messages from TensorFlow +import os +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' \ No newline at end of file diff --git a/src/frontends/tensorflow/tests/test_models/gen_wrapper.py b/src/frontends/tensorflow/tests/test_models/gen_wrapper.py index 5a68acec6a5..0e4b726e9b2 100644 --- a/src/frontends/tensorflow/tests/test_models/gen_wrapper.py +++ b/src/frontends/tensorflow/tests/test_models/gen_wrapper.py @@ -5,17 +5,17 @@ import os import subprocess import sys -print(sys.argv) +# do not print messages from TensorFlow +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + if len(sys.argv) < 4: - print("Script[model in pbtxt format], output folder and mark file must be specified as arguments") + print("Script[model in pbtxt format], output folder and mark file must be specified as arguments", str(sys.argv)) exit(1) gen_script = sys.argv[1] out_folder = sys.argv[2] mark_file = sys.argv[3] -print("Processing: {} ".format(gen_script)) - if gen_script.endswith('.py'): subprocess.run([sys.executable, gen_script, out_folder], env=os.environ) elif gen_script.endswith('.pbtxt'): diff --git a/src/frontends/tensorflow/tests/test_models/models_pbtxt/__init__.py b/src/frontends/tensorflow/tests/test_models/models_pbtxt/__init__.py new file mode 100644 index 00000000000..b066bd81675 --- /dev/null +++ b/src/frontends/tensorflow/tests/test_models/models_pbtxt/__init__.py @@ -0,0 +1,3 @@ +# do not print messages from TensorFlow +import os +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' \ No newline at end of file diff --git a/src/frontends/tensorflow_common/CMakeLists.txt b/src/frontends/tensorflow_common/CMakeLists.txt index 12d6edab844..e11be311e03 100644 --- a/src/frontends/tensorflow_common/CMakeLists.txt +++ b/src/frontends/tensorflow_common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2022 Intel Corporation +# Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/src/frontends/tensorflow_common/include/place.hpp b/src/frontends/tensorflow_common/include/place.hpp index 09f0546248d..ba946e59466 100644 --- a/src/frontends/tensorflow_common/include/place.hpp +++ b/src/frontends/tensorflow_common/include/place.hpp @@ -33,6 +33,9 @@ public: std::vector get_names() const override { return m_names; } + void set_names(const std::vector& names) { + m_names = names; + } private: const ov::frontend::InputModel& m_input_model; diff --git a/src/frontends/tensorflow_common/src/CMakeLists.txt b/src/frontends/tensorflow_common/src/CMakeLists.txt index 16ee2a7c7b8..b09f3cd7118 100644 --- a/src/frontends/tensorflow_common/src/CMakeLists.txt +++ b/src/frontends/tensorflow_common/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2022 Intel Corporation +# Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # @@ -9,18 +9,21 @@ file(GLOB_RECURSE LIBRARY_SRC ${root_dir}/src/*.cpp) file(GLOB_RECURSE LIBRARY_HEADERS ${root_dir}/include/*.hpp) add_library(${TARGET_NAME} STATIC ${LIBRARY_SRC} ${LIBRARY_HEADERS}) +add_library(openvino::frontend::tensorflow_common ALIAS ${TARGET_NAME}) + if(NOT BUILD_SHARED_LIBS) target_compile_definitions(${TARGET_NAME} PRIVATE OPENVINO_STATIC_LIBRARY) endif() -target_link_libraries(${TARGET_NAME} PRIVATE openvino::util) -add_library(openvino::frontend::tensorflow_common ALIAS ${TARGET_NAME}) +set_target_properties(${TARGET_NAME} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE ${ENABLE_LTO}) + +target_link_libraries(${TARGET_NAME} PRIVATE openvino::util + PUBLIC openvino::core::dev) + target_include_directories(${TARGET_NAME} - PUBLIC - $ - PRIVATE - ${root_dir}/src - $) + PUBLIC $ + PRIVATE ${root_dir}/src) add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME}) ov_install_static_lib(${TARGET_NAME} ${OV_CPACK_COMP_CORE}) diff --git a/src/frontends/tensorflow_common/src/op/bias_add.cpp b/src/frontends/tensorflow_common/src/op/bias_add.cpp index 67e51a00767..b9cac748330 100644 --- a/src/frontends/tensorflow_common/src/op/bias_add.cpp +++ b/src/frontends/tensorflow_common/src/op/bias_add.cpp @@ -37,7 +37,7 @@ OutputVector translate_bias_add_op(const NodeContext& node) { auto value_rank = value_shape.rank().get_length(); std::vector axes_unsqueeze; - for (size_t dim_ind = 0; dim_ind < value_rank; ++dim_ind) { + for (int64_t dim_ind = 0; dim_ind < value_rank; ++dim_ind) { if (dim_ind != 1) { axes_unsqueeze.push_back(dim_ind); } diff --git a/src/frontends/tensorflow_common/src/op/binary_op.cpp b/src/frontends/tensorflow_common/src/op/binary_op.cpp index e91029a14c4..67d8dce497c 100644 --- a/src/frontends/tensorflow_common/src/op/binary_op.cpp +++ b/src/frontends/tensorflow_common/src/op/binary_op.cpp @@ -52,6 +52,7 @@ template OutputVector translate_binary_op(const NodeContext& node); template OutputVector translate_binary_op(const NodeContext& node); template OutputVector translate_binary_op(const NodeContext& node); template OutputVector translate_binary_op(const NodeContext& node); +template OutputVector translate_binary_op(const NodeContext& node); template OutputVector translate_binary_op(const NodeContext& node); template OutputVector translate_binary_op(const NodeContext& node); template OutputVector translate_binary_op(const NodeContext& node); diff --git a/src/frontends/tensorflow_common/src/op/einsum.cpp b/src/frontends/tensorflow_common/src/op/einsum.cpp index f6d121a93da..e8eedb10921 100644 --- a/src/frontends/tensorflow_common/src/op/einsum.cpp +++ b/src/frontends/tensorflow_common/src/op/einsum.cpp @@ -19,7 +19,7 @@ OutputVector translate_einsum_op(const NodeContext& node) { OutputVector inputs; for (size_t input_ind = 0; input_ind < node.get_input_size(); ++input_ind) { - inputs.push_back(node.get_input(input_ind)); + inputs.push_back(node.get_input(static_cast(input_ind))); } auto einsum = make_shared(inputs, equation); diff --git a/src/frontends/tensorflow_common/src/op/unary_op.cpp b/src/frontends/tensorflow_common/src/op/unary_op.cpp index a227740533d..e3a0ac45bbb 100644 --- a/src/frontends/tensorflow_common/src/op/unary_op.cpp +++ b/src/frontends/tensorflow_common/src/op/unary_op.cpp @@ -43,6 +43,7 @@ template OutputVector translate_unary_op(const NodeContext& node); template OutputVector translate_unary_op(const NodeContext& node); template OutputVector translate_unary_op(const NodeContext& node); template OutputVector translate_unary_op(const NodeContext& node); +template OutputVector translate_unary_op(const NodeContext& node); template OutputVector translate_unary_op(const NodeContext& node); template OutputVector translate_unary_op(const NodeContext& node); template OutputVector translate_unary_op(const NodeContext& node); diff --git a/src/frontends/tensorflow_common/src/pass/transpose_sinking.cpp b/src/frontends/tensorflow_common/src/pass/transpose_sinking.cpp index d80ddafd434..ec2b6d96018 100644 --- a/src/frontends/tensorflow_common/src/pass/transpose_sinking.cpp +++ b/src/frontends/tensorflow_common/src/pass/transpose_sinking.cpp @@ -174,7 +174,7 @@ static void convert_binary_to_default_order(const shared_ptr& binary, // instead of a transpose shared_ptr new_node; auto left_rank = get_static_rank(left); - if (left_rank < perm_to_def.size() && left.get_partial_shape().is_static()) { + if (left_rank < static_cast(perm_to_def.size()) && left.get_partial_shape().is_static()) { auto left_shape = left.get_shape(); left_shape.insert(left_shape.begin(), perm_to_def.size() - left_rank, 1); diff --git a/src/frontends/tensorflow_common/tests/CMakeLists.txt b/src/frontends/tensorflow_common/tests/CMakeLists.txt index b692466dfbd..ded0d0164fd 100644 --- a/src/frontends/tensorflow_common/tests/CMakeLists.txt +++ b/src/frontends/tensorflow_common/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2022 Intel Corporation +# Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 # diff --git a/src/frontends/tensorflow_lite/CMakeLists.txt b/src/frontends/tensorflow_lite/CMakeLists.txt new file mode 100644 index 00000000000..e11be311e03 --- /dev/null +++ b/src/frontends/tensorflow_lite/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +add_subdirectory(src) + +if(ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/conversion.hpp b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/conversion.hpp new file mode 100644 index 00000000000..a0881fe548f --- /dev/null +++ b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/conversion.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/frontend/extension/conversion.hpp" +#include "openvino/frontend/frontend.hpp" +#include "openvino/frontend/tensorflow_lite/visibility.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class TENSORFLOW_LITE_API ConversionExtension : public ConversionExtensionBase { +public: + using Ptr = std::shared_ptr; + + ConversionExtension() = delete; + + ConversionExtension(const std::string& op_type, const ov::frontend::CreatorFunction& converter) + : ConversionExtensionBase(op_type), + m_converter(converter) {} + + const ov::frontend::CreatorFunction& get_converter() const { + return m_converter; + } + + ~ConversionExtension() override; + +private: + ov::frontend::CreatorFunction m_converter; +}; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/op.hpp b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/op.hpp new file mode 100644 index 00000000000..b91926f03ac --- /dev/null +++ b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/extension/op.hpp @@ -0,0 +1,18 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "openvino/frontend/extension/op.hpp" +#include "openvino/frontend/tensorflow_lite/extension/conversion.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +template +using OpExtension = ov::frontend::OpExtensionBase; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/frontend.hpp b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/frontend.hpp new file mode 100644 index 00000000000..d3ad4734fcf --- /dev/null +++ b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/frontend.hpp @@ -0,0 +1,79 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include + +#include "openvino/core/any.hpp" +#include "openvino/frontend/extension/decoder_transformation.hpp" +#include "openvino/frontend/extension/telemetry.hpp" +#include "openvino/frontend/frontend.hpp" +#include "openvino/frontend/tensorflow_lite/extension/conversion.hpp" +#include "openvino/frontend/tensorflow_lite/node_context.hpp" +#include "openvino/frontend/tensorflow_lite/visibility.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +using CreatorFunction = std::function; +using TranslatorDictionaryType = std::map; + +class TENSORFLOW_LITE_API FrontEnd : public ov::frontend::FrontEnd { +public: + FrontEnd(); + /// \brief Completely convert the model + /// \return fully converted ov Model + std::shared_ptr convert(const ov::frontend::InputModel::Ptr& model) const override; + + /// \brief Completely convert the remaining, not converted part of a function. + /// \param partiallyConverted partially converted ov Model + void convert(const std::shared_ptr& partiallyConverted) const override; + + /// \brief Convert only those parts of the model that can be converted leaving others + /// as-is. Converted parts are not normalized by additional transformations; normalize + /// function or another form of convert function should be called to finalize the + /// conversion process. + /// \param model Input model + /// \return partially converted ov Model + std::shared_ptr convert_partially(const ov::frontend::InputModel::Ptr& model) const override; + + /// \brief Convert operations with one-to-one mapping with decoding nodes. + /// Each decoding node is an ov node representing a single TFLite operation node with + /// all attributes represented in FW-independent way. + /// \param model Input model + /// \return ov Model after decoding + std::shared_ptr decode(const ov::frontend::InputModel::Ptr& model) const override; + + /// \brief Runs normalization passes on function that was loaded with partial conversion + /// \param Model partially converted ov Model + void normalize(const std::shared_ptr& function) const override; + + /// \brief Gets name of this FrontEnd. Can be used by clients + std::string get_name() const override { + return "tflite"; + } + void add_extension(const std::shared_ptr& extension) override; + +protected: + /// \brief Check if FrontEndTensorflowLite can recognize model from given parts + bool supported_impl(const std::vector& variants) const override; + ov::frontend::InputModel::Ptr load_impl(const std::vector& variants) const override; + + void translate_graph(const ov::frontend::InputModel::Ptr& model, + bool fail_fast, + bool no_conversion, + std::shared_ptr& ng_function) const; + + TelemetryExtension::Ptr m_telemetry; + std::vector m_transformation_extensions; + std::vector m_conversion_extensions; + + TranslatorDictionaryType m_op_translators; +}; +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/node_context.hpp b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/node_context.hpp new file mode 100644 index 00000000000..0d165ea0ec0 --- /dev/null +++ b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/node_context.hpp @@ -0,0 +1,67 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/core/any.hpp" +#include "openvino/frontend/decoder.hpp" +#include "openvino/frontend/node_context.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +/// Keep necessary data for a single node in the original FW graph to facilitate +/// conversion process in the rules code. +class TENSORFLOW_LITE_API NodeContext : public ov::frontend::NodeContext { +public: + using Ptr = std::shared_ptr; + NodeContext(const std::shared_ptr& decoder, const OutputVector& inputs) + : ov::frontend::NodeContext(decoder->get_op_type()), + m_decoder(decoder), + m_inputs(inputs) {} + + /// Detects if there is at least one input attached with a given name + bool has_input(const size_t& port_index) const { + return port_index < m_inputs.size(); + } + + Output get_input(int port_index) const override { + return m_inputs.at(port_index); + } + + OutputVector get_inputs() const { + return m_inputs; + } + + size_t get_input_size() const override { + return m_inputs.size(); + } + + /// \brief Get a node name + const std::string& get_name() const override { + return m_decoder->get_op_name(); + } + + /// \brief Get a decoder + std::shared_ptr get_decoder() const { + return m_decoder; + } + + ov::Any get_attribute_as_any(const std::string& name) const override { + auto res = m_decoder->get_attribute(name); + return res; + } + +private: + std::shared_ptr m_decoder; + const OutputVector& m_inputs; +}; + +using CreatorFunction = std::function; +using TranslatorDictionaryType = std::map; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/visibility.hpp b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/visibility.hpp new file mode 100644 index 00000000000..7131b5b6864 --- /dev/null +++ b/src/frontends/tensorflow_lite/include/openvino/frontend/tensorflow_lite/visibility.hpp @@ -0,0 +1,20 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/frontend/exception.hpp" + +#ifdef OPENVINO_STATIC_LIBRARY +# define TENSORFLOW_LITE_API +# define TENSORFLOW_LITE_C_API +#else +# ifdef openvino_tensorflow_lite_frontend_EXPORTS +# define TENSORFLOW_LITE_API OPENVINO_CORE_EXPORTS +# define TENSORFLOW_LITE_C_API OPENVINO_EXTERN_C OPENVINO_CORE_EXPORTS +# else +# define TENSORFLOW_LITE_API OPENVINO_CORE_IMPORTS +# define TENSORFLOW_LITE_C_API OPENVINO_EXTERN_C OPENVINO_CORE_IMPORTS +# endif // openvino_tensorflow_lite_frontend_EXPORTS +#endif // OPENVINO_STATIC_LIBRARY diff --git a/src/frontends/tensorflow_lite/src/CMakeLists.txt b/src/frontends/tensorflow_lite/src/CMakeLists.txt new file mode 100644 index 00000000000..4b453b2724f --- /dev/null +++ b/src/frontends/tensorflow_lite/src/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + ie_add_compiler_flags(/wd4267) +endif() + +ov_add_frontend(NAME tensorflow_lite + LINKABLE_FRONTEND + FILEDESCRIPTION "FrontEnd to load and convert TensorFlow Lite file format" + LINK_LIBRARIES openvino::core::dev openvino::frontend::tensorflow_common) diff --git a/src/frontends/tensorflow_lite/src/decoder_flatbuffer.cpp b/src/frontends/tensorflow_lite/src/decoder_flatbuffer.cpp new file mode 100644 index 00000000000..0ce55d90f9d --- /dev/null +++ b/src/frontends/tensorflow_lite/src/decoder_flatbuffer.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "decoder_flatbuffer.h" + +#include "schema_generated.h" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +size_t DecoderFlatBuffer::get_input_size() const { + return m_input_info.size(); +} + +void DecoderFlatBuffer::get_input_node(size_t input_port_idx, + std::string& producer_name, + size_t& producer_output_port_index) const { + const auto inputs = m_node_def->inputs(); + FRONT_END_GENERAL_CHECK(inputs->size() > input_port_idx, + "Input port index is out of range for node ", + get_op_name(), + ". Requested input index: ", + input_port_idx, + ". Number of inputs: ", + inputs->size()); + auto input_tensor_idx = (*inputs)[input_port_idx]; + auto tensor = m_input_info.at(input_port_idx).tensor; + std::string name = (*tensor).name()->str(); + producer_name = name; + producer_output_port_index = input_tensor_idx; +} + +const std::string& DecoderFlatBuffer::get_op_type() const { + return m_type; +} + +const std::string& DecoderFlatBuffer::get_op_name() const { + return m_name; +} + +size_t DecoderFlatBuffer::get_output_size() const { + return m_node_def->outputs()->size(); +} + +std::string DecoderFlatBuffer::get_input_tensor_name(size_t idx) const { + FRONT_END_GENERAL_CHECK(idx < get_input_size(), "Requested input is out-of-range"); + return m_input_info.at(idx).tensor->name()->str(); +} +std::string DecoderFlatBuffer::get_output_tensor_name(size_t idx) const { + FRONT_END_GENERAL_CHECK(idx < get_output_size(), "Requested output is out-of-range"); + return m_output_info.at(idx).tensor->name()->str(); +} + +std::shared_ptr DecoderFlatBuffer::decode_input_tensor( + size_t idx, + const InputModel& model) const { + FRONT_END_GENERAL_CHECK(idx < get_input_size(), "Requested input is out-of-range"); + return decode_tensor(m_input_info.at(idx), model); +} + +std::shared_ptr DecoderFlatBuffer::decode_output_tensor( + size_t idx, + const InputModel& model) const { + FRONT_END_GENERAL_CHECK(idx < get_output_size(), "Requested output is out-of-range"); + return decode_tensor(m_output_info.at(idx), model); +} + +std::shared_ptr DecoderFlatBuffer::decode_tensor( + const ov::frontend::tensorflow_lite::TensorInfo& tensor_info, + const InputModel& model) const { + const auto tensor = tensor_info.tensor; + std::vector names = {tensor->name()->str()}; + + return std::make_shared( + model, + ov::frontend::tensorflow_lite::get_ov_shape(tensor->shape()), + ov::frontend::tensorflow_lite::get_ov_type(tensor->type()), + names, + ov::frontend::tensorflow_lite::get_quantization(tensor->quantization()), + tensor_info.input_idx, + tensor_info.output_idx, + (tensor_info.buffer->data() ? tensor_info.buffer->data()->data() : nullptr)); +} + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/decoder_flatbuffer.h b/src/frontends/tensorflow_lite/src/decoder_flatbuffer.h new file mode 100644 index 00000000000..1b7d061ba72 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/decoder_flatbuffer.h @@ -0,0 +1,68 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include "tensor_lite_place.hpp" +#include "graph_iterator_flatbuffer.hpp" +#include "openvino/frontend/tensorflow_lite/visibility.hpp" +#include "openvino/frontend/decoder.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class TensorLitePlace; +struct TensorInfo; + +class DecoderFlatBuffer : public ov::frontend::DecoderBase { +public: + explicit DecoderFlatBuffer(const tflite::Operator* node_def, + const std::string& type, + const std::string& name, + std::map input_info, + std::map output_info) + : m_node_def(node_def), m_type(type), m_name(name), m_input_info(input_info), m_output_info(output_info) {} + + template + Ret get_attribute(Ret (Class::*member)() const) const { + const auto opts = m_node_def->builtin_options_as(); + FRONT_END_GENERAL_CHECK(opts != nullptr, "Chosen Builtin Option is not accessible for this node"); + return (opts->*member)(); + } + + ov::Any get_attribute(const std::string& name) const override { + return {}; + } + + size_t get_input_size() const override; + size_t get_output_size() const; + + void get_input_node(size_t input_port_idx, + std::string& producer_name, + size_t& producer_output_port_index) const override; + std::string get_output_tensor_name(size_t idx) const; + std::string get_input_tensor_name(size_t idx) const; + + const std::string& get_op_type() const override; + const std::string& get_op_name() const override; + + std::shared_ptr decode_input_tensor(size_t idx, const InputModel& model) const; + + std::shared_ptr decode_output_tensor(size_t idx, const InputModel& model) const; + +private: + std::shared_ptr decode_tensor( + const ov::frontend::tensorflow_lite::TensorInfo& tensor_info, const InputModel& model) const; + + const tflite::Operator* m_node_def; + std::string m_type, m_name; + std::map m_input_info, m_output_info; +}; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/decoder_map.hpp b/src/frontends/tensorflow_lite/src/decoder_map.hpp new file mode 100644 index 00000000000..0d3913db7f5 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/decoder_map.hpp @@ -0,0 +1,87 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "openvino/core/any.hpp" +#include "openvino/frontend/decoder.hpp" +#include "openvino/frontend/tensorflow_lite/visibility.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class DecoderMap : public ov::frontend::DecoderBase { +public: + DecoderMap(std::shared_ptr decoder, + const std::map& attrs, + bool empty_name = false) + : ov::frontend::DecoderBase(), + m_decoder(std::move(decoder)), + m_attrs(attrs), + m_empty_name(empty_name) {} + + DecoderMap(std::shared_ptr decoder, + const std::map& attrs, + std::string type, + bool empty_name = false) + : ov::frontend::DecoderBase(), + m_decoder(std::move(decoder)), + m_attrs(attrs), + m_type(type), + m_empty_name(empty_name) {} + + /// \brief Get attribute value by name + /// + /// \param name Attribute name + /// \return Shared pointer to appropriate value converted to openvino data type if it exists, 'nullptr' otherwise + ov::Any get_attribute(const std::string& name) const override { + FRONT_END_GENERAL_CHECK(m_attrs.count(name), "DecoderMap was requested attribute that doesn't exist: ", name); + return m_attrs.at(name); + } + + /// \brief Get a number of inputs + size_t get_input_size() const override { + return m_decoder->get_input_size(); + } + + /// \brief Get a producer name and its output port index + /// + /// \param input_port_idx Input port index by which data is consumed + /// \param producer_name A producer name + /// \return producer_output_port_index Output port index from which data is generated + void get_input_node(size_t input_port_idx, + std::string& producer_name, + size_t& producer_output_port_index) const override { + m_decoder->get_input_node(input_port_idx, producer_name, producer_output_port_index); + } + + /// \brief Get operation type + const std::string& get_op_type() const override { + if (m_type.empty()) + return m_decoder->get_op_type(); + return m_type; + } + + /// \brief Get node name + const std::string& get_op_name() const override { + return m_empty_name ? empty_name : m_decoder->get_op_name(); + } + + /// \brief Destructor + ~DecoderMap() = default; + +private: + std::map m_attrs; + std::shared_ptr m_decoder; + std::string m_type; + const std::string empty_name; + bool m_empty_name; +}; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/extension/conversion.cpp b/src/frontends/tensorflow_lite/src/extension/conversion.cpp new file mode 100644 index 00000000000..8aa7c6217e2 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/extension/conversion.cpp @@ -0,0 +1,9 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/frontend/tensorflow_lite/extension/conversion.hpp" + +using namespace ov::frontend::tensorflow_lite; + +ConversionExtension::~ConversionExtension() = default; diff --git a/src/frontends/tensorflow_lite/src/frontend.cpp b/src/frontends/tensorflow_lite/src/frontend.cpp new file mode 100644 index 00000000000..938d1d1c741 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/frontend.cpp @@ -0,0 +1,297 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/frontend/tensorflow_lite/frontend.hpp" + +#include "graph_iterator_flatbuffer.hpp" +#include "input_model.hpp" +#include "op/op_translation_utils.hpp" +#include "op_table.hpp" +#include "openvino/frontend/tensorflow_lite/extension/op.hpp" +#include "openvino/util/common_util.hpp" +#include "pass/transpose_sinking.hpp" +#include "so_extension.hpp" +#include "tensor_lite_place.hpp" +#include "tf_framework_node.hpp" +#include "transformations/common_optimizations/transpose_sinking.hpp" + +using namespace ov; +using namespace ov::frontend::tensorflow_lite; + +namespace { +void translate_framework_node(const std::shared_ptr& node, + const ov::frontend::tensorflow_lite::TranslatorDictionaryType& op_translators) { + auto type = node->get_op_type(); + + const auto& TRANSLATE_OP_MAP = op_translators; + auto translator_it = TRANSLATE_OP_MAP.find(type); + FRONT_END_OP_CONVERSION_CHECK(translator_it != TRANSLATE_OP_MAP.end(), "No translator found for ", type, " node."); + + ov::OutputVector ov_inputs = node->input_values(); + ov::frontend::tensorflow_lite::NodeContext node_ctx(node->get_decoder(), ov_inputs); + auto new_node_outputs = translator_it->second(node_ctx); + ov::frontend::tensorflow_lite::op::set_output_names(node_ctx, new_node_outputs); + + auto new_output = new_node_outputs.begin(); + auto old_outputs = node->outputs(); + auto old_output = old_outputs.begin(); + + for (; new_output != new_node_outputs.end() && old_output != old_outputs.end(); ++old_output, ++new_output) { + old_output->replace(*new_output); + apply_quantization(*new_output); + } +} +} // namespace + +FrontEnd::FrontEnd() { + m_op_translators = ov::frontend::tensorflow_lite::op::get_supported_ops(); +} + +/// \brief Check if FrontEndTensorflowLite can recognize model from given parts +bool FrontEnd::supported_impl(const std::vector& variants) const { + if (variants.size() != 1) + return false; + + if (variants[0].is()) { + std::string suffix = ".tflite"; + std::string model_path = variants[0].as(); + if (ov::util::ends_with(model_path, suffix.c_str())) { + return true; + } + } +#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32) + else if (variants[0].is()) { + std::wstring suffix = L".tflite"; + std::wstring model_path = variants[0].as(); + if (ov::util::ends_with(model_path, suffix)) { + return true; + } + } +#endif + return false; +} + +ov::frontend::InputModel::Ptr FrontEnd::load_impl(const std::vector& variants) const { + if (variants.size() == 1) { + if (variants[0].is()) { + std::string suffix = ".tflite"; + std::string model_path = variants[0].as(); + if (ov::util::ends_with(model_path, suffix.c_str())) { + return std::make_shared( + std::make_shared(model_path), + m_telemetry); + } + } +#if defined(OPENVINO_ENABLE_UNICODE_PATH_SUPPORT) && defined(_WIN32) + else if (variants[0].is()) { + std::wstring suffix = L".tflite"; + std::wstring model_path = variants[0].as(); + if (ov::util::ends_with(model_path, suffix)) { + return std::make_shared( + std::make_shared(model_path), + m_telemetry); + } + } +#endif + } + return nullptr; +} + +std::shared_ptr FrontEnd::convert(const ov::frontend::InputModel::Ptr& model) const { + std::shared_ptr ov_model; + if (!m_transformation_extensions.empty()) { + auto ov_model = decode(model); + + ov::pass::Manager manager; + for (const auto& transformation : m_transformation_extensions) { + transformation->register_pass(manager); + } + manager.run_passes(ov_model); + convert(ov_model); + return ov_model; + } + + translate_graph(model, true, false, ov_model); + normalize(ov_model); + + for (const auto& node : ov_model->get_ordered_ops()) { + if (const auto& fw_node = ov::as_type_ptr(node)) { + auto op_type = fw_node->get_decoder()->get_op_type(); + auto op_name = fw_node->get_decoder()->get_op_name(); + FRONT_END_OP_CONVERSION_CHECK(false, + "The translation is incomplete due to operation ", + op_name, + " of type ", + op_type); + } + } + return ov_model; +} + +void FrontEnd::convert(const std::shared_ptr& partiallyConverted) const { + for (const auto& node : partiallyConverted->get_ordered_ops()) { + if (ov::is_type(node)) { + translate_framework_node(std::dynamic_pointer_cast(node), + m_op_translators); + } + } + for (const auto& result : partiallyConverted->get_results()) { + result->validate_and_infer_types(); + } + normalize(partiallyConverted); +} + +std::shared_ptr FrontEnd::convert_partially(const ov::frontend::InputModel::Ptr& model) const { + if (!m_transformation_extensions.empty()) { + auto function = decode(model); + ov::pass::Manager manager; + for (const auto& transformation : m_transformation_extensions) { + transformation->register_pass(manager); + } + manager.run_passes(function); + convert(function); + return function; + } + + std::shared_ptr f; + translate_graph(model, false, false, f); + normalize(f); + return f; +} + +void FrontEnd::translate_graph(const InputModel::Ptr& model, + bool fail_fast, + bool no_conversion, + std::shared_ptr& ov_function) const { + const auto& model_lite = std::dynamic_pointer_cast(model); + FRONT_END_GENERAL_CHECK(model_lite, "nullptr for InputModel is given for translation into OV Model"); + + const auto& translate_map = + no_conversion ? ov::frontend::tensorflow_lite::TranslatorDictionaryType{} : m_op_translators; + + auto all_tensor_values = model_lite->get_tensor_values(); + auto all_tensor_places = model_lite->get_tensor_places(); + + for (auto& value : all_tensor_values) { + auto& output = value.second; + FRONT_END_GENERAL_CHECK(ov::is_type(output.get_node_shared_ptr()), + "Unexpected constant data configuration at the beginning of graph translation"); + const auto& input_tensor = all_tensor_places.at(value.first); + FRONT_END_GENERAL_CHECK(input_tensor != nullptr, "Inputs must be TensorPlaces"); + input_tensor->translate(output, !no_conversion); + } + + // inputs + ParameterVector parameters; + parameters.reserve(model_lite->get_inputs().size()); + for (const auto& input : model_lite->get_inputs()) { + const auto& input_tensor = std::dynamic_pointer_cast(input); + FRONT_END_GENERAL_CHECK( + input_tensor != nullptr, + "Inputs of ov::frontend::tensorflow_lite::InputModel must be TensorLitePlace instances"); + const auto name = input_tensor->get_names()[0]; + auto parameter = std::make_shared(input_tensor->get_element_type(), + input_tensor->get_partial_shape()); + parameter->set_friendly_name(name); + parameters.push_back(parameter); + all_tensor_values[name] = parameter->output(0); + input_tensor->translate(all_tensor_values[name], !no_conversion); + } + + // operations + for (const auto& op_place : model_lite->get_op_places()) { + const auto& decoder = std::dynamic_pointer_cast(op_place->get_decoder()); + FRONT_END_GENERAL_CHECK(decoder != nullptr, "Decoder must be DecoderFlatBuffer or its child"); + ov::OutputVector inputs(decoder->get_input_size()); + for (size_t i = 0; i < decoder->get_input_size(); ++i) { + auto name = decoder->get_input_tensor_name(i); + FRONT_END_GENERAL_CHECK(all_tensor_values.find(name) != all_tensor_values.end(), + "Unknown tensor name: ", + name, + "."); + inputs[i] = all_tensor_values[name]; + } + + const auto& out_size = decoder->get_output_size(); + ov::OutputVector ov_outputs(out_size); + try { + FRONT_END_OP_CONVERSION_CHECK(translate_map.count(decoder->get_op_type()), + "No translator found for " + decoder->get_op_type() + " node."); + auto op_fun = &(translate_map.at(decoder->get_op_type())); + ov::frontend::tensorflow_lite::NodeContext node_context(decoder, inputs); + ov_outputs = (*op_fun)(node_context); + } catch (...) { + if (fail_fast) { + if (m_telemetry && translate_map.count(decoder->get_op_type()) == 0) { + m_telemetry->send_event("error_cause", "tflite_" + decoder->get_op_type()); + } + throw; + } else { + auto operation = std::make_shared(decoder, inputs, out_size); + operation->set_friendly_name(decoder->get_op_name()); + ov_outputs = operation->outputs(); + } + } + for (size_t i = 0; i < out_size; ++i) { + const auto& name = decoder->get_output_tensor_name(i); + all_tensor_values[name] = ov_outputs[i]; + all_tensor_places[name]->translate(all_tensor_values[name], !no_conversion); + } + } + + // outputs + ResultVector results; + results.reserve(model_lite->get_outputs().size()); + for (const auto& output : model_lite->get_outputs()) { + const auto& tensor = std::dynamic_pointer_cast(output); + FRONT_END_GENERAL_CHECK( + tensor != nullptr, + "Inputs of ov::frontend::tensorflow_lite::InputModel must be TensorLitePlace instances"); + const auto name = tensor->get_names()[0]; + const auto& output_value = all_tensor_values[name]; + const auto& result = std::make_shared(output_value); + auto input = result->output(0); + tensor->translate(input, !no_conversion); + results.push_back(result); + } + auto model_name = "TensorFlow_Lite_Frontend_IR"; + ov_function = std::make_shared(results, parameters, model_name); +} + +std::shared_ptr FrontEnd::decode(const InputModel::Ptr& model) const { + std::shared_ptr ov_model; + translate_graph(model, false, true, ov_model); + return ov_model; +} + +void FrontEnd::normalize(const std::shared_ptr& function) const { + ov::pass::Manager manager; + // TODO: register i8 weights normalization after implemented + // TODO: remove custom transpose sinking after common TS ready + manager.register_pass(); + manager.register_pass(); + manager.run_passes(function); +} + +void FrontEnd::add_extension(const std::shared_ptr& extension) { + if (auto telemetry = std::dynamic_pointer_cast(extension)) { + m_telemetry = telemetry; + } else if (auto transformation = std::dynamic_pointer_cast(extension)) { + m_transformation_extensions.push_back(transformation); + } else if (const auto& so_ext = std::dynamic_pointer_cast(extension)) { + add_extension(so_ext->extension()); + m_extensions.push_back(so_ext); + } else if (auto common_conv_ext = std::dynamic_pointer_cast(extension)) { + m_conversion_extensions.push_back(common_conv_ext); + m_op_translators[common_conv_ext->get_op_type()] = [=](const NodeContext& context) { + return common_conv_ext->get_converter()(context); + }; + } else if (const auto& tensorflow_conv_ext = + std::dynamic_pointer_cast(extension)) { + m_conversion_extensions.push_back(tensorflow_conv_ext); + m_op_translators[tensorflow_conv_ext->get_op_type()] = [=](const NodeContext& context) { + return tensorflow_conv_ext->get_converter()(context); + }; + } +} diff --git a/src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.cpp b/src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.cpp new file mode 100644 index 00000000000..1f488e8cb75 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.cpp @@ -0,0 +1,88 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +using namespace ov::frontend::tensorflow_lite; + +#ifdef OPENVINO_ENABLE_UNICODE_PATH_SUPPORT + +GraphIteratorFlatBuffer::GraphIteratorFlatBuffer(const std::wstring& path) + : GraphIteratorFlatBuffer(ov::util::wstring_to_string(path)) {} + +#endif // OPENVINO_ENABLE_UNICODE_PATH_SUPPORT + +GraphIteratorFlatBuffer::GraphIteratorFlatBuffer(const std::string& path) { + std::ifstream model_file; + model_file.open(path, std::ios::binary | std::ios::in); + FRONT_END_GENERAL_CHECK(model_file && model_file.is_open(), "Model file does not exist: ", path); + + model_file.seekg(0, std::ios::end); + auto length = model_file.tellg(); + model_file.seekg(0, std::ios::beg); + char* data = new char[length]; + model_file.read(data, length); + model_file.close(); + + m_model = std::shared_ptr(tflite::GetMutableModel(data), [](tflite::Model* p) {}); + const auto subgraphs = m_model->subgraphs(); + FRONT_END_GENERAL_CHECK(subgraphs->size() == 1, + "Number of sub-graphs in the model is ", + subgraphs->size(), + ". Supported number of sub-graphs is 1."); + const auto graph = *subgraphs->begin(); + const auto operators = graph->operators(); + m_nodes = {operators->begin(), operators->end()}; +} + +std::shared_ptr GraphIteratorFlatBuffer::get_decoder() const { + auto inputs_vec = (*m_model->subgraphs()->begin())->inputs(); + auto outputs_vec = (*m_model->subgraphs()->begin())->outputs(); + auto inputs = std::set{inputs_vec->begin(), inputs_vec->end()}; + auto outputs = std::set{outputs_vec->begin(), outputs_vec->end()}; + + auto buffers = m_model->buffers(); + auto tensors = m_model->subgraphs()->begin()->tensors(); + + std::map input_info = {}, output_info = {}; + size_t i = 0; + for (auto input : *m_nodes[node_index]->inputs()) { + if (input == -1) { + continue; + } + auto buffer = (*buffers)[(*tensors)[input]->buffer()]; + auto is_input = inputs.find(input) != inputs.end(); + int64_t input_idx = + !is_input ? -1 : std::find(inputs_vec->begin(), inputs_vec->end(), input) - inputs_vec->begin(); + auto is_output = outputs.find(input) != outputs.end(); + int64_t output_idx = + !is_output ? -1 : std::find(outputs_vec->begin(), outputs_vec->end(), input) - outputs_vec->begin(); + input_info[i++] = TensorInfo{input_idx, output_idx, (*tensors)[input], buffer}; + } + i = 0; + // If we have any m_nodes[node_index]->intermediates() than trigger internal smth? no + // put all the info in Decoder as a sub-graph! + + for (auto output : *m_nodes[node_index]->outputs()) { + auto buffer = (*buffers)[(*tensors)[output]->buffer()]; + auto is_output = outputs.find(output) != outputs.end(); + int64_t output_idx = + !is_output ? -1 : std::find(outputs_vec->begin(), outputs_vec->end(), output) - outputs_vec->begin(); + output_info[i++] = TensorInfo{-1, output_idx, (*tensors)[output], buffer}; + } + auto op_codes = m_model->operator_codes(); + auto operator_code = (*op_codes)[m_nodes[node_index]->opcode_index()]; + std::string type; + if (operator_code->deprecated_builtin_code() < + tflite::BuiltinOperator::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES) { + type = tflite::EnumNamesBuiltinOperator()[operator_code->deprecated_builtin_code()]; + } else { + type = tflite::EnumNamesBuiltinOperator()[operator_code->builtin_code()]; + } + return std::make_shared(m_nodes[node_index], + type, + std::to_string(node_index), + input_info, + output_info); +} diff --git a/src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.hpp b/src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.hpp new file mode 100644 index 00000000000..3e5f37ecbe8 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/graph_iterator_flatbuffer.hpp @@ -0,0 +1,65 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "decoder_flatbuffer.h" +#include "openvino/frontend/exception.hpp" +#include "openvino/util/file_util.hpp" +#include "schema_generated.h" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +class DecoderFlatBuffer; + +struct TensorInfo { + int64_t input_idx, output_idx; + const tflite::Tensor* tensor; + const tflite::Buffer* buffer; +}; + +class GraphIteratorFlatBuffer { + size_t node_index = 0; + std::vector m_nodes; + std::shared_ptr m_model; + +public: + explicit GraphIteratorFlatBuffer(const std::string& path); + +#ifdef OPENVINO_ENABLE_UNICODE_PATH_SUPPORT + explicit GraphIteratorFlatBuffer(const std::wstring& path); +#endif + + using Ptr = std::shared_ptr; + + ~GraphIteratorFlatBuffer() = default; + + /// Set iterator to the start position + void reset() { + node_index = 0; + } + + size_t size() const { + return m_nodes.size(); + } + + /// Moves to the next node in the graph + void next() { + node_index++; + } + + bool is_end() const { + return node_index >= m_nodes.size(); + } + + /// Return Decoder for the current node that iterator points to + std::shared_ptr get_decoder() const; +}; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/input_model.cpp b/src/frontends/tensorflow_lite/src/input_model.cpp new file mode 100644 index 00000000000..662fdbdae17 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/input_model.cpp @@ -0,0 +1,389 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "input_model.hpp" + +#include +#include + +#include "openvino/frontend/exception.hpp" +#include "openvino/opsets/opset10.hpp" +#include "openvino/util/log.hpp" +#include "tensor_lite_place.hpp" +#include "utils.hpp" + +using namespace ov::frontend::tensorflow; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class InputModel::InputModelTFLiteImpl { +public: + InputModelTFLiteImpl(const GraphIteratorFlatBuffer::Ptr& graph_iterator, + const ov::frontend::InputModel& input_model); + InputModelTFLiteImpl(const GraphIteratorFlatBuffer::Ptr& graph_iterator, + const ov::frontend::InputModel& input_model, + const std::shared_ptr& telemetry); + std::vector getInputs() const; + std::vector getOutputs() const; + ov::frontend::Place::Ptr getPlaceByTensorName(const std::string& tensorName) const; + + ///// Searching for places ///// + std::vector> get_op_places() const { + return m_op_places; + } + std::map> get_tensor_places() const { + return m_tensor_places; + } + std::map> get_tensor_values() const { + return m_tensor_values; + } + + ///// Naming and annotation ///// + void setNameForTensor(const Place::Ptr& tensor, const std::string& new_name); + void addNameForTensor(const Place::Ptr& tensor, const std::string& new_name); + void setNameForOperation(const Place::Ptr& operation, const std::string& new_name); + + ///// Setting / getting tensor properties ///// + void setPartialShape(ov::frontend::Place::Ptr place, const ov::PartialShape& shape); + ov::PartialShape getPartialShape(ov::frontend::Place::Ptr place) const; + void setElementType(ov::frontend::Place::Ptr place, const ov::element::Type& type); + ov::element::Type getElementType(ov::frontend::Place::Ptr place) const; + void setTensorValue(ov::frontend::Place::Ptr place, const void* value); + + ///// Topology Editing ///// + void overrideAllOutputs(const std::vector& outputs); + void overrideAllInputs(const std::vector& inputs); + void extractSubgraph(const std::vector& inputs, + const std::vector& outputs); + +private: + void loadModel(); + void cleanUp(); + + std::vector> m_op_places; + std::map> m_op_places_map; + std::map> m_tensor_places; + std::vector m_inputs; + std::vector m_outputs; + std::map> m_tensor_values; + + std::shared_ptr m_graph_iterator; + const ov::frontend::InputModel& m_input_model; + + std::shared_ptr m_telemetry; +}; + +void InputModel::InputModelTFLiteImpl::loadModel() { + std::map op_statistics; // for telemetry + + m_op_places.reserve(m_graph_iterator->size()); + for (; !m_graph_iterator->is_end(); m_graph_iterator->next()) { + const auto& decoder = m_graph_iterator->get_decoder(); + m_op_places.push_back(std::make_shared(m_input_model, decoder)); + + if (m_telemetry) { + op_statistics[decoder->get_op_type()]++; + } + + for (size_t i = 0; i < decoder->get_input_size(); ++i) { + auto place = decoder->decode_input_tensor(i, m_input_model); + auto name = place->get_names()[0]; + if (m_tensor_places.find(name) == m_tensor_places.end()) { + m_tensor_places[name] = place; + if (place->is_input()) { + // will reorder by index later + m_inputs.push_back(place); + } else if (auto data = place->get_data()) { + auto constant = ov::op::v0::Constant::create(place->get_element_type(), + place->get_partial_shape().to_shape(), + data); + constant->set_friendly_name(name); + m_tensor_values[name] = constant; + } else { + FRONT_END_GENERAL_CHECK(false, + "This tensor should be either input, constant or ", + "should be already produced by previous operators: ", + name, + ". Error is encountered while working with operation of type ", + decoder->get_op_type(), + " and name ", + decoder->get_op_name(), + "."); + } + } + } + for (size_t i = 0; i < decoder->get_output_size(); ++i) { + auto place = decoder->decode_output_tensor(i, m_input_model); + auto name = place->get_names()[0]; + if (m_tensor_places.find(name) == m_tensor_places.end()) { + m_tensor_places[name] = place; + if (place->is_output()) { + // will reorder by index later + m_outputs.push_back(place); + } + } + } + } + + auto sorting_places_by_idx = [](bool are_input_places) { + return + [are_input_places](const ov::frontend::Place::Ptr& lhs_place, const ov::frontend::Place::Ptr& rhs_place) { + auto tflite_lhs_place = + std::dynamic_pointer_cast(lhs_place); + auto tflite_rhs_place = + std::dynamic_pointer_cast(rhs_place); + FRONT_END_GENERAL_CHECK(tflite_lhs_place != nullptr && tflite_rhs_place != nullptr, + "TFLite Frontend works with TensorLitePlaces only"); + size_t rhs_idx, lhs_idx; + if (are_input_places) { + lhs_idx = tflite_lhs_place->get_input_index(); + rhs_idx = tflite_rhs_place->get_input_index(); + } else { + lhs_idx = tflite_lhs_place->get_output_index(); + rhs_idx = tflite_rhs_place->get_output_index(); + } + return lhs_idx < rhs_idx; + }; + }; + std::sort(m_inputs.begin(), m_inputs.end(), sorting_places_by_idx(true)); + std::sort(m_outputs.begin(), m_outputs.end(), sorting_places_by_idx(false)); + + if (m_telemetry) { + for (const auto& op : op_statistics) { + m_telemetry->send_event("op_count", "tflite_" + op.first, static_cast(op.second)); + } + } +} + +InputModel::InputModelTFLiteImpl::InputModelTFLiteImpl(const GraphIteratorFlatBuffer::Ptr& graph_iterator, + const ov::frontend::InputModel& input_model) + : m_input_model(input_model), + m_graph_iterator(graph_iterator) { + FRONT_END_GENERAL_CHECK(m_graph_iterator, "Null pointer specified for GraphIterator"); + loadModel(); +} + +InputModel::InputModelTFLiteImpl::InputModelTFLiteImpl(const GraphIteratorFlatBuffer::Ptr& graph_iterator, + const ov::frontend::InputModel& input_model, + const std::shared_ptr& telemetry) + : m_input_model(input_model), + m_graph_iterator(graph_iterator), + m_telemetry(telemetry) { + FRONT_END_GENERAL_CHECK(m_graph_iterator, "Null pointer specified for GraphIterator"); + loadModel(); +} + +std::vector InputModel::InputModelTFLiteImpl::getInputs() const { + return m_inputs; +} + +std::vector InputModel::InputModelTFLiteImpl::getOutputs() const { + return m_outputs; +} + +std::shared_ptr castToTensorPlace(const ov::frontend::Place::Ptr& place) { + if (auto var_place = std::dynamic_pointer_cast(place)) { + return var_place; + } + FRONT_END_GENERAL_CHECK(false, "Cannot cast this Place to TensorPlace."); +} + +ov::frontend::Place::Ptr InputModel::InputModelTFLiteImpl::getPlaceByTensorName(const std::string& tensorName) const { + if (m_tensor_places.find(tensorName) != m_tensor_places.end()) + return castToTensorPlace(m_tensor_places.at(tensorName)); + else + return nullptr; +} + +std::shared_ptr castToOpPlace(const ov::frontend::Place::Ptr& place) { + if (auto var_place = std::dynamic_pointer_cast(place)) { + return var_place; + } + FRONT_END_GENERAL_CHECK(false, "Cannot cast this Place to TensorPlace."); +} + +void InputModel::InputModelTFLiteImpl::setPartialShape(ov::frontend::Place::Ptr place, const PartialShape& shape) { + castToTensorPlace(place)->set_partial_shape(shape); +} + +ov::PartialShape InputModel::InputModelTFLiteImpl::getPartialShape(ov::frontend::Place::Ptr place) const { + return castToTensorPlace(place)->get_partial_shape(); +} + +void InputModel::InputModelTFLiteImpl::setElementType(ov::frontend::Place::Ptr place, const element::Type& type) { + castToTensorPlace(place)->set_element_type(type); +} + +ov::element::Type InputModel::InputModelTFLiteImpl::getElementType(ov::frontend::Place::Ptr place) const { + return castToTensorPlace(place)->get_element_type(); +} + +void InputModel::InputModelTFLiteImpl::setTensorValue(ov::frontend::Place::Ptr place, const void* value) { + auto tensor_place = castToTensorPlace(place); + auto p_shape = tensor_place->get_partial_shape(); + auto type = tensor_place->get_element_type(); + FRONT_END_GENERAL_CHECK(tensor_place->get_names().size() > 0, + "TensorFlow Lite Frontend: place to be frozen must have the name."); + auto name = tensor_place->get_names()[0]; + FRONT_END_GENERAL_CHECK(p_shape.is_static(), + "TensorFlow Lite Frontend: specify static shape for " + name + " to be frozen."); + FRONT_END_GENERAL_CHECK(type.is_static(), + "TensorFlow Lite Frontend: define static size type for " + name + " to be frozen."); + auto constant = opset10::Constant::create(type, p_shape.to_shape(), value); + constant->set_friendly_name(name); + m_tensor_values[name] = constant; +} + +void InputModel::InputModelTFLiteImpl::setNameForTensor(const Place::Ptr& tensor, const std::string& new_name) { + castToTensorPlace(tensor)->set_names({new_name}); +} + +void InputModel::InputModelTFLiteImpl::addNameForTensor(const Place::Ptr& tensor, const std::string& new_name) { + auto tf_tensor = castToTensorPlace(tensor); + auto names = tf_tensor->get_names(); + names.push_back(new_name); + tf_tensor->set_names(names); +} + +void InputModel::InputModelTFLiteImpl::setNameForOperation(const Place::Ptr& operation, const std::string& new_name) { + auto op = castToOpPlace(operation); + auto names = op->get_names(); + names.push_back(new_name); + op->set_names(names); +} + +void InputModel::InputModelTFLiteImpl::overrideAllInputs(const std::vector& inputs) { + for (const auto& input_place : m_inputs) { + auto input_lite_place = std::dynamic_pointer_cast(input_place); + FRONT_END_GENERAL_CHECK(input_lite_place != nullptr, "Input Model has unexpected place as input"); + input_lite_place->set_input_index(-1); + } + m_inputs.clear(); + for (const auto& input_place : inputs) { + m_inputs.push_back(castToTensorPlace(input_place)); + } + cleanUp(); +} + +void InputModel::InputModelTFLiteImpl::overrideAllOutputs(const std::vector& outputs) { + for (const auto& output_place : m_outputs) { + auto output_lite_place = + std::dynamic_pointer_cast(output_place); + FRONT_END_GENERAL_CHECK(output_lite_place != nullptr, "Input Model has unexpected place as output"); + output_lite_place->set_output_index(-1); + } + m_outputs.clear(); + for (const auto& output_place : outputs) { + m_outputs.push_back(castToTensorPlace(output_place)); + } + cleanUp(); +} + +void InputModel::InputModelTFLiteImpl::extractSubgraph(const std::vector& inputs, + const std::vector& outputs) { + for (const auto& input_place : m_inputs) { + auto input_lite_place = std::dynamic_pointer_cast(input_place); + FRONT_END_GENERAL_CHECK(input_lite_place != nullptr, "Input Model has unexpected place as input"); + input_lite_place->set_input_index(-1); + } + m_inputs.clear(); + for (const auto& input_place : inputs) { + m_inputs.push_back(castToTensorPlace(input_place)); + } + for (const auto& output_place : m_outputs) { + auto output_lite_place = + std::dynamic_pointer_cast(output_place); + FRONT_END_GENERAL_CHECK(output_lite_place != nullptr, "Input Model has unexpected place as output"); + output_lite_place->set_output_index(-1); + } + m_outputs.clear(); + for (const auto& output_place : outputs) { + m_outputs.push_back(castToTensorPlace(output_place)); + } + cleanUp(); +} + +void InputModel::InputModelTFLiteImpl::cleanUp() { + // TODO: remove all the unnecessary tensors and operations. Could be postponed as TF Lite is OOB type of FrontEnd +} + +InputModel::InputModel(const GraphIteratorFlatBuffer::Ptr& graph_iterator, + const std::shared_ptr& telemetry) + : _impl{std::make_shared(graph_iterator, *this, telemetry)} {} + +std::vector> InputModel::get_op_places() const { + return _impl->get_op_places(); +} + +std::map> InputModel::get_tensor_places() + const { + return _impl->get_tensor_places(); +} + +std::map> InputModel::get_tensor_values() const { + return _impl->get_tensor_values(); +} + +std::vector InputModel::get_inputs() const { + return _impl->getInputs(); +} + +std::vector InputModel::get_outputs() const { + return _impl->getOutputs(); +} + +ov::frontend::Place::Ptr InputModel::get_place_by_tensor_name(const std::string& tensorName) const { + return _impl->getPlaceByTensorName(tensorName); +} + +void InputModel::set_partial_shape(const Place::Ptr& place, const PartialShape& shape) { + _impl->setPartialShape(place, shape); +} + +ov::PartialShape InputModel::get_partial_shape(const Place::Ptr& place) const { + return _impl->getPartialShape(place); +} + +void InputModel::set_element_type(const Place::Ptr& place, const element::Type& type) { + _impl->setElementType(place, type); +} + +ov::element::Type InputModel::get_element_type(const Place::Ptr& place) const { + return _impl->getElementType(place); +} + +void InputModel::set_tensor_value(const Place::Ptr& place, const void* value) { + _impl->setTensorValue(place, value); +} + +void InputModel::set_name_for_tensor(const Place::Ptr& tensor, const std::string& new_name) { + _impl->setNameForTensor(tensor, new_name); +} + +void InputModel::add_name_for_tensor(const Place::Ptr& tensor, const std::string& new_name) { + _impl->addNameForTensor(tensor, new_name); +} + +void InputModel::set_name_for_operation(const Place::Ptr& operation, const std::string& new_name) { + _impl->setNameForOperation(operation, new_name); +} + +void InputModel::override_all_outputs(const std::vector& outputs) { + _impl->overrideAllOutputs(outputs); +} + +void InputModel::override_all_inputs(const std::vector& inputs) { + _impl->overrideAllInputs(inputs); +} + +void InputModel::extract_subgraph(const std::vector& inputs, + const std::vector& outputs) { + _impl->extractSubgraph(inputs, outputs); +} + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/input_model.hpp b/src/frontends/tensorflow_lite/src/input_model.hpp new file mode 100644 index 00000000000..8b4b415a36b --- /dev/null +++ b/src/frontends/tensorflow_lite/src/input_model.hpp @@ -0,0 +1,56 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "graph_iterator_flatbuffer.hpp" +#include "input_model.hpp" +#include "openvino/frontend/extension/telemetry.hpp" +#include "openvino/frontend/tensorflow_lite/frontend.hpp" +#include "openvino/opsets/opset1.hpp" +#include "tensor_lite_place.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class InputModel : public ov::frontend::InputModel { + friend class ov::frontend::tensorflow_lite::FrontEnd; + class InputModelTFLiteImpl; + std::shared_ptr _impl; + + std::vector> get_op_places() const; + std::map> get_tensor_places() const; + std::map> get_tensor_values() const; + +public: + explicit InputModel(const ov::frontend::tensorflow_lite::GraphIteratorFlatBuffer::Ptr& graph_iterator, + const std::shared_ptr& telemetry = {}); + + ///// Searching for places ///// + std::vector get_inputs() const override; + std::vector get_outputs() const override; + ov::frontend::Place::Ptr get_place_by_tensor_name(const std::string& tensorName) const override; + + ///// Naming and annotation ///// + void set_name_for_tensor(const Place::Ptr& tensor, const std::string& new_name) override; + void add_name_for_tensor(const Place::Ptr& tensor, const std::string& new_name) override; + void set_name_for_operation(const Place::Ptr& operation, const std::string& new_name) override; + + ///// Setting / getting tensor properties ///// + void set_partial_shape(const Place::Ptr& place, const ov::PartialShape& shape) override; + ov::PartialShape get_partial_shape(const Place::Ptr& place) const override; + void set_element_type(const Place::Ptr& place, const ov::element::Type& type) override; + ov::element::Type get_element_type(const Place::Ptr& place) const override; + void set_tensor_value(const Place::Ptr& place, const void* value) override; + + ///// Topology Editing ///// + void override_all_outputs(const std::vector& outputs) override; + void override_all_inputs(const std::vector& inputs) override; + void extract_subgraph(const std::vector& inputs, + const std::vector& outputs) override; +}; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/batch_matmul.cpp b/src/frontends/tensorflow_lite/src/op/batch_matmul.cpp new file mode 100644 index 00000000000..14927a984e5 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/batch_matmul.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector batch_matmul(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"adj_x", decoder->get_attribute(&tflite::BatchMatMulOptions::adj_x)}, + {"adj_y", decoder->get_attribute(&tflite::BatchMatMulOptions::adj_y)}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_batch_mat_mul_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/cast.cpp b/src/frontends/tensorflow_lite/src/op/cast.cpp new file mode 100644 index 00000000000..126bdec697b --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/cast.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector cast(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"DstT", get_ov_type(decoder->get_attribute(&tflite::CastOptions::out_data_type))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_cast_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/concatenation.cpp b/src/frontends/tensorflow_lite/src/op/concatenation.cpp new file mode 100644 index 00000000000..a1ed0d7ce72 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/concatenation.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector concatenation(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + int64_t axis = static_cast(decoder->get_attribute(&tflite::ConcatenationOptions::axis)); + auto concat = make_shared(node.get_inputs(), axis); + concat->set_friendly_name(decoder->get_op_name()); + return concat->outputs(); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/conv2d.cpp b/src/frontends/tensorflow_lite/src/op/conv2d.cpp new file mode 100644 index 00000000000..d5926d71a86 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/conv2d.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector conv2d(const ov::frontend::tensorflow_lite::NodeContext& node) { + auto decoder = get_conv_decoder_map("Conv2D", node); + FRONT_END_GENERAL_CHECK(node.get_input_size() >= 2, + "Unexpected number of input in node of type=", + node.get_op_type(), + " name=", + node.get_name()); + OutputVector output; + get_conv(output, node, decoder, &ov::frontend::tensorflow::op::translate_conv_2d_op); + get_bias(output, node, decoder); + get_activation(output, decoder); + output[0].get_node_shared_ptr()->set_friendly_name(node.get_name()); + return output; +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/depth_to_space.cpp b/src/frontends/tensorflow_lite/src/op/depth_to_space.cpp new file mode 100644 index 00000000000..7c658794eab --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/depth_to_space.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector depth_to_space(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"block_size", static_cast(decoder->get_attribute(&tflite::DepthToSpaceOptions::block_size))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_depth_to_space_op, "DepthToSpace"); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/depthwise_conv2d.cpp b/src/frontends/tensorflow_lite/src/op/depthwise_conv2d.cpp new file mode 100644 index 00000000000..126a6adde3c --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/depthwise_conv2d.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector depthwise_conv2d(const ov::frontend::tensorflow_lite::NodeContext& node) { + auto decoder = get_conv_decoder_map("DepthwiseConv2dNative", node); + FRONT_END_GENERAL_CHECK(node.get_input_size() >= 2, + "Unexpected number of input in node of type=", + node.get_op_type(), + " name=", + node.get_name()); + OutputVector output; + get_conv(output, node, decoder, &ov::frontend::tensorflow::op::translate_depthwise_conv_2d_native_op); + get_bias(output, node, decoder); + get_activation(output, decoder); + output[0].get_node_shared_ptr()->set_friendly_name(node.get_name()); + return output; +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/fully_connected.cpp b/src/frontends/tensorflow_lite/src/op/fully_connected.cpp new file mode 100644 index 00000000000..aa853e90f6a --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/fully_connected.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector fully_connected(const ov::frontend::tensorflow_lite::NodeContext& node) { + using FCOptions = tflite::FullyConnectedOptions; + const auto& decoder = get_decoder(node); + auto data = node.get_input(0); + auto weights = node.get_input(1); + if (decoder->get_attribute(&FCOptions::weights_format) != tflite::FullyConnectedOptionsWeightsFormat_DEFAULT) { + FRONT_END_NOT_IMPLEMENTED( + "FullyConnectedOptions::weights_format != FullyConnectedOptionsWeightsFormat_DEFAULT"); + } + if (!decoder->get_attribute(&FCOptions::keep_num_dims)) { + // Everything is 2D now -- insert Reshape + // weights = Reshape; + } + auto output = std::make_shared(data, weights, false, true)->outputs(); + auto activation_name = + EnumNameActivationFunctionType(decoder->get_attribute(&FCOptions::fused_activation_function)); + get_activation(output, node, activation_name); + output[0].get_node_shared_ptr()->set_friendly_name(decoder->get_op_name()); + return output; +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/gather.cpp b/src/frontends/tensorflow_lite/src/op/gather.cpp new file mode 100644 index 00000000000..28701f0f996 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/gather.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector gather(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + auto batch_dims = static_cast(decoder->get_attribute(&tflite::GatherOptions::batch_dims)); + auto axis = opset10::Constant::create(element::i32, {}, {decoder->get_attribute(&tflite::GatherOptions::axis)}); + auto input = node.get_input(0); + auto input_indices = node.get_input(1); + auto res = make_shared(input, input_indices, axis, batch_dims); + res->set_friendly_name(node.get_name()); + return res->outputs(); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/leaky_relu.cpp b/src/frontends/tensorflow_lite/src/op/leaky_relu.cpp new file mode 100644 index 00000000000..ee616e4b4f7 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/leaky_relu.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector leaky_relu(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"alpha", decoder->get_attribute(&tflite::LeakyReluOptions::alpha)}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_leaky_relu_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/mirror_pad.cpp b/src/frontends/tensorflow_lite/src/op/mirror_pad.cpp new file mode 100644 index 00000000000..761e103ddb1 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/mirror_pad.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector mirror_pad(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"mode", string(EnumNameMirrorPadMode(decoder->get_attribute(&tflite::MirrorPadOptions::mode)))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_mirror_pad_op, "MirrorPad"); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/one_hot.cpp b/src/frontends/tensorflow_lite/src/op/one_hot.cpp new file mode 100644 index 00000000000..bcaed617f26 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/one_hot.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector one_hot(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"axis", static_cast(decoder->get_attribute(&tflite::OneHotOptions::axis))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_one_hot_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/op_translation_utils.cpp b/src/frontends/tensorflow_lite/src/op/op_translation_utils.cpp new file mode 100644 index 00000000000..4dd5cb160d2 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/op_translation_utils.cpp @@ -0,0 +1,155 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "op_translation_utils.hpp" + +#include +#include +#include + +#include "openvino/core/node_vector.hpp" +#include "openvino/frontend/tensorflow_lite/node_context.hpp" +#include "openvino_conversions.hpp" +#include "utils.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +void set_output_names(const ov::frontend::tensorflow_lite::NodeContext& node, OutputVector& outputs) { + const auto& decoder_with_name = std::dynamic_pointer_cast(node.get_decoder()); + FRONT_END_GENERAL_CHECK(decoder_with_name != nullptr, + "Unexpected decoder during operation translation. Expected DecoderFlatBuffer"); + FRONT_END_GENERAL_CHECK(outputs.size() == decoder_with_name->get_output_size(), + "Unexpected decoder during operation translation. Expected DecoderFlatBuffer"); + for (size_t i = 0; i < decoder_with_name->get_output_size(); ++i) { + outputs[i].set_names({decoder_with_name->get_output_tensor_name(i)}); + } +} + +void del_output_names(OutputVector& outputs) { + for (auto& output : outputs) { + output.set_names({}); + } +} + +void get_conv(ov::OutputVector& output, + const ov::frontend::NodeContext& node, + const std::shared_ptr& decoder, + ov::OutputVector (*converter)(const ov::frontend::NodeContext&)) { + ov::OutputVector inputs = {node.get_input(0), + ov::frontend::tensorflow::make_transpose(node.get_input(1), ov::AxisVector{1, 2, 3, 0})}; + auto context = ov::frontend::tensorflow_lite::NodeContext(decoder, inputs); + output = converter(context); + del_output_names(output); +} + +void get_pool(ov::OutputVector& output, + const ov::frontend::NodeContext& node, + const std::shared_ptr& decoder, + ov::OutputVector (*converter)(const ov::frontend::NodeContext&)) { + ov::OutputVector inputs = {node.get_input(0)}; + auto context = ov::frontend::tensorflow_lite::NodeContext(decoder, inputs); + output = converter(context); + del_output_names(output); +} + +void get_bias(ov::OutputVector& output, + const ov::frontend::NodeContext& node, + const std::shared_ptr& decoder) { + if (node.get_input_size() == 3) { + const OutputVector inputs_for_bias = {output[0], node.get_input(2)}; + auto context_for_bias_add = ov::frontend::tensorflow_lite::NodeContext(decoder, inputs_for_bias); + // FIXME: dependence on layout? + output = ov::frontend::tensorflow::op::translate_binary_op(context_for_bias_add); + del_output_names(output); + } +} + +void get_activation(ov::OutputVector& output, + const ov::frontend::tensorflow_lite::NodeContext& node, + const std::string& activation) { + if (activation == "RELU") { + output = ov::frontend::tensorflow::op::translate_unary_op(node); + } else if (activation == "RELU6") { + output = ov::frontend::tensorflow::op::translate_relu_6_op(node); + } else if (activation == "TANH") { + output = ov::frontend::tensorflow::op::translate_unary_op(node); + } else { + // TODO: Fused activation to support: + // RELU_N1_TO_1 = 2, + // SIGN_BIT = 5, + if (activation != "NONE") { + FRONT_END_THROW("Unknown Activation fused to " + node.get_decoder()->get_op_type() + ": " + activation); + } + } + del_output_names(output); +} + +void get_activation(ov::OutputVector& output, + const std::shared_ptr& decoder) { + auto context_for_activation = ov::frontend::tensorflow_lite::NodeContext(decoder, output); + const auto activation = decoder->get_attribute("activation").as(); + get_activation(output, context_for_activation, activation); +} + +std::shared_ptr get_pool_decoder_map( + const std::string& new_type_name, + const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = std::dynamic_pointer_cast(node.get_decoder()); + FRONT_END_GENERAL_CHECK(decoder != nullptr, + "Unexpected decoder during operation translation. Expected DecoderFlatBuffer"); + + const std::map attrs{ + {"strides", + std::vector{1, + decoder->get_attribute(&tflite::Pool2DOptions::stride_h), + decoder->get_attribute(&tflite::Pool2DOptions::stride_w), + 1}}, + {"padding", std::string(EnumNamePadding(decoder->get_attribute(&tflite::Pool2DOptions::padding)))}, + {"ksize", + std::vector{1, + decoder->get_attribute(&tflite::Pool2DOptions::filter_height), + decoder->get_attribute(&tflite::Pool2DOptions::filter_width), + 1}}, + {"data_format", "NHWC"}, + {"activation", + EnumNameActivationFunctionType(decoder->get_attribute(&tflite::Pool2DOptions::fused_activation_function))}, + }; + return std::make_shared(node.get_decoder(), attrs, new_type_name, true); +} + +OutputVector attribute_helper(const ov::frontend::tensorflow_lite::NodeContext& node, + const std::map& attrs, + ov::OutputVector (*converter)(const ov::frontend::NodeContext&), + std::string new_op_type, + bool empty_name) { + const auto& original_decoder = std::dynamic_pointer_cast(node.get_decoder()); + FRONT_END_GENERAL_CHECK(original_decoder != nullptr, + "Unexpected decoder during operation translation. Expected DecoderFlatBuffer"); + auto decoder = std::make_shared( + original_decoder, + attrs, + (new_op_type.empty() ? original_decoder->get_op_type() : new_op_type), + empty_name); + + OutputVector inputs = node.get_inputs(); + auto context = ov::frontend::tensorflow_lite::NodeContext(decoder, inputs); + auto outputs = converter(context); + del_output_names(outputs); + return outputs; +} + +std::shared_ptr get_decoder(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = std::dynamic_pointer_cast(node.get_decoder()); + FRONT_END_GENERAL_CHECK(decoder != nullptr, + "Unexpected decoder during operation translation. Expected DecoderFlatBuffer"); + return decoder; +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/op_translation_utils.hpp b/src/frontends/tensorflow_lite/src/op/op_translation_utils.hpp new file mode 100644 index 00000000000..2df07178441 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/op_translation_utils.hpp @@ -0,0 +1,117 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +#include "common_op_table.hpp" +#include "decoder_map.hpp" +#include "openvino/core/node_vector.hpp" +#include "openvino/frontend/tensorflow_lite/node_context.hpp" +#include "openvino/opsets/opset10.hpp" +#include "openvino_conversions.hpp" +#include "utils.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +std::shared_ptr get_decoder(const ov::frontend::tensorflow_lite::NodeContext& node); +void set_output_names(const ov::frontend::tensorflow_lite::NodeContext& node, OutputVector& outputs); +void del_output_names(OutputVector& outputs); + +// convolutions +template +std::shared_ptr get_conv_decoder_map( + const std::string& new_type_name, + const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + const std::map attrs{ + {"strides", + std::vector{1, decoder->get_attribute(&T::stride_h), decoder->get_attribute(&T::stride_w), 1}}, + {"padding", std::string(EnumNamePadding(decoder->get_attribute(&T::padding)))}, + {"dilations", + std::vector{1, + decoder->get_attribute(&T::dilation_h_factor), + decoder->get_attribute(&T::dilation_w_factor), + 1}}, + {"data_format", "NHWC"}, + {"activation", EnumNameActivationFunctionType(decoder->get_attribute(&T::fused_activation_function))}, + }; + return std::make_shared(node.get_decoder(), attrs, new_type_name, true); +} +void get_conv(ov::OutputVector& output, + const ov::frontend::NodeContext& node, + const std::shared_ptr& decoder, + ov::OutputVector (*converter)(const ov::frontend::NodeContext&)); +void get_bias(ov::OutputVector& output, + const ov::frontend::NodeContext& node, + const std::shared_ptr& decoder); +void get_activation(ov::OutputVector& output, + const std::shared_ptr& decoder); +void get_activation(ov::OutputVector& output, + const ov::frontend::tensorflow_lite::NodeContext& node, + const std::string& activation); + +std::shared_ptr get_pool_decoder_map( + const std::string& new_type_name, + const ov::frontend::tensorflow_lite::NodeContext& node); +void get_pool(ov::OutputVector& output, + const ov::frontend::NodeContext& node, + const std::shared_ptr& decoder, + ov::OutputVector (*converter)(const ov::frontend::NodeContext&)); + +template +OutputVector translate_binary_op_with_activation(const ov::frontend::tensorflow_lite::NodeContext& node) { + auto output = ov::frontend::tensorflow::op::translate_binary_op(node); + const auto& decoder = get_decoder(node); + get_activation(output, + node, + EnumNameActivationFunctionType(decoder->get_attribute(&TF_TYPE::fused_activation_function))); + return output; +} + +template OutputVector translate_binary_op_with_activation( + const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_binary_op_with_activation( + const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_binary_op_with_activation( + const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_binary_op_with_activation( + const ov::frontend::tensorflow_lite::NodeContext& node); + +OutputVector attribute_helper(const ov::frontend::tensorflow_lite::NodeContext& node, + const std::map& attrs, + ov::OutputVector (*converter)(const ov::frontend::NodeContext&), + std::string new_op_type = "", + bool empty_name = false); + +template +OutputVector translate_reduce_op(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& original_decoder = std::dynamic_pointer_cast(node.get_decoder()); + FRONT_END_GENERAL_CHECK(original_decoder != nullptr, + "Unexpected decoder during operation translation. Expected DecoderFlatBuffer"); + const std::map attrs{ + {"keep_dims", original_decoder->get_attribute(&tflite::ReducerOptions::keep_dims)}}; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_direct_reduce_op); +} + +template OutputVector translate_reduce_op(const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_reduce_op( + const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_reduce_op( + const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_reduce_op(const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_reduce_op(const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_reduce_op(const ov::frontend::tensorflow_lite::NodeContext& node); +template OutputVector translate_reduce_op(const ov::frontend::tensorflow_lite::NodeContext& node); + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/pack.cpp b/src/frontends/tensorflow_lite/src/op/pack.cpp new file mode 100644 index 00000000000..7422c4c12dc --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/pack.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector pack(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + const std::map attrs{ + {"axis", static_cast(decoder->get_attribute(&tflite::PackOptions::axis))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_pack_op, "Pack"); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/pool2d.cpp b/src/frontends/tensorflow_lite/src/op/pool2d.cpp new file mode 100644 index 00000000000..b8546a89b5e --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/pool2d.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector pooling(const ov::frontend::tensorflow_lite::NodeContext& node, + const std::string& type_name, + ov::OutputVector (*converter)(const ov::frontend::NodeContext&)) { + auto decoder_for_tf_translator = get_pool_decoder_map(type_name, node); + FRONT_END_GENERAL_CHECK(node.get_input_size() == 1, + "Unexpected number of input in node of type=", + node.get_op_type(), + " name=", + node.get_name()); + OutputVector output; + get_pool(output, node, decoder_for_tf_translator, converter); + get_activation(output, decoder_for_tf_translator); + output[0].get_node_shared_ptr()->set_friendly_name(node.get_name()); + return output; +} + +OutputVector max_pool_2d(const ov::frontend::tensorflow_lite::NodeContext& node) { + return pooling(node, "MaxPool", &ov::frontend::tensorflow::op::translate_max_pool_op); +} + +OutputVector avg_pool_2d(const ov::frontend::tensorflow_lite::NodeContext& node) { + return pooling(node, "AvgPool", &ov::frontend::tensorflow::op::translate_avg_pool_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/range.cpp b/src/frontends/tensorflow_lite/src/op/range.cpp new file mode 100644 index 00000000000..08ed4fa9afb --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/range.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector range(const ov::frontend::tensorflow_lite::NodeContext& node) { + std::map attrs{ + {"Tidx", node.get_input(0).get_element_type()}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_range_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/reshape.cpp b/src/frontends/tensorflow_lite/src/op/reshape.cpp new file mode 100644 index 00000000000..bd8dd962703 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/reshape.cpp @@ -0,0 +1,41 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector reshape(const ov::frontend::tensorflow_lite::NodeContext& node) { + size_t input_size = node.get_input_size(); + FRONT_END_GENERAL_CHECK(input_size == 1 || input_size == 2, + "Unexpected number of inputs -- ", + input_size, + ", for node ", + node.get_op_type()); + + Output shape; + if (input_size == 1) { + const auto& decoder = get_decoder(node); + auto reshape_new_shape = decoder->get_attribute(&tflite::ReshapeOptions::new_shape); + const auto new_shape = std::vector(reshape_new_shape->begin(), reshape_new_shape->end()); + shape = opset10::Constant::create(element::i64, ov::Shape{new_shape.size()}, new_shape); + } else { + shape = node.get_input(1); + } + auto reshape = std::make_shared(node.get_input(0), shape, false); + reshape->set_friendly_name(node.get_name()); + return reshape->outputs(); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/resize.cpp b/src/frontends/tensorflow_lite/src/op/resize.cpp new file mode 100644 index 00000000000..15cbb30d788 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/resize.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; +using namespace ov::frontend::tensorflow::op; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector resize_bilinear(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + const std::map attrs{ + {"align_corners", decoder->get_attribute(&tflite::ResizeBilinearOptions::align_corners)}, + {"half_pixel_centers", decoder->get_attribute(&tflite::ResizeBilinearOptions::half_pixel_centers)}, + }; + return attribute_helper(node, attrs, translate_interpolate_op, "ResizeBilinear"); +} + +OutputVector resize_nearest_neightbor(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + const std::map attrs{ + {"align_corners", decoder->get_attribute(&tflite::ResizeNearestNeighborOptions::align_corners)}, + {"half_pixel_centers", false}, + }; + return attribute_helper(node, attrs, translate_interpolate_op, "ResizeNearestNeighbor"); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/reverse_sequence.cpp b/src/frontends/tensorflow_lite/src/op/reverse_sequence.cpp new file mode 100644 index 00000000000..c719d0d6eb4 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/reverse_sequence.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; +using namespace ov::frontend::tensorflow::op; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector reverse_sequence(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"seq_dim", static_cast(decoder->get_attribute(&tflite::ReverseSequenceOptions::seq_dim))}, + {"batch_dim", static_cast(decoder->get_attribute(&tflite::ReverseSequenceOptions::batch_dim))}, + }; + return attribute_helper(node, attrs, translate_reverse_sequence_op, "ReverseSequence"); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/shape.cpp b/src/frontends/tensorflow_lite/src/op/shape.cpp new file mode 100644 index 00000000000..1d45ea668d9 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/shape.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector shape(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"out_type", get_ov_type(decoder->get_attribute(&tflite::ShapeOptions::out_type))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_shape_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/softmax.cpp b/src/frontends/tensorflow_lite/src/op/softmax.cpp new file mode 100644 index 00000000000..290455ed05a --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/softmax.cpp @@ -0,0 +1,33 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector softmax(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + auto beta = decoder->get_attribute(&tflite::SoftmaxOptions::beta); + Output output = node.get_input(0); + if (beta != 1.) { + auto beta_const = opset10::Constant::create(element::f32, Shape{}, vector{beta}); + auto mul_data = make_shared(beta_const, output); + output = make_shared(output, mul_data); + } + output = make_shared(output, -1); + output.get_node()->set_friendly_name(decoder->get_op_name()); + return {output}; +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/space_to_depth.cpp b/src/frontends/tensorflow_lite/src/op/space_to_depth.cpp new file mode 100644 index 00000000000..d9de93c222c --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/space_to_depth.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector space_to_depth(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"seed", static_cast(decoder->get_attribute(&tflite::SpaceToDepthOptions::block_size))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_space_to_depth_op, "SpaceToDepth"); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/split.cpp b/src/frontends/tensorflow_lite/src/op/split.cpp new file mode 100644 index 00000000000..2a95f544adb --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/split.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector split(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"num_split", static_cast(decoder->get_attribute(&tflite::SplitOptions::num_splits))}}; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_split_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/squeeze.cpp b/src/frontends/tensorflow_lite/src/op/squeeze.cpp new file mode 100644 index 00000000000..8dc00d60f0b --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/squeeze.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector squeeze(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + auto squeeze_dims = decoder->get_attribute(&tflite::SqueezeOptions::squeeze_dims); + std::vector axes{squeeze_dims->begin(), squeeze_dims->end()}; + return attribute_helper(node, {{"axis", axes}}, ov::frontend::tensorflow::op::translate_squeeze_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/strided_slice.cpp b/src/frontends/tensorflow_lite/src/op/strided_slice.cpp new file mode 100644 index 00000000000..0ceb44df7c6 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/strided_slice.cpp @@ -0,0 +1,32 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector strided_slice(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"begin_mask", static_cast(decoder->get_attribute(&tflite::StridedSliceOptions::begin_mask))}, + {"end_mask", static_cast(decoder->get_attribute(&tflite::StridedSliceOptions::end_mask))}, + {"new_axis_mask", static_cast(decoder->get_attribute(&tflite::StridedSliceOptions::new_axis_mask))}, + {"ellipsis_mask", static_cast(decoder->get_attribute(&tflite::StridedSliceOptions::ellipsis_mask))}, + {"shrink_axis_mask", + static_cast(decoder->get_attribute(&tflite::StridedSliceOptions::shrink_axis_mask))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_strided_slice_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/unique.cpp b/src/frontends/tensorflow_lite/src/op/unique.cpp new file mode 100644 index 00000000000..557e60577f1 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/unique.cpp @@ -0,0 +1,27 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector unique(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"out_idx", get_ov_type(decoder->get_attribute(&tflite::UniqueOptions::idx_out_type))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_unique_op, "Unique"); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op/unpack.cpp b/src/frontends/tensorflow_lite/src/op/unpack.cpp new file mode 100644 index 00000000000..c6f87224f52 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op/unpack.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "common_op_table.hpp" +#include "op_translation_utils.hpp" +#include "utils.hpp" + +using namespace std; + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { + +OutputVector unpack(const ov::frontend::tensorflow_lite::NodeContext& node) { + const auto& decoder = get_decoder(node); + std::map attrs{ + {"axis", static_cast(decoder->get_attribute(&tflite::UnpackOptions::axis))}, + {"num", static_cast(decoder->get_attribute(&tflite::UnpackOptions::num))}, + }; + return attribute_helper(node, attrs, ov::frontend::tensorflow::op::translate_unpack_op); +} + +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op_table.cpp b/src/frontends/tensorflow_lite/src/op_table.cpp new file mode 100644 index 00000000000..1b61b3075b5 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op_table.cpp @@ -0,0 +1,194 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "op_table.hpp" + +#include "decoder_map.hpp" +#include "openvino/opsets/opset10.hpp" + +using namespace std; +using namespace ov; + +#define OP_CONVERT_TYPE_RENAME(func, name) \ + [](const ov::frontend::tensorflow_lite::NodeContext& node) -> OutputVector { \ + auto decoder = make_shared(node.get_decoder(), std::map{}, name, false); \ + auto inputs = node.get_inputs(); \ + auto context = frontend::tensorflow_lite::NodeContext(decoder, inputs); \ + return func(context); \ + } + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { +std::map get_supported_ops() { + return { + {"ABS", ov::frontend::tensorflow::op::translate_unary_op}, + {"ADD", translate_binary_op_with_activation}, + {"ADD_N", ov::frontend::tensorflow::op::translate_add_n_op}, + // ARG_MAX + // ARG_MIN + // ASSIGN_VARIABLE + // ATAN2 + {"AVERAGE_POOL_2D", avg_pool_2d}, + {"BATCH_MATMUL", batch_matmul}, + {"BATCH_TO_SPACE_ND", + OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_batch_to_space_nd_op, "BatchToSpaceND")}, + // BIDIRECTIONAL_SEQUENCE_LSTM + // BIDIRECTIONAL_SEQUENCE_RNN + {"BROADCAST_ARGS", + OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_broadcast_args_op, "BroadcastArgs")}, + {"BROADCAST_TO", + OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_broadcast_to_op, "BroadcastTo")}, + // BUCKETIZE + // CALL + // CALL_ONCE + {"CAST", cast}, + {"CEIL", ov::frontend::tensorflow::op::translate_unary_op}, + // COMPLEX_ABS + // CONCAT_EMBEDDINGS + {"CONCATENATION", concatenation}, + {"CONV_2D", conv2d}, + // CONV_3D + // CONV_3D_TRANSPOSE + {"COS", ov::frontend::tensorflow::op::translate_unary_op}, + // CUMSUM + // CUSTOM + // DELEGATE + // DENSIFY + {"DEPTH_TO_SPACE", depth_to_space}, + {"DEPTHWISE_CONV_2D", depthwise_conv2d}, + // DEQUANTIZE + {"DIV", translate_binary_op_with_activation}, + // DYNAMIC_UPDATE_SLICE + {"ELU", ov::frontend::tensorflow::op::translate_elu_op}, + // EMBEDDING_LOOKUP + // EMBEDDING_LOOKUP_SPARSE + {"EQUAL", ov::frontend::tensorflow::op::translate_binary_op}, + {"EXP", ov::frontend::tensorflow::op::translate_unary_op}, + {"EXPAND_DIMS", ov::frontend::tensorflow::op::translate_expand_dims_op}, + // FAKE_QUANT + {"FILL", ov::frontend::tensorflow::op::translate_fill_op}, + {"FLOOR", ov::frontend::tensorflow::op::translate_unary_op}, + {"FLOOR_DIV", ov::frontend::tensorflow::op::translate_floor_div_op}, + {"FLOOR_MOD", ov::frontend::tensorflow::op::translate_binary_op}, + {"FULLY_CONNECTED", fully_connected}, + {"GATHER", gather}, + {"GATHER_ND", ov::frontend::tensorflow::op::translate_gather_nd_op}, + // GELU + {"GREATER", ov::frontend::tensorflow::op::translate_binary_op}, + {"GREATER_EQUAL", ov::frontend::tensorflow::op::translate_binary_op}, + {"HARD_SWISH", ov::frontend::tensorflow::op::translate_unary_op}, + // HASHTABLE + // HASHTABLE_FIND + // HASHTABLE_IMPORT + // HASHTABLE_LOOKUP + // HASHTABLE_SIZE + // IF + // IMAG + // L2_NORMALIZATION + // L2_POOL_2D + {"LEAKY_RELU", leaky_relu}, + {"LESS", ov::frontend::tensorflow::op::translate_binary_op}, + {"LESS_EQUAL", ov::frontend::tensorflow::op::translate_binary_op}, + // LOCAL_RESPONSE_NORMALIZATION + {"LOG", ov::frontend::tensorflow::op::translate_unary_op}, + {"LOG_SOFTMAX", ov::frontend::tensorflow::op::translate_log_softmax_op}, + {"LOGICAL_AND", ov::frontend::tensorflow::op::translate_binary_op}, + {"LOGICAL_NOT", ov::frontend::tensorflow::op::translate_unary_op}, + {"LOGICAL_OR", ov::frontend::tensorflow::op::translate_binary_op}, + {"LOGISTIC", ov::frontend::tensorflow::op::translate_unary_op}, + // LSH_PROJECTION + // LSTM + {"MATRIX_DIAG", ov::frontend::tensorflow::op::translate_matrix_diag_op}, + // MATRIX_SET_DIAG + {"MAX_POOL_2D", max_pool_2d}, + {"MAXIMUM", ov::frontend::tensorflow::op::translate_binary_op}, + {"MEAN", translate_reduce_op}, + {"MINIMUM", ov::frontend::tensorflow::op::translate_binary_op}, + {"MIRROR_PAD", mirror_pad}, + {"MUL", translate_binary_op_with_activation}, + // MULTINOMIAL + {"NEG", ov::frontend::tensorflow::op::translate_unary_op}, + // NON_MAX_SUPPRESSION_V4 + // NON_MAX_SUPPRESSION_V5 + {"NOT_EQUAL", ov::frontend::tensorflow::op::translate_binary_op}, + {"ONE_HOT", one_hot}, + {"PACK", pack}, + {"PAD", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_pad_op, "Pad")}, + {"PADV2", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_padv2_op, "PadV2")}, + {"POW", ov::frontend::tensorflow::op::translate_binary_op}, + {"PRELU", ov::frontend::tensorflow::op::translate_binary_op}, + // QUANTIZE + // RANDOM_STANDARD_NORMAL + // RANDOM_UNIFORM + {"RANGE", range}, + {"RANK", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_rank_op, "Rank")}, + // READ_VARIABLE + // REAL + {"REDUCE_ALL", translate_reduce_op}, + {"REDUCE_ANY", translate_reduce_op}, + {"REDUCE_MAX", translate_reduce_op}, + {"REDUCE_MIN", translate_reduce_op}, + {"REDUCE_PROD", translate_reduce_op}, + {"RELU", ov::frontend::tensorflow::op::translate_unary_op}, + // RELU_0_TO_1 + // RELU_N1_TO_1 + {"RELU6", ov::frontend::tensorflow::op::translate_relu_6_op}, + {"RESHAPE", reshape}, + {"RESIZE_BILINEAR", resize_bilinear}, + {"RESIZE_NEAREST_NEIGHBOR", resize_nearest_neightbor}, + {"REVERSE_SEQUENCE", reverse_sequence}, + {"REVERSE_V2", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_reverse_v2_op, "ReverseV2")}, + // RFFT2D + // RNN + {"ROUND", ov::frontend::tensorflow::op::translate_round_op}, + {"RSQRT", ov::frontend::tensorflow::op::translate_rsqrt_op}, + {"SCATTER_ND", ov::frontend::tensorflow::op::translate_scatter_nd_op}, + {"SEGMENT_SUM", ov::frontend::tensorflow::op::translate_segment_sum_op}, + {"SELECT", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_select_op, "Select")}, + {"SELECT_V2", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_select_v2_op, "SelectV2")}, + {"SHAPE", shape}, + {"SIGN", ov::frontend::tensorflow::op::translate_unary_op}, + {"SIN", ov::frontend::tensorflow::op::translate_unary_op}, + // SKIP_GRAM + {"SLICE", ov::frontend::tensorflow::op::translate_slice_op}, + {"SOFTMAX", softmax}, + {"SPACE_TO_BATCH_ND", + OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_space_to_batch_nd_op, "SpaceToBatchND")}, + {"SPACE_TO_DEPTH", space_to_depth}, + // SPARSE_TO_DENSE + {"SPLIT", split}, + {"SPLIT_V", ov::frontend::tensorflow::op::translate_split_v_op}, + {"SQRT", ov::frontend::tensorflow::op::translate_sqrt_op}, + {"SQUARE", ov::frontend::tensorflow::op::translate_square_op}, + {"SQUARED_DIFFERENCE", ov::frontend::tensorflow::op::translate_binary_op}, + {"SQUEEZE", squeeze}, + {"STRIDED_SLICE", strided_slice}, + {"SUB", translate_binary_op_with_activation}, + {"SUM", translate_reduce_op}, + // SVDF + {"TANH", ov::frontend::tensorflow::op::translate_unary_op}, + {"TILE", ov::frontend::tensorflow::op::translate_tile_op}, + {"TOPK_V2", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_top_k_v2_op, "TopKV2")}, + {"TRANSPOSE", ov::frontend::tensorflow::op::translate_transpose_op}, + // TRANSPOSE_CONV + // UNIDIRECTIONAL_SEQUENCE_LSTM + // UNIDIRECTIONAL_SEQUENCE_RNN + {"UNIQUE", unique}, + {"UNPACK", unpack}, + // UNSORTED_SEGMENT_MAX + // UNSORTED_SEGMENT_MIN + // UNSORTED_SEGMENT_PROD + // UNSORTED_SEGMENT_SUM + // VAR_HANDLE + {"WHERE", OP_CONVERT_TYPE_RENAME(ov::frontend::tensorflow::op::translate_where_op, "Where")}, + // WHILE + {"ZEROS_LIKE", ov::frontend::tensorflow::op::translate_zeros_like_op}, + }; +} +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/op_table.hpp b/src/frontends/tensorflow_lite/src/op_table.hpp new file mode 100644 index 00000000000..b84d14d744c --- /dev/null +++ b/src/frontends/tensorflow_lite/src/op_table.hpp @@ -0,0 +1,63 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +#include "common_op_table.hpp" +#include "decoder_map.hpp" +#include "openvino/core/node_vector.hpp" +#include "openvino/frontend/tensorflow_lite/node_context.hpp" +#include "openvino/opsets/opset10.hpp" +#include "openvino_conversions.hpp" +#include "utils.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { +namespace op { +using CreatorFunction = std::function; + +std::map get_supported_ops(); + +OutputVector batch_matmul(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector cast(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector conv2d(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector depthwise_conv2d(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector fully_connected(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector max_pool_2d(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector avg_pool_2d(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector concatenation(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector reshape(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector pack(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector softmax(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector resize_nearest_neightbor(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector resize_bilinear(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector squeeze(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector split(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector shape(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector range(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector strided_slice(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector gather(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector space_to_depth(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector depth_to_space(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector leaky_relu(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector mirror_pad(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector one_hot(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector reverse_sequence(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector unique(const ov::frontend::tensorflow_lite::NodeContext& node); +OutputVector unpack(const ov::frontend::tensorflow_lite::NodeContext& node); + +template +OutputVector translate_binary_op_with_activation(const ov::frontend::tensorflow_lite::NodeContext& node); + +template +OutputVector translate_reduce_op(const ov::frontend::tensorflow_lite::NodeContext& node); +} // namespace op +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/quantization_info.hpp b/src/frontends/tensorflow_lite/src/quantization_info.hpp new file mode 100644 index 00000000000..391b71fbb2d --- /dev/null +++ b/src/frontends/tensorflow_lite/src/quantization_info.hpp @@ -0,0 +1,69 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include +#include +#include + +#include "common_op_table.hpp" +#include "decoder_map.hpp" +#include "openvino/core/node_vector.hpp" +#include "openvino/opsets/opset10.hpp" +#include "openvino_conversions.hpp" +#include "utils.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class QuantizationInfo : public ov::RuntimeAttribute { +public: + OPENVINO_RTTI("QuantizationInfo"); + QuantizationInfo() = default; + explicit QuantizationInfo(const std::vector& scale, + const std::vector& zero_point, + const int64_t& axis) + : m_scale(scale), + m_zero_point(zero_point), + m_axis(axis) {} + + bool is_copyable() const override { + return false; + } + const std::vector& get_scale() const { + return m_scale; + } + void set_scale(const std::vector& scale) { + m_scale = scale; + } + const std::vector& get_zero_point() const { + return m_zero_point; + } + void set_zero_point(const std::vector& zero_point) { + m_zero_point = zero_point; + } + const int64_t& get_axis() const { + return m_axis; + } + void set_axis(const int64_t& axis) { + m_axis = axis; + } + bool is_disabled() const { + return m_disabled; + } + void disable() { + m_disabled = true; + } + +private: + std::vector m_scale; + std::vector m_zero_point; + int64_t m_axis{}; + bool m_disabled = false; +}; +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/schema/schema.fbs b/src/frontends/tensorflow_lite/src/schema/schema.fbs new file mode 100644 index 00000000000..b0d8e35fa8e --- /dev/null +++ b/src/frontends/tensorflow_lite/src/schema/schema.fbs @@ -0,0 +1,1333 @@ +// 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. + +// Revision History +// Version 0: Initial version. +// Version 1: Add subgraphs to schema. +// Version 2: Rename operators to conform to NN API. +// Version 3: Move buffer data from Model.Subgraph.Tensors to Model.Buffers. +// Version 3a: Add new builtin op code field. Has backward compatibility with +// version 3. +// Version 3b: Rename fields in SignatureDef. Has backward compatibility with +// version 3 and 3a. + +namespace tflite; + +// This corresponds to the version. +file_identifier "TFL3"; +// File extension of any written files. +file_extension "tflite"; + +// IMPORTANT: All new members of tables, enums and unions must be added at the +// end to ensure backwards compatibility. + +// The type of data stored in a tensor. +enum TensorType : byte { + FLOAT32 = 0, + FLOAT16 = 1, + INT32 = 2, + UINT8 = 3, + INT64 = 4, + STRING = 5, + BOOL = 6, + INT16 = 7, + COMPLEX64 = 8, + INT8 = 9, + FLOAT64 = 10, + COMPLEX128 = 11, + UINT64 = 12, + // Experimental: Resource and variant types are experimental, that are subject + // to change. Do not implement custom kernels using resource & variant types + // now. + RESOURCE = 13, + VARIANT = 14, + UINT32 = 15, + UINT16 = 16, + INT4 = 17, +} + +// Custom quantization parameters for experimenting with new quantization +// techniques. +table CustomQuantization { + custom:[ubyte] (force_align: 16); +} + +// Represents a specific quantization technique's parameters. +union QuantizationDetails { + CustomQuantization, +} + +// Parameters for converting a quantized tensor back to float. +table QuantizationParameters { + // These four parameters are the asymmetric linear quantization parameters. + // Given a quantized value q, the corresponding float value f should be: + // f = scale * (q - zero_point) + // For other quantization types, the QuantizationDetails below is used. + min:[float]; // For importing back into tensorflow. + max:[float]; // For importing back into tensorflow. + scale:[float]; // For dequantizing the tensor's values. + zero_point:[long]; + + // If this is not none, the other quantization parameters (i.e. min, max, + // scale, zero_point fields above) are ignored and the value of the + // QuantizationDetails union should be used. + details:QuantizationDetails; + + // Specifies the dimension of the Tensor's shape that the scales and + // zero_points correspond to. For example, a tensor t, with dims=[4, 3, 2, 1] + // with quantization params: + // scale=[1.0, 2.0, 3.0], zero_point=[1, 2, 3], quantization_dimension=1 + // will be quantized across the second dimension of t. + // t[:, 0, :, :] will have scale[0]=1.0, zero_point[0]=1 + // t[:, 1, :, :] will have scale[1]=2.0, zero_point[0]=2 + // t[:, 2, :, :] will have scale[2]=3.0, zero_point[0]=3 + quantized_dimension:int; +} + +// Sparse tensors. +// We use a modification of the TACO format. +// Reference: http://tensor-compiler.org/kjolstad-oopsla17-tensor-compiler.pdf +// +// To encode a conceptual n-dimensional dense tensor with dims (d0, ..., dn-1), +// potentially with a k-dimensional block (0 <= k <= n) with dims +// (dn, ..., dn+k-1), the format needs to specify: +// 1. In what order to traverse these dimensions. For example, to store a 2-D +// matrix in row major order, the traversal order would be (d0, d1), +// whereas to store it in column major order, the traversal order would be +// (d1, d0). If the 2-D matrix has a 2-D inner block, the traversal order +// could be (d0, d1, d2, d3). +// 2. How each block dimension in (dn, ..., dn+k-1) maps to the original +// tensor dimension in (d0, ..., dn-1). +// 3. In the traversal order defined above, the format (dense vs. sparse) and +// index metadata for each dimension. For a dense dimension, this is just +// the size of that dimension. For a sparse dimension, it's the same as +// the compressed index defined in the Compressed Sparse Row (CSR) format. +// (http://scipy-lectures.org/advanced/scipy_sparse/csr_matrix.html) + +// The storage type for a dimension. Currently we support: +// 1. DENSE: each coordinate in this dimension is stored implicitly. +// 2. SPARSE_CSR: only the coordinates with non-zero elements are stored. The +// compression technique is the same what CSR uses. +// More types like a sparse dimension with a different compression technique +// could be added to the list in the future. +enum DimensionType : byte { + DENSE = 0, + SPARSE_CSR = 1, +} + +table Int32Vector { + values:[int]; +} + +table Uint16Vector { + values:[ushort] (force_align: 4); +} + +table Uint8Vector { + values:[ubyte] (force_align: 4); +} + +// Variable-typed buffer to store the index metadata for a sparse dimension. +// The widest type is Int32 instead of UInt32 because tensor's shape is a int32 +// vector. We don't want the per-dimensional index to overflow that range. +union SparseIndexVector { + Int32Vector, + Uint16Vector, + Uint8Vector +} + +table DimensionMetadata { + // Whether a dimension is dense or sparse. + format:DimensionType; + // Index metadata used for a dimension. + // - If format is DimensionType.DENSE then we use the dense_size field to + // store the size of that dimension. Each index in that dimension is + // stored implicitly. + // - If format is DimensionType.SPARSE_CSR then we use array_segments and + // array_indices to encode that dimension. array_segments represents how + // to segment the indices array, each segment corresponds to one element + // in the previous dimension. array_indices represents the index of the + // non-zero elements within this dimension (as those in the CSR matrix + // format, where the first array is row pointers and the second array is + // column indices). + dense_size:int; + array_segments:SparseIndexVector; + array_indices:SparseIndexVector; +} + +// Parameters to encode a sparse TfLite tensor. +table SparsityParameters { + // The traversal order of the dimensions defined in the `shape` field of the + // conceptual dense tensor. For a n-dimensional tensors with dims (d0, d1, + // ..., dn-1), + // - if not block sparse, the traversal_order is just a permutation of (d0, + // ..., dn-1). For example, a 2-D matrix stored in row-major order would + // have traversal_order = (d0, d1). + // - if block sparse with a k-dimensional block (0 <= k <= n), the + // traversal_order has n + k elements. The first n elements are still a + // permutation of (d0, ..., dn-1). The lask k elements are a permutation + // of (dn, ..., dn+k-1), defining how to traverse a block internally. For + // example, a 2-D matrix with 2-D blocks, both stored in row-major order + // would have traversal_order = (d0, d1, d2, d3). + traversal_order:[int]; + // For an n-dimensional tensor with a k-dimensional block (0 <= k <= n), + // stores how a block dimension in (dn, ..., dn+k-1) maps to the original + // tensor dimension in (d0, ..., dn). + // It's stored in the order of (dn, ..., dn+k-1). + // If not block-sparse, this field is NULL. + block_map:[int]; + // In the traversal order defined above, the metadata needed for + // each dimension to locate the non-zero values in the original dense tensor. + // The size of the dim_metadata array = the size of the traversal_order array + // = n + k. + dim_metadata:[DimensionMetadata]; +} + +// The nested tensor type for VARIANT type. +table VariantSubType { + // The tensor shape. + shape:[int]; + type:TensorType; + // If false, the rank or the number of tensor dimensions is unknown. + // If false, "shape" must be []. + has_rank: bool = false; +} + +table Tensor { + // The tensor shape. The meaning of each entry is operator-specific but + // builtin ops use: [batch size, height, width, number of channels] (That's + // Tensorflow's NHWC). + shape:[int]; + type:TensorType; + // An index that refers to the buffers table at the root of the model. Or, + // if there is no data buffer associated (i.e. intermediate results), then + // this is 0 (which refers to an always existent empty buffer). + // + // The data_buffer itself is an opaque container, with the assumption that the + // target device is little-endian. In addition, all builtin operators assume + // the memory is ordered such that if `shape` is [4, 3, 2], then index + // [i, j, k] maps to data_buffer[i*3*2 + j*2 + k]. + buffer:uint; + name:string; // For debugging and importing back into tensorflow. + quantization:QuantizationParameters; // Optional. + + is_variable:bool = false; + + // Parameters to encode a sparse tensor. See the example in + // tensorflow/lite/testdata/sparse_tensor.json. + sparsity:SparsityParameters; // Optional. + + // Encodes `shape` with unknown dimensions. Unknown dimensions are + // represented with -1. + shape_signature:[int]; // Optional. + + // If false, the rank or the number of tensor dimensions is unknown. + // If false, "shape" must be []. + has_rank: bool = false; + + // The nested Tensor types for VARIANT type. This is always empty for + // non-VARIANT types. This is optional because the nested type can be omitted. + // Currently only 1 subtype is supported. The field is defined as an array for + // flexibility of supporting multiple subtypes in the future. + variant_tensors:[VariantSubType]; +} + +// A list of builtin operators. Builtin operators are slightly faster than custom +// ones, but not by much. Moreover, while custom operators accept an opaque +// object containing configuration parameters, builtins have a predetermined +// set of acceptable options. +// LINT.IfChange +enum BuiltinOperator : int32 { + ADD = 0, + AVERAGE_POOL_2D = 1, + CONCATENATION = 2, + CONV_2D = 3, + DEPTHWISE_CONV_2D = 4, + DEPTH_TO_SPACE = 5, + DEQUANTIZE = 6, + EMBEDDING_LOOKUP = 7, + FLOOR = 8, + FULLY_CONNECTED = 9, + HASHTABLE_LOOKUP = 10, + L2_NORMALIZATION = 11, + L2_POOL_2D = 12, + LOCAL_RESPONSE_NORMALIZATION = 13, + LOGISTIC = 14, + LSH_PROJECTION = 15, + LSTM = 16, + MAX_POOL_2D = 17, + MUL = 18, + RELU = 19, + // NOTE(aselle): RELU_N1_TO_1 used to be called RELU1, but it was renamed + // since different model developers use RELU1 in different ways. Never + // create another op called RELU1. + RELU_N1_TO_1 = 20, + RELU6 = 21, + RESHAPE = 22, + RESIZE_BILINEAR = 23, + RNN = 24, + SOFTMAX = 25, + SPACE_TO_DEPTH = 26, + SVDF = 27, + TANH = 28, + CONCAT_EMBEDDINGS = 29, + SKIP_GRAM = 30, + CALL = 31, + CUSTOM = 32, + EMBEDDING_LOOKUP_SPARSE = 33, + PAD = 34, + UNIDIRECTIONAL_SEQUENCE_RNN = 35, + GATHER = 36, + BATCH_TO_SPACE_ND = 37, + SPACE_TO_BATCH_ND = 38, + TRANSPOSE = 39, + MEAN = 40, + SUB = 41, + DIV = 42, + SQUEEZE = 43, + UNIDIRECTIONAL_SEQUENCE_LSTM = 44, + STRIDED_SLICE = 45, + BIDIRECTIONAL_SEQUENCE_RNN = 46, + EXP = 47, + TOPK_V2 = 48, + SPLIT = 49, + LOG_SOFTMAX = 50, + // DELEGATE is a special op type for the operations which are delegated to + // other backends. + // WARNING: Experimental interface, subject to change + DELEGATE = 51, + BIDIRECTIONAL_SEQUENCE_LSTM = 52, + CAST = 53, + PRELU = 54, + MAXIMUM = 55, + ARG_MAX = 56, + MINIMUM = 57, + LESS = 58, + NEG = 59, + PADV2 = 60, + GREATER = 61, + GREATER_EQUAL = 62, + LESS_EQUAL = 63, + SELECT = 64, + SLICE = 65, + SIN = 66, + TRANSPOSE_CONV = 67, + SPARSE_TO_DENSE = 68, + TILE = 69, + EXPAND_DIMS = 70, + EQUAL = 71, + NOT_EQUAL = 72, + LOG = 73, + SUM = 74, + SQRT = 75, + RSQRT = 76, + SHAPE = 77, + POW = 78, + ARG_MIN = 79, + FAKE_QUANT = 80, + REDUCE_PROD = 81, + REDUCE_MAX = 82, + PACK = 83, + LOGICAL_OR = 84, + ONE_HOT = 85, + LOGICAL_AND = 86, + LOGICAL_NOT = 87, + UNPACK = 88, + REDUCE_MIN = 89, + FLOOR_DIV = 90, + REDUCE_ANY = 91, + SQUARE = 92, + ZEROS_LIKE = 93, + FILL = 94, + FLOOR_MOD = 95, + RANGE = 96, + RESIZE_NEAREST_NEIGHBOR = 97, + LEAKY_RELU = 98, + SQUARED_DIFFERENCE = 99, + MIRROR_PAD = 100, + ABS = 101, + SPLIT_V = 102, + UNIQUE = 103, + CEIL = 104, + REVERSE_V2 = 105, + ADD_N = 106, + GATHER_ND = 107, + COS = 108, + WHERE = 109, + RANK = 110, + ELU = 111, + REVERSE_SEQUENCE = 112, + MATRIX_DIAG = 113, + QUANTIZE = 114, + MATRIX_SET_DIAG = 115, + ROUND = 116, + HARD_SWISH = 117, + IF = 118, + WHILE = 119, + NON_MAX_SUPPRESSION_V4 = 120, + NON_MAX_SUPPRESSION_V5 = 121, + SCATTER_ND = 122, + SELECT_V2 = 123, + DENSIFY = 124, + SEGMENT_SUM = 125, + BATCH_MATMUL = 126, + PLACEHOLDER_FOR_GREATER_OP_CODES = 127, + CUMSUM = 128, + CALL_ONCE = 129, + BROADCAST_TO = 130, + RFFT2D = 131, + CONV_3D = 132, + IMAG=133, + REAL=134, + COMPLEX_ABS=135, + HASHTABLE = 136, + HASHTABLE_FIND = 137, + HASHTABLE_IMPORT = 138, + HASHTABLE_SIZE = 139, + REDUCE_ALL = 140, + CONV_3D_TRANSPOSE = 141, + VAR_HANDLE = 142, + READ_VARIABLE = 143, + ASSIGN_VARIABLE = 144, + BROADCAST_ARGS = 145, + RANDOM_STANDARD_NORMAL = 146, + BUCKETIZE = 147, + RANDOM_UNIFORM = 148, + MULTINOMIAL = 149, + GELU = 150, + DYNAMIC_UPDATE_SLICE = 151, + RELU_0_TO_1 = 152, + UNSORTED_SEGMENT_PROD = 153, + UNSORTED_SEGMENT_MAX = 154, + UNSORTED_SEGMENT_SUM = 155, + ATAN2 = 156, + UNSORTED_SEGMENT_MIN = 157, + SIGN = 158 +} +// LINT.ThenChange(nnapi_linter/linter.proto) + +// Options for the builtin operators. +union BuiltinOptions { + Conv2DOptions, + DepthwiseConv2DOptions, + ConcatEmbeddingsOptions, + LSHProjectionOptions, + Pool2DOptions, + SVDFOptions, + RNNOptions, + FullyConnectedOptions, + SoftmaxOptions, + ConcatenationOptions, + AddOptions, + L2NormOptions, + LocalResponseNormalizationOptions, + LSTMOptions, + ResizeBilinearOptions, + CallOptions, + ReshapeOptions, + SkipGramOptions, + SpaceToDepthOptions, + EmbeddingLookupSparseOptions, + MulOptions, + PadOptions, + GatherOptions, + BatchToSpaceNDOptions, + SpaceToBatchNDOptions, + TransposeOptions, + ReducerOptions, + SubOptions, + DivOptions, + SqueezeOptions, + SequenceRNNOptions, + StridedSliceOptions, + ExpOptions, + TopKV2Options, + SplitOptions, + LogSoftmaxOptions, + CastOptions, + DequantizeOptions, + MaximumMinimumOptions, + ArgMaxOptions, + LessOptions, + NegOptions, + PadV2Options, + GreaterOptions, + GreaterEqualOptions, + LessEqualOptions, + SelectOptions, + SliceOptions, + TransposeConvOptions, + SparseToDenseOptions, + TileOptions, + ExpandDimsOptions, + EqualOptions, + NotEqualOptions, + ShapeOptions, + PowOptions, + ArgMinOptions, + FakeQuantOptions, + PackOptions, + LogicalOrOptions, + OneHotOptions, + LogicalAndOptions, + LogicalNotOptions, + UnpackOptions, + FloorDivOptions, + SquareOptions, + ZerosLikeOptions, + FillOptions, + BidirectionalSequenceLSTMOptions, + BidirectionalSequenceRNNOptions, + UnidirectionalSequenceLSTMOptions, + FloorModOptions, + RangeOptions, + ResizeNearestNeighborOptions, + LeakyReluOptions, + SquaredDifferenceOptions, + MirrorPadOptions, + AbsOptions, + SplitVOptions, + UniqueOptions, + ReverseV2Options, + AddNOptions, + GatherNdOptions, + CosOptions, + WhereOptions, + RankOptions, + ReverseSequenceOptions, + MatrixDiagOptions, + QuantizeOptions, + MatrixSetDiagOptions, + HardSwishOptions, + IfOptions, + WhileOptions, + DepthToSpaceOptions, + NonMaxSuppressionV4Options, + NonMaxSuppressionV5Options, + ScatterNdOptions, + SelectV2Options, + DensifyOptions, + SegmentSumOptions, + BatchMatMulOptions, + CumsumOptions, + CallOnceOptions, + BroadcastToOptions, + Rfft2dOptions, + Conv3DOptions, + HashtableOptions, + HashtableFindOptions, + HashtableImportOptions, + HashtableSizeOptions, + VarHandleOptions, + ReadVariableOptions, + AssignVariableOptions, + RandomOptions, + BucketizeOptions, + GeluOptions, + DynamicUpdateSliceOptions, + UnsortedSegmentProdOptions, + UnsortedSegmentMaxOptions, + UnsortedSegmentMinOptions, + UnsortedSegmentSumOptions, + ATan2Options, + SignOptions +} + +// LINT.IfChange +enum Padding : byte { SAME, VALID } +// LINT.ThenChange(//tensorflow/compiler/mlir/lite/ir/tfl_op_enums.td) + +// LINT.IfChange +enum ActivationFunctionType : byte { + NONE = 0, + RELU = 1, + RELU_N1_TO_1 = 2, + RELU6 = 3, + TANH = 4, + SIGN_BIT = 5, +} +// LINT.ThenChange(//tensorflow/compiler/mlir/lite/ir/tfl_op_enums.td) + +table Conv2DOptions { + padding:Padding; + stride_w:int; + stride_h:int; + fused_activation_function:ActivationFunctionType; + dilation_w_factor:int = 1; + dilation_h_factor:int = 1; +} + +// Options for both Conv3D and Conv3DTranspose. +table Conv3DOptions { + padding:Padding; + stride_d:int; + stride_w:int; + stride_h:int; + fused_activation_function:ActivationFunctionType; + dilation_d_factor:int = 1; + dilation_w_factor:int = 1; + dilation_h_factor:int = 1; +} + +table Pool2DOptions { + padding:Padding; + stride_w:int; + stride_h:int; + filter_width:int; + filter_height:int; + fused_activation_function:ActivationFunctionType; +} + +table DepthwiseConv2DOptions { + // Parameters for DepthwiseConv version 1 or above. + padding:Padding; + stride_w:int; + stride_h:int; + // `depth_multiplier` is redundant. It's used by CPU kernels in + // TensorFlow 2.0 or below, but ignored in versions above. + // See comments in lite/c/builtin_op_data.h for more details. + depth_multiplier:int; + fused_activation_function:ActivationFunctionType; + // Parameters for DepthwiseConv version 2 or above. + dilation_w_factor:int = 1; + dilation_h_factor:int = 1; +} + +table ConcatEmbeddingsOptions { + num_channels:int; + num_columns_per_channel:[int]; + embedding_dim_per_channel:[int]; // This could be inferred from parameters. +} + +enum LSHProjectionType: byte { + UNKNOWN = 0, + SPARSE = 1, + DENSE = 2, +} + +table LSHProjectionOptions { + type: LSHProjectionType; +} + +table SVDFOptions { + rank:int; + fused_activation_function:ActivationFunctionType; + // For weights-only quantization, use asymmetric quantization for non + // constant inputs at evaluation time. + asymmetric_quantize_inputs:bool; +} + +// An implementation of TensorFlow RNNCell. +table RNNOptions { + fused_activation_function:ActivationFunctionType; + asymmetric_quantize_inputs:bool; +} + +// An implementation of TensorFlow dynamic_rnn with RNNCell. +table SequenceRNNOptions { + time_major:bool; + fused_activation_function:ActivationFunctionType; + asymmetric_quantize_inputs:bool; +} + +// An implementation of TensorFlow bidrectional_dynamic_rnn with RNNCell. +table BidirectionalSequenceRNNOptions { + time_major:bool; + fused_activation_function:ActivationFunctionType; + merge_outputs: bool; + asymmetric_quantize_inputs:bool; +} + +// LINT.IfChange +enum FullyConnectedOptionsWeightsFormat: byte { + DEFAULT = 0, + SHUFFLED4x16INT8 = 1, +} +// LINT.ThenChange(//tensorflow/compiler/mlir/lite/ir/tfl_op_enums.td) + +// An implementation of TensorFlow fully_connected (a.k.a Dense) layer. +table FullyConnectedOptions { + // Parameters for FullyConnected version 1 or above. + fused_activation_function:ActivationFunctionType; + + // Parameters for FullyConnected version 2 or above. + weights_format:FullyConnectedOptionsWeightsFormat = DEFAULT; + + // Parameters for FullyConnected version 5 or above. + // If set to true, then the number of dimension is preserved. Furthermore, + // all but the last dimension of the input and output shapes will be equal. + keep_num_dims: bool; + + // Parameters for FullyConnected version 7 or above. + // If set to true, then weights-only op will use asymmetric quantization for + // inputs. + asymmetric_quantize_inputs: bool; +} + +table SoftmaxOptions { + beta: float; +} + +// An implementation of TensorFlow concat. +table ConcatenationOptions { + axis:int; + fused_activation_function:ActivationFunctionType; +} + +table AddOptions { + fused_activation_function:ActivationFunctionType; + // Parameters supported by version 3. + pot_scale_int16:bool = true; +} + +table MulOptions { + fused_activation_function:ActivationFunctionType; +} + +table L2NormOptions { + // This field is currently ignored in the L2 Norm Op. + fused_activation_function:ActivationFunctionType; +} + +table LocalResponseNormalizationOptions { + radius:int; + bias:float; + alpha:float; + beta:float; +} + +// LINT.IfChange +enum LSTMKernelType : byte { + // Full LSTM kernel which supports peephole and projection. + FULL = 0, + // Basic LSTM kernels. Equivalent to TensorFlow BasicLSTMCell. + BASIC = 1, +} +// LINT.ThenChange(//tensorflow/compiler/mlir/lite/ir/tfl_op_enums.td) + +// An implementation of TensorFlow LSTMCell and CoupledInputForgetGateLSTMCell +table LSTMOptions { + // Parameters for LSTM version 1 or above. + fused_activation_function:ActivationFunctionType; + cell_clip: float; // Optional, 0.0 means no clipping + proj_clip: float; // Optional, 0.0 means no clipping + + // Parameters for LSTM version 2 or above. + // Basic kernel is only supported in version 2 or above. + kernel_type: LSTMKernelType = FULL; + + // Parameters for LSTM version 4 or above. + asymmetric_quantize_inputs: bool; +} + +// An implementation of TensorFlow dynamic_rnn with LSTMCell. +table UnidirectionalSequenceLSTMOptions { + fused_activation_function:ActivationFunctionType; + cell_clip: float; // Optional, 0.0 means no clipping + proj_clip: float; // Optional, 0.0 means no clipping + + // If true then first dimension is sequence, otherwise batch. + time_major:bool; + + // Parameter for Unidirectional Sequence LSTM version 3. + asymmetric_quantize_inputs:bool; +} + +table BidirectionalSequenceLSTMOptions { + // Parameters supported by version 1: + fused_activation_function:ActivationFunctionType; + cell_clip: float; // Optional, 0.0 means no clipping + proj_clip: float; // Optional, 0.0 means no clipping + + // If true, store the outputs of both directions into the first output. + merge_outputs: bool; + + // Parameters supported by version 2: + // If true then first dimension is sequence, otherwise batch. + // Version 1 implementations assumed time_major to be true, so this default + // value should never change. + time_major: bool = true; + + // Parameters for version 3 or above. + asymmetric_quantize_inputs:bool; +} + +table ResizeBilinearOptions { + new_height: int (deprecated); + new_width: int (deprecated); + align_corners: bool; + half_pixel_centers: bool; +} + +table ResizeNearestNeighborOptions { + align_corners: bool; + half_pixel_centers: bool; +} + +// A call operation options +table CallOptions { + // The subgraph index that needs to be called. + subgraph:uint; +} + +table PadOptions { +} + +table PadV2Options { +} + +table ReshapeOptions { + new_shape:[int]; +} + +table SpaceToBatchNDOptions { +} + +table BatchToSpaceNDOptions { +} + +table SkipGramOptions { + ngram_size: int; + max_skip_size: int; + include_all_ngrams: bool; +} + +table SpaceToDepthOptions { + block_size: int; +} + +table DepthToSpaceOptions { + block_size: int; +} + +table SubOptions { + fused_activation_function:ActivationFunctionType; + // Parameters supported by version 5 + pot_scale_int16:bool = true; +} + +table DivOptions { + fused_activation_function:ActivationFunctionType; +} + +table TopKV2Options { +} + +enum CombinerType : byte { + SUM = 0, + MEAN = 1, + SQRTN = 2, +} + +table EmbeddingLookupSparseOptions { + combiner:CombinerType; +} + +table GatherOptions { + axis: int; + // Parameters for Gather version 5 or above. + batch_dims: int = 0; +} + +table TransposeOptions { +} + +table ExpOptions { +} + +table CosOptions { +} + +table ReducerOptions { + keep_dims: bool; +} + +table SqueezeOptions { + squeeze_dims:[int]; +} + +table SplitOptions { + num_splits: int; +} + +table SplitVOptions { + num_splits: int; +} + +table StridedSliceOptions { + begin_mask: int; + end_mask: int; + ellipsis_mask: int; + new_axis_mask: int; + shrink_axis_mask: int; +} + +table LogSoftmaxOptions { +} + +table CastOptions { + in_data_type: TensorType; + out_data_type: TensorType; +} + +table DequantizeOptions { +} + +table MaximumMinimumOptions { +} + +table TileOptions { +} + +table ArgMaxOptions { + output_type : TensorType; +} + +table ArgMinOptions { + output_type : TensorType; +} + +table GreaterOptions { +} + +table GreaterEqualOptions { +} + +table LessOptions { +} + +table LessEqualOptions { +} + +table NegOptions { +} + +table SelectOptions { +} + +table SliceOptions { +} + +table TransposeConvOptions { + padding:Padding; + stride_w:int; + stride_h:int; +} + +table ExpandDimsOptions { +} + +table SparseToDenseOptions { + validate_indices:bool; +} + +table EqualOptions { +} + +table NotEqualOptions { +} + +table ShapeOptions { + // Optional output type of the operation (int32 or int64). Defaults to int32. + out_type : TensorType; +} + +table RankOptions { +} + +table PowOptions { +} + +table FakeQuantOptions { + // Parameters supported by version 1: + min:float; + max:float; + num_bits:int; + + // Parameters supported by version 2: + narrow_range:bool; +} + +table PackOptions { + values_count:int; + axis:int; +} + +table LogicalOrOptions { +} + +table OneHotOptions { + axis:int; +} + +table AbsOptions { +} + + +table HardSwishOptions { +} + +table LogicalAndOptions { +} + +table LogicalNotOptions { +} + +table UnpackOptions { + num:int; + axis:int; +} + +table FloorDivOptions { +} + +table SquareOptions { +} + +table ZerosLikeOptions { +} + +table FillOptions { +} + +table FloorModOptions { +} + +table RangeOptions { +} + +table LeakyReluOptions { + alpha:float; +} + +table SquaredDifferenceOptions { +} + +// LINT.IfChange +enum MirrorPadMode : byte { + // Doesn't include borders. + REFLECT = 0, + // Includes borders. + SYMMETRIC = 1, +} +// LINT.ThenChange(//tensorflow/compiler/mlir/lite/ir/tfl_op_enums.td) + +table MirrorPadOptions { + mode:MirrorPadMode; +} + +table UniqueOptions { + idx_out_type:TensorType = INT32; +} + +table ReverseV2Options { +} + +table AddNOptions { +} + +table GatherNdOptions { +} + +table WhereOptions { +} + +table ReverseSequenceOptions { + seq_dim:int; + batch_dim:int = 0; +} + +table MatrixDiagOptions { +} + +table QuantizeOptions { +} + +table MatrixSetDiagOptions { +} + +table IfOptions { + then_subgraph_index:int; + else_subgraph_index:int; +} + +table CallOnceOptions { + init_subgraph_index:int; +} + +table WhileOptions { + cond_subgraph_index:int; + body_subgraph_index:int; +} + +table NonMaxSuppressionV4Options { +} + +table NonMaxSuppressionV5Options { +} + +table ScatterNdOptions { +} + +table SelectV2Options { +} + +table DensifyOptions { +} + +table SegmentSumOptions { +} + +table BatchMatMulOptions { + adj_x:bool; + adj_y:bool; + // Parameters for BatchMatMul version 4 or above. + // If set to true, then weights-only op will use asymmetric quantization for + // inputs. + asymmetric_quantize_inputs: bool; +} + +table CumsumOptions { + exclusive:bool; + reverse:bool; +} + +table BroadcastToOptions { +} + +table Rfft2dOptions { +} + +table HashtableOptions { + // The identity of hash tables. This identity will be used across different + // subgraphs in the same interpreter instance. + table_id:int; + key_dtype:TensorType; + value_dtype:TensorType; +} + +table HashtableFindOptions { +} + +table HashtableImportOptions { +} + +table HashtableSizeOptions { +} + +table VarHandleOptions { + container:string; + shared_name:string; +} + +table ReadVariableOptions { +} + +table AssignVariableOptions { +} + +table RandomOptions { + seed: long; + seed2: long; +} + +table BucketizeOptions { + boundaries: [float]; // The bucket boundaries. +} + +table GeluOptions { + approximate: bool; +} + +table DynamicUpdateSliceOptions { +} + +table UnsortedSegmentProdOptions { +} + +table UnsortedSegmentMaxOptions { +} + +table UnsortedSegmentSumOptions { +} + +table ATan2Options { +} + +table UnsortedSegmentMinOptions{ +} + +table SignOptions { +} + + +// An OperatorCode can be an enum value (BuiltinOperator) if the operator is a +// builtin, or a string if the operator is custom. +table OperatorCode { + // This field is for backward compatibility. This field will be used when + // the value of the extended builtin_code field has less than + // BulitinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES. + deprecated_builtin_code:byte; + custom_code:string; + + // The version of the operator. The version need to be bumped whenever new + // parameters are introduced into an op. + version:int = 1; + + // This field is introduced for resolving op builtin code shortage problem + // (the original BuiltinOperator enum field was represented as a byte). + // This field will be used when the value of the extended builtin_code field + // has greater than BulitinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES. + builtin_code:BuiltinOperator; +} + +enum CustomOptionsFormat : byte { + FLEXBUFFERS = 0, +} + +// An operator takes tensors as inputs and outputs. The type of operation being +// performed is determined by an index into the list of valid OperatorCodes, +// while the specifics of each operations is configured using builtin_options +// or custom_options. +table Operator { + // Index into the operator_codes array. Using an integer here avoids + // complicate map lookups. + opcode_index:uint; + + // Optional input are indicated by -1. + inputs:[int]; + outputs:[int]; + + builtin_options:BuiltinOptions; + custom_options:[ubyte]; + custom_options_format:CustomOptionsFormat; + + // A list of booleans indicating the input tensors which are being mutated by + // this operator.(e.g. used by RNN and LSTM). + // For example, if the "inputs" array refers to 5 tensors and the second and + // fifth are mutable variables, then this list will contain + // [false, true, false, false, true]. + // + // If the list is empty, no variable is mutated in this operator. + // The list either has the same length as `inputs`, or is empty. + mutating_variable_inputs:[bool]; + + // A list of indices to the subgraph's "tensors" that are internal to an Op. + // Internal tensors are those that do not flow in or out of the operation, + // but instead are part of internal computation. As such, the operation's + // implementation may manage its memory more efficiently. They are needed + // however (i.e. not just an implementation detail) since they are part of the + // computation, which may require relevant metadata such as quantization + // parameters. + intermediates:[int]; +} + +// The root type, defining a subgraph, which typically represents an entire +// model. +table SubGraph { + // A list of all tensors used in this subgraph. + tensors:[Tensor]; + + // Indices of the tensors that are inputs into this subgraph. Note this is + // the list of non-static tensors that feed into the subgraph for inference. + inputs:[int]; + + // Indices of the tensors that are outputs out of this subgraph. Note this is + // the list of output tensors that are considered the product of the + // subgraph's inference. + outputs:[int]; + + // All operators, in execution order. + operators:[Operator]; + + // Name of this subgraph (used for debugging). + name:string; +} + +// Table of raw data buffers (used for constant tensors). Referenced by tensors +// by index. The generous alignment accommodates mmap-friendly data structures. +table Buffer { + data:[ubyte] (force_align: 16); +} + +table Metadata { + // A human readable string to uniquely identify a Metadata. + name:string; + // An index to the buffers table. + buffer:uint; +} + +// Map from an alias name of tensor to tensor index in the graph. +// This is used in Signature def. +table TensorMap { + // Represents the alias to use for this tensor. + name:string; + + // The actual tensor index in the primary graph, that 'name' corresponds to. + tensor_index:uint; +} + +// This corresponds to SignatureDef in Tensorflow SavedModel. +// The SignatureDef will be part of the SavedModel provided for conversion. +table SignatureDef { + // Named inputs for this signature. + inputs:[TensorMap]; + + // Named outputs for this signature. + outputs:[TensorMap]; + + // Key value which was in the Tensorflow SavedModel SignatureDef map. + signature_key:string; + + // Model tag, deprecated. + deprecated_tag:string (deprecated); + + // Index of subgraphs that corresponds to the exported method. + subgraph_index:uint; +} + +table Model { + // Version of the schema. + version:uint; + + // A list of all operator codes used in this model. This is + // kept in order because operators carry an index into this + // vector. + operator_codes:[OperatorCode]; + + // All the subgraphs of the model. The 0th is assumed to be the main + // model. + subgraphs:[SubGraph]; + + // A description of the model. + description:string; + + // Buffers of the model. + // Note the 0th entry of this array must be an empty buffer (sentinel). + // This is a convention so that tensors without a buffer can provide 0 as + // their buffer. + buffers:[Buffer]; + + // Metadata about the model. Indirects into the existings buffers list. + // Deprecated, prefer to use metadata field. + metadata_buffer:[int]; + + // Metadata about the model. + metadata:[Metadata]; + + // Optional SignatureDefs for the model. + signature_defs:[SignatureDef]; +} + +root_type Model; diff --git a/src/frontends/tensorflow_lite/src/tensor_lite_place.cpp b/src/frontends/tensorflow_lite/src/tensor_lite_place.cpp new file mode 100644 index 00000000000..7625a25042e --- /dev/null +++ b/src/frontends/tensorflow_lite/src/tensor_lite_place.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// +#include "tensor_lite_place.hpp" + +#include "quantization_info.hpp" + +void ov::frontend::tensorflow_lite::TensorLitePlace::translate(ov::Output& output, + bool convert_tensor_attrs_to_nodes) { + output.set_names({*get_names().begin()}); + output.get_rt_info()[QuantizationInfo::get_type_info_static()] = m_quantization; + if (convert_tensor_attrs_to_nodes) + apply_quantization(output); +} diff --git a/src/frontends/tensorflow_lite/src/tensor_lite_place.hpp b/src/frontends/tensorflow_lite/src/tensor_lite_place.hpp new file mode 100644 index 00000000000..9443d3cb9a7 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/tensor_lite_place.hpp @@ -0,0 +1,70 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "openvino/frontend/frontend.hpp" +#include "openvino/frontend/tensorflow_lite/visibility.hpp" +#include "place.hpp" +#include "quantization_info.hpp" +#include "utils.hpp" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class TensorLitePlace : public ov::frontend::tensorflow::TensorPlace { +public: + TensorLitePlace(const ov::frontend::InputModel& input_model, + const ov::PartialShape& pshape, + ov::element::Type type, + const std::vector& names, + std::shared_ptr quantization, + int64_t input_idx, + int64_t output_idx, + const void* data) + : ov::frontend::tensorflow::TensorPlace(input_model, pshape, type, names), + m_quantization(quantization), + m_input_idx(input_idx), + m_output_idx(output_idx), + m_data(data){}; + + void translate(ov::Output& output, bool convert_tensor_attrs_to_nodes = false); + + bool is_input() const override { + return m_input_idx >= 0; + } + size_t get_input_index() const { + FRONT_END_GENERAL_CHECK(is_input(), "This is not input TensorPlace. Can not deliver input index"); + return static_cast(m_input_idx); + } + bool is_output() const override { + return m_output_idx >= 0; + } + size_t get_output_index() const { + FRONT_END_GENERAL_CHECK(is_output(), "This is not output TensorPlace. Can not deliver output index"); + return static_cast(m_output_idx); + } + void set_input_index(const int64_t& idx) { + m_input_idx = idx; + } + void set_output_index(const int64_t& idx) { + m_output_idx = idx; + } + + const void* get_data() const { + return m_data; + } + +protected: + std::shared_ptr m_quantization; + int64_t m_input_idx, m_output_idx; + const void* m_data; +}; + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov diff --git a/src/frontends/tensorflow_lite/src/tensorflow_lite.cpp b/src/frontends/tensorflow_lite/src/tensorflow_lite.cpp new file mode 100644 index 00000000000..dbfc22da76f --- /dev/null +++ b/src/frontends/tensorflow_lite/src/tensorflow_lite.cpp @@ -0,0 +1,20 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/frontend/manager.hpp" +#include "openvino/frontend/tensorflow_lite/frontend.hpp" +#include "openvino/frontend/tensorflow_lite/visibility.hpp" + +TENSORFLOW_LITE_C_API ov::frontend::FrontEndVersion GetAPIVersion() { + return OV_FRONTEND_API_VERSION; +} + +TENSORFLOW_LITE_C_API void* GetFrontEndData() { + auto res = new ov::frontend::FrontEndPluginInfo(); + res->m_name = "tflite"; + res->m_creator = []() { + return std::make_shared(); + }; + return res; +} diff --git a/src/frontends/tensorflow_lite/src/utils.cpp b/src/frontends/tensorflow_lite/src/utils.cpp new file mode 100644 index 00000000000..d1a4e4fe42e --- /dev/null +++ b/src/frontends/tensorflow_lite/src/utils.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "utils.hpp" + +#include + +#include "schema_generated.h" + +using namespace ov; + +std::shared_ptr ov::frontend::tensorflow_lite::get_quantization( + const tflite::QuantizationParameters* tf_quantization) { + if (tf_quantization == NULL) + return {}; + auto quantization = std::make_shared(); + auto tf_zp = tf_quantization->zero_point(); + auto tf_scale = tf_quantization->scale(); + if (tf_zp != NULL) + quantization->set_zero_point({(*tf_zp).begin(), (*tf_zp).end()}); + if (tf_scale != NULL) + quantization->set_scale({(*tf_scale).begin(), (*tf_scale).end()}); + if (quantization->get_zero_point().empty() && quantization->get_scale().empty()) + return {}; + quantization->set_axis(tf_quantization->quantized_dimension()); + return quantization; +} + +namespace { +const std::map& TYPE_MAP() { + static const std::map type_map{ + {tflite::TensorType_FLOAT32, element::f32}, + {tflite::TensorType_FLOAT16, element::f16}, + {tflite::TensorType_INT32, element::i32}, + {tflite::TensorType_UINT8, element::u8}, + {tflite::TensorType_INT64, element::i64}, + {tflite::TensorType_BOOL, element::boolean}, + {tflite::TensorType_INT16, element::i16}, + {tflite::TensorType_INT8, element::i8}, + {tflite::TensorType_FLOAT64, element::f64}, + {tflite::TensorType_UINT64, element::u64}, + {tflite::TensorType_UINT32, element::u32}, + {tflite::TensorType_UINT16, element::u16}, + {tflite::TensorType_INT4, element::i4}, + // TODO: support the following types + // {TensorType_STRING, element::string}, + // {TensorType_COMPLEX64, element::complex64}, + // {TensorType_COMPLEX128, element::complex128}, + // {TensorType_RESOURCE, element::resource}, + // {TensorType_VARIANT, element::variant}, + }; + return type_map; +} +} // namespace + +ov::element::Type ov::frontend::tensorflow_lite::get_ov_type(const tflite::TensorType& tf_type) { + const auto& mapping = TYPE_MAP(); + if (mapping.find(tf_type) == mapping.end()) { + FRONT_END_THROW("Unexpected type"); + } + return mapping.at(tf_type); +} + +ov::PartialShape ov::frontend::tensorflow_lite::get_ov_shape(const flatbuffers::Vector* tf_shape) { + return ov::Shape{tf_shape->begin(), tf_shape->end()}; +} + +ov::Shape get_quant_shape(const Output& output, + const std::shared_ptr& quantization, + const size_t& size) { + auto shape = ov::Shape{}; + if (size > 1) { + FRONT_END_GENERAL_CHECK(output.get_partial_shape().rank().is_static(), + "Per-Channel Quantization of tensor with dynamic rank"); + auto rank = output.get_partial_shape().size(); + shape = ov::Shape(rank, 1); + shape[quantization->get_axis()] = size; + } + return shape; +} + +void ov::frontend::tensorflow_lite::apply_quantization(ov::Output& output) { + auto rt_info = output.get_rt_info(); + if (!rt_info.count(QuantizationInfo::get_type_info_static())) // no quantization + return; + + auto quantization = rt_info[QuantizationInfo::get_type_info_static()].as>(); + if (!quantization || quantization->is_disabled()) + return; + + bool is_constant = ov::is_type(output.get_node_shared_ptr()); + bool is_input = ov::is_type(output.get_node_shared_ptr()); + + auto input_type = output.get_element_type(); + ov::Output input_low, input_high, output_low, output_high; + + auto zp = quantization->get_zero_point(); + auto scale = quantization->get_scale(); + + auto zp_shape = get_quant_shape(output, quantization, zp.size()); + auto scale_shape = get_quant_shape(output, quantization, scale.size()); + + auto input_rank = output.get_partial_shape().rank(); + FRONT_END_GENERAL_CHECK(input_rank.is_static(), "Quantization is no"); + + auto zp_node = ov::opset10::Constant::create(element::f32, zp_shape, zp); + auto scale_node = ov::opset10::Constant::create(element::f32, scale_shape, scale); + + if (is_constant) { + output = std::make_shared(output, element::f32); + if (std::any_of(zp.begin(), zp.end(), [](const int64_t& i) { + return i != 0; + })) + output = std::make_shared(output, zp_node); + output = std::make_shared(output, scale_node); + return; + } + + auto levels = 256; + if (is_input) { + FRONT_END_GENERAL_CHECK(input_type == element::u8 || input_type == element::i8, + "Inputs of type other than u8 is not yet supported"); + if (input_type == element::u8) { + output = std::make_shared(output, element::f32); + input_low = ov::opset10::Constant::create(element::f32, {}, {0}); + input_high = ov::opset10::Constant::create(element::f32, {}, {levels - 1}); + } else if (input_type == element::i8) { + output = std::make_shared(output, element::f32); + input_low = ov::opset10::Constant::create(element::f32, {}, {-128}); + input_high = ov::opset10::Constant::create(element::f32, {}, {127}); + } + } + if (std::all_of(zp.begin(), zp.end(), [](const int64_t& i) { + return i == 0; + })) { + output_low = ov::opset10::Constant::create(element::f32, {}, {0}); + } else { + output_low = std::make_shared(std::make_shared(scale_node), zp_node); + } + output_high = std::make_shared( + scale_node, + std::make_shared(ov::opset10::Constant::create(element::f32, {}, {levels - 1}), zp_node)); + if (!is_input) { + input_low = output_low; + input_high = output_high; + } + output = std::make_shared(output, input_low, input_high, output_low, output_high, levels); + quantization->disable(); // we applied parameters -- disable them so that they won't apply twice +} \ No newline at end of file diff --git a/src/frontends/tensorflow_lite/src/utils.hpp b/src/frontends/tensorflow_lite/src/utils.hpp new file mode 100644 index 00000000000..13478fbe276 --- /dev/null +++ b/src/frontends/tensorflow_lite/src/utils.hpp @@ -0,0 +1,25 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include "decoder_flatbuffer.h" +#include "place.hpp" +#include "quantization_info.hpp" +#include "schema_generated.h" + +namespace ov { +namespace frontend { +namespace tensorflow_lite { + +class TensorLitePlace; +class QuantizationInfo; + +ov::element::Type get_ov_type(const tflite::TensorType& tf_type); +ov::PartialShape get_ov_shape(const flatbuffers::Vector* tf_shape); +std::shared_ptr get_quantization(const tflite::QuantizationParameters* tf_quantization); +void apply_quantization(ov::Output& output); + +} // namespace tensorflow_lite +} // namespace frontend +} // namespace ov \ No newline at end of file diff --git a/src/frontends/tensorflow_lite/tests/CMakeLists.txt b/src/frontends/tensorflow_lite/tests/CMakeLists.txt new file mode 100644 index 00000000000..f0ab2f2b138 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/CMakeLists.txt @@ -0,0 +1,75 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +set(TARGET_NAME "ov_tensorflow_lite_frontend_tests") + +ov_add_test_target( + NAME ${TARGET_NAME} + ROOT ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDENCIES + tensorflow_lite_test_models + tensorflow_lite_fe_standalone_build_test + LINK_LIBRARIES + gtest_main_manifest + frontend_shared_test_classes + openvino_tensorflow_lite_frontend + ADD_CLANG_FORMAT + LABELS + OV + TF_FE +) + +# Test model generating + +ov_check_pip_packages(REQUIREMENTS_FILE "${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt" + MESSAGE_MODE WARNING + WARNING_MESSAGE "TensorFlow Lite frontend unit tests will be skipped" + RESULT_VAR tensorflow_FOUND) + +set(TEST_TENSORFLOW_LITE_MODELS_DIRNAME test_model_zoo/tensorflow_lite_test_models) +target_compile_definitions(${TARGET_NAME} PRIVATE -D TEST_TENSORFLOW_LITE_MODELS_DIRNAME=\"${TEST_TENSORFLOW_LITE_MODELS_DIRNAME}/\") + +# If 'tensorflow' is not found, code will still be compiled +# but models will not be generated and tests will fail +# This is done this way for 'code style' and check cases - cmake shall pass, but CI machine doesn't need to have +# 'tensorflow' installed to check code style +if (tensorflow_FOUND) + set(TEST_TENSORFLOW_LITE_MODELS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_TENSORFLOW_LITE_MODELS_DIRNAME}/) + + file(GLOB_RECURSE TENSORFLOW_GEN_SCRIPTS ${CMAKE_CURRENT_SOURCE_DIR}/test_models/gen_scripts/generate_*.py) + file(GLOB_RECURSE TENSORFLOW_ALL_SCRIPTS ${CMAKE_CURRENT_SOURCE_DIR}/*.py) + set(OUT_FILES "") + foreach(GEN_SCRIPT ${TENSORFLOW_GEN_SCRIPTS}) + get_filename_component(FILE_WE ${GEN_SCRIPT} NAME_WE) + set(OUT_DONE_FILE ${TEST_TENSORFLOW_LITE_MODELS}/${FILE_WE}_done.txt) + set(OUT_FILES ${OUT_DONE_FILE} ${OUT_FILES}) + add_custom_command(OUTPUT ${OUT_DONE_FILE} + COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/test_models/gen_wrapper.py + ${GEN_SCRIPT} + ${TEST_TENSORFLOW_LITE_MODELS} + ${OUT_DONE_FILE} + JOB_POOL four_jobs + DEPENDS ${TENSORFLOW_ALL_SCRIPTS} + ) + endforeach() + add_custom_target(tensorflow_lite_test_models DEPENDS ${OUT_FILES}) + + install(DIRECTORY ${TEST_TENSORFLOW_LITE_MODELS} + DESTINATION tests/${TEST_TENSORFLOW_LITE_MODELS_DIRNAME} + COMPONENT tests + EXCLUDE_FROM_ALL) +else() + # Produce warning message at build time as well + add_custom_command(OUTPUT unable_build_tensorflow_models.txt + COMMAND ${CMAKE_COMMAND} + -E cmake_echo_color --red "Warning: Unable to generate tensorflow lite test models. Running '${TARGET_NAME}' will likely fail" + ) + add_custom_target(tensorflow_lite_test_models DEPENDS unable_build_tensorflow_models.txt) +endif() + +get_target_property(TENSORFLOW_LITE_FRONTEND_SRC_DIR openvino_tensorflow_lite_frontend SOURCE_DIR) + +add_subdirectory(standalone_build) +add_dependencies(${TARGET_NAME} tensorflow_lite_fe_standalone_build_test) diff --git a/src/frontends/tensorflow_lite/tests/basic_api.cpp b/src/frontends/tensorflow_lite/tests/basic_api.cpp new file mode 100644 index 00000000000..96287fcfec7 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/basic_api.cpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "basic_api.hpp" + +#include "tf_utils.hpp" + +using namespace ngraph; +using namespace ov::frontend; + +using TFLiteBasicTest = FrontEndBasicTest; + +static const std::vector models{ + std::string("2in_2out/2in_2out.tflite"), +}; + +INSTANTIATE_TEST_SUITE_P(TFLiteBasicTest, + FrontEndBasicTest, + ::testing::Combine(::testing::Values(TF_LITE_FE), + ::testing::Values(std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME)), + ::testing::ValuesIn(models)), + FrontEndBasicTest::getTestCaseName); diff --git a/src/frontends/tensorflow_lite/tests/conversion.cpp b/src/frontends/tensorflow_lite/tests/conversion.cpp new file mode 100644 index 00000000000..97f3240d71d --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/conversion.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "conversion_extension.hpp" +#include "openvino/frontend/extension/telemetry.hpp" +#include "openvino/frontend/tensorflow_lite/frontend.hpp" +#include "so_extension.hpp" +#include "tf_utils.hpp" + +using namespace ov::frontend; + +using TFLiteConversionExtensionTest = FrontEndConversionExtensionTest; + +static const std::string translator_name = "LOGISTIC"; + +class TensorflowLiteFrontendWrapper : public ov::frontend::tensorflow_lite::FrontEnd { + void add_extension(const std::shared_ptr& extension) override { + ov::frontend::tensorflow_lite::FrontEnd::add_extension(extension); + + if (auto conv_ext = std::dynamic_pointer_cast(extension)) { + EXPECT_NE(std::find(m_conversion_extensions.begin(), m_conversion_extensions.end(), conv_ext), + m_conversion_extensions.end()) + << "ConversionExtension is not registered."; + EXPECT_NE(m_op_translators.find(conv_ext->get_op_type()), m_op_translators.end()) + << conv_ext->get_op_type() << " translator is not registered."; + } else if (auto telemetry = std::dynamic_pointer_cast(extension)) { + EXPECT_EQ(m_telemetry, telemetry) << "TelemetryExtension is not registered."; + } else if (auto transformation = std::dynamic_pointer_cast(extension)) { + EXPECT_NE(std::find(m_transformation_extensions.begin(), m_transformation_extensions.end(), transformation), + m_transformation_extensions.end()) + << "DecoderTransformationExtension is not registered."; + } else if (auto so_ext = std::dynamic_pointer_cast(extension)) { + EXPECT_NE(std::find(m_extensions.begin(), m_extensions.end(), so_ext), m_extensions.end()) + << "SOExtension is not registered."; + } + } +}; + +static ConversionExtensionFEParam getTestData() { + ConversionExtensionFEParam res; + res.m_frontEndName = TF_LITE_FE; + res.m_modelsPath = std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME); + res.m_modelName = "2in_2out/2in_2out.tflite"; + res.m_translatorName = translator_name; + res.m_frontend = std::make_shared(); + return res; +} + +INSTANTIATE_TEST_SUITE_P(TFLiteConversionExtensionTest, + FrontEndConversionExtensionTest, + ::testing::Values(getTestData()), + FrontEndConversionExtensionTest::getTestCaseName); diff --git a/src/frontends/tensorflow_lite/tests/convert_model.cpp b/src/frontends/tensorflow_lite/tests/convert_model.cpp new file mode 100644 index 00000000000..a6bf764a660 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/convert_model.cpp @@ -0,0 +1,23 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "convert_model.hpp" + +#include "tf_utils.hpp" + +using namespace ngraph; +using namespace ov::frontend; + +using TFLiteConvertModelTest = FrontEndConvertModelTest; + +static const std::vector models{ + std::string("2in_2out/2in_2out.tflite"), +}; + +INSTANTIATE_TEST_SUITE_P(TFLiteConvertModelTest, + FrontEndConvertModelTest, + ::testing::Combine(::testing::Values(TF_LITE_FE), + ::testing::Values(std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME)), + ::testing::ValuesIn(models)), + FrontEndConvertModelTest::getTestCaseName); diff --git a/src/frontends/tensorflow_lite/tests/library_extension.cpp b/src/frontends/tensorflow_lite/tests/library_extension.cpp new file mode 100644 index 00000000000..d8d3c1df577 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/library_extension.cpp @@ -0,0 +1,24 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "library_extension.hpp" + +#include "tf_utils.hpp" + +using namespace ov::frontend; + +using TFLiteLibraryExtensionTest = FrontendLibraryExtensionTest; + +static FrontendLibraryExtensionTestParams getTestData() { + FrontendLibraryExtensionTestParams params; + params.m_frontEndName = TF_LITE_FE; + params.m_modelsPath = std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME); + params.m_modelName = "2in_2out/2in_2out.tflite"; + return params; +} + +INSTANTIATE_TEST_SUITE_P(TFLiteLibraryExtensionTest, + FrontendLibraryExtensionTest, + ::testing::Values(getTestData()), + FrontendLibraryExtensionTest::getTestCaseName); diff --git a/src/frontends/tensorflow_lite/tests/op_extension.cpp b/src/frontends/tensorflow_lite/tests/op_extension.cpp new file mode 100644 index 00000000000..42115190196 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/op_extension.cpp @@ -0,0 +1,141 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "op_extension.hpp" + +#include "openvino/frontend/extension/op.hpp" +#include "openvino/frontend/tensorflow_lite/extension/op.hpp" +#include "so_extension.hpp" +#include "tf_utils.hpp" + +using namespace ov::frontend; + +using TFLiteOpExtensionTest = FrontEndOpExtensionTest; + +class Relu1 : public Relu { +public: + OPENVINO_OP("CustomRelu_1"); + OPENVINO_FRAMEWORK_MAP(tensorflow_lite) +}; + +class Relu2 : public Relu { +public: + OPENVINO_FRAMEWORK_MAP(tensorflow_lite, "CustomRelu_2") +}; + +class Relu3 : public Relu { +public: + OPENVINO_FRAMEWORK_MAP(tensorflow_lite, + "CustomRelu_3", + {{"ov_attribute_1", "fw_attribute_1"}, {"ov_attribute_2", "fw_attribute_2"}}) +}; + +class Relu4 : public Relu { +public: + OPENVINO_FRAMEWORK_MAP(tensorflow_lite, + "CustomRelu_4", + {{"ov_attribute_1", "fw_attribute_1"}, {"ov_attribute_2", "fw_attribute_2"}}, + { + {"ov_attribute_str", "string"}, + {"ov_attribute_int", 4}, + {"ov_attribute_bool", true}, + {"ov_attribute_float", 4.f}, + {"ov_attribute_vec_string", std::vector{"str1", "str2", "str3"}}, + {"ov_attribute_vec_int", std::vector{1, 2, 3, 4, 5, 6, 7}}, + {"ov_attribute_vec_bool", std::vector{true, false, true}}, + {"ov_attribute_vec_float", std::vector{1., 2., 3., 4., 5., 6., 7.}}, + }) +}; + +static OpExtensionFEParam getTestDataOpExtensionViaUserClass() { + OpExtensionFEParam res; + res.m_frontEndName = TF_LITE_FE; + res.m_modelsPath = std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME); + res.m_modelName = "2in_2out/2in_2out.tflite"; + // use core OpExtension + res.m_extensions = std::vector>{std::make_shared>(), + std::make_shared>(), + std::make_shared>(), + std::make_shared>()}; + return res; +} + +static OpExtensionFEParam getTestDataOpExtensionViaTFConstructor() { + OpExtensionFEParam res; + res.m_frontEndName = TF_LITE_FE; + res.m_modelsPath = std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME); + res.m_modelName = "2in_2out/2in_2out.pb"; + // use ov::frontend::tensorflow OpExtension + res.m_extensions = std::vector>{ + std::make_shared>("CustomRelu_5"), + std::make_shared>("ov_CustomRelu_6", "fw_CustomRelu_6"), + std::make_shared>( + "ov_CustomRelu_7", + "fw_CustomRelu_7", + std::map{{"ov_attribute_1", "fw_attribute_1"}, + {"ov_attribute_2", "fw_attribute_2"}}), + std::make_shared>( + "ov_CustomRelu_8", + "fw_CustomRelu_8", + std::map{{"ov_attribute_1", "fw_attribute_1"}, + {"ov_attribute_2", "fw_attribute_2"}}, + std::map{ + {"ov_attribute_str", "string"}, + {"ov_attribute_int", 4}, + {"ov_attribute_bool", true}, + {"ov_attribute_float", 4.f}, + {"ov_attribute_vec_string", std::vector{"str1", "str2", "str3"}}, + {"ov_attribute_vec_int", std::vector{1, 2, 3, 4, 5, 6, 7}}, + {"ov_attribute_vec_bool", std::vector{true, false, true}}, + {"ov_attribute_vec_float", std::vector{1., 2., 3., 4., 5., 6., 7.}}, + })}; + return res; +} + +static OpExtensionFEParam getTestDataOpExtensionViaCommonConstructor() { + OpExtensionFEParam res; + res.m_frontEndName = TF_LITE_FE; + res.m_modelsPath = std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME); + res.m_modelName = "2in_2out/2in_2out.tflite"; + // use ov::frontend::OpExtension + res.m_extensions = std::vector>{ + std::make_shared>("CustomRelu_9"), + std::make_shared>("ov_CustomRelu_10", "fw_CustomRelu_10"), + std::make_shared>( + "ov_CustomRelu_11", + "fw_CustomRelu_11", + std::map{{"ov_attribute_1", "fw_attribute_1"}, + {"ov_attribute_2", "fw_attribute_2"}}), + std::make_shared>( + "ov_CustomRelu_12", + "fw_CustomRelu_12", + std::map{{"ov_attribute_1", "fw_attribute_1"}, + {"ov_attribute_2", "fw_attribute_2"}}, + std::map{ + {"ov_attribute_str", "string"}, + {"ov_attribute_int", 4}, + {"ov_attribute_bool", true}, + {"ov_attribute_float", 4.f}, + {"ov_attribute_vec_string", std::vector{"str1", "str2", "str3"}}, + {"ov_attribute_vec_int", std::vector{1, 2, 3, 4, 5, 6, 7}}, + {"ov_attribute_vec_bool", std::vector{true, false, true}}, + {"ov_attribute_vec_float", std::vector{1., 2., 3., 4., 5., 6., 7.}}, + })}; + return res; +} + +INSTANTIATE_TEST_SUITE_P(TFLiteOpExtensionTestViaUserClass, + FrontEndOpExtensionTest, + ::testing::Values(getTestDataOpExtensionViaUserClass()), + FrontEndOpExtensionTest::getTestCaseName); + +INSTANTIATE_TEST_SUITE_P(TFOpExtensionViaTFConstructor, + FrontEndOpExtensionTest, + ::testing::Values(getTestDataOpExtensionViaTFConstructor()), + FrontEndOpExtensionTest::getTestCaseName); + +INSTANTIATE_TEST_SUITE_P(TFOpExtensionViaCommonConstructor, + FrontEndOpExtensionTest, + ::testing::Values(getTestDataOpExtensionViaCommonConstructor()), + FrontEndOpExtensionTest::getTestCaseName); \ No newline at end of file diff --git a/src/frontends/tensorflow_lite/tests/requirements.txt b/src/frontends/tensorflow_lite/tests/requirements.txt new file mode 100644 index 00000000000..6ad62764d61 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/requirements.txt @@ -0,0 +1,2 @@ +numpy>=1.16.6,<=1.23.4 +tensorflow>=1.15.5,<=2.11.0 diff --git a/src/frontends/tensorflow_lite/tests/standalone_build/CMakeLists.txt b/src/frontends/tensorflow_lite/tests/standalone_build/CMakeLists.txt new file mode 100644 index 00000000000..e65ed73bcc0 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/standalone_build/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# +set(TARGET_NAME "tensorflow_lite_fe_standalone_build_test") + +add_library(${TARGET_NAME} STATIC standalone_build_test.cpp) + +# This test verifies that application can link to TensorFlow Lite frontend only +# Other dependencies on core header files will be resolved automatically +target_link_libraries(${TARGET_NAME} PUBLIC openvino_tensorflow_lite_frontend) + +# Enable code style check +add_clang_format_target(${TARGET_NAME}_clang FOR_TARGETS ${TARGET_NAME}) diff --git a/src/frontends/tensorflow_lite/tests/standalone_build/standalone_build_test.cpp b/src/frontends/tensorflow_lite/tests/standalone_build/standalone_build_test.cpp new file mode 100644 index 00000000000..2c75df2476b --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/standalone_build/standalone_build_test.cpp @@ -0,0 +1,10 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +void test_load() { + ov::frontend::tensorflow_lite::FrontEnd fe; + fe.get_name(); +} \ No newline at end of file diff --git a/src/frontends/tensorflow_lite/tests/telemetry.cpp b/src/frontends/tensorflow_lite/tests/telemetry.cpp new file mode 100644 index 00000000000..cd6f94194ad --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/telemetry.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "telemetry.hpp" + +#include "tf_utils.hpp" + +using namespace ov::frontend; + +using TFLiteTelemetryTest = FrontEndTelemetryTest; + +static TelemetryFEParam getTestData() { + TelemetryFEParam res; + res.m_frontEndName = TF_LITE_FE; + res.m_modelsPath = std::string(TEST_TENSORFLOW_LITE_MODELS_DIRNAME); + res.m_modelName = "2in_2out/2in_2out.tflite"; + res.m_expected_events = {{std::make_tuple("mo", "op_count", "tflite_ADD", 1), + std::make_tuple("mo", "op_count", "tflite_CONCATENATION", 1), + std::make_tuple("mo", "op_count", "tflite_CONV_2D", 1), + std::make_tuple("mo", "op_count", "tflite_DEPTHWISE_CONV_2D", 1), + std::make_tuple("mo", "op_count", "tflite_LOGISTIC", 1), + std::make_tuple("mo", "op_count", "tflite_RELU", 1), + std::make_tuple("mo", "op_count", "tflite_PAD", 1)}}; + return res; +} + +INSTANTIATE_TEST_SUITE_P(TFLiteTelemetryTest, + FrontEndTelemetryTest, + ::testing::Values(getTestData()), + FrontEndTelemetryTest::getTestCaseName); diff --git a/src/frontends/tensorflow_lite/tests/test_models/gen_scripts/generate_2in_2out.py b/src/frontends/tensorflow_lite/tests/test_models/gen_scripts/generate_2in_2out.py new file mode 100644 index 00000000000..73323949d18 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/test_models/gen_scripts/generate_2in_2out.py @@ -0,0 +1,52 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import numpy as np +import os +import sys + +# do not print messages from TensorFlow +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' +import tensorflow as tf + +tf.compat.v1.reset_default_graph() + +# Create the graph and model +with tf.compat.v1.Session() as sess: + input1 = tf.compat.v1.placeholder(tf.float32, [1, 3, 3, 1], 'inputX1') + kernel1 = tf.constant(np.random.randn(1, 1, 1, 1), dtype=tf.float32) + conv2d1 = tf.nn.conv2d(input1, kernel1, strides=[1, 1], padding='VALID') + relu1 = tf.nn.relu6(conv2d1) + + input2 = tf.compat.v1.placeholder(tf.float32, [1, 3, 3, 1], 'inputX2') + kernel2 = tf.constant(np.random.randn(1, 1, 1, 1), dtype=tf.float32) + depthconv2d2 = tf.nn.depthwise_conv2d(input2, kernel2, strides=[1, 1, 1, 1], padding='VALID') + sigmoid2 = tf.nn.sigmoid(depthconv2d2) + + concat = tf.concat([relu1, sigmoid2], axis=-1) + + random_constant = tf.constant(np.random.randn(1, 1, 1, 1), dtype=tf.float32) + add1 = tf.add(concat, random_constant) + sig = tf.nn.relu(add1, name="sigmoid3b") + paddings = tf.constant([[0, 0], [1, 1], [2, 2], [0, 0]]) + pad = tf.pad(add1, paddings, "CONSTANT", name="pad") + + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph_def + +path_to_model_dir = os.path.join(sys.argv[1], "2in_2out") +tf_file_name = '2in_2out.pb' +tflite_file_name = '2in_2out.tflite' +tf.io.write_graph(tf_net, path_to_model_dir, tf_file_name, False) + +inputs = ["inputX1", "inputX2"] +outputs = ["pad", "sigmoid3b"] + +converter = tf.compat.v1.lite.TFLiteConverter.from_frozen_graph(os.path.join(path_to_model_dir, tf_file_name), inputs, outputs) +tflite_model = converter.convert() + +tflite_model_path = os.path.join(path_to_model_dir, tflite_file_name) +with tf.io.gfile.GFile(tflite_model_path, 'wb') as f: + f.write(tflite_model) + diff --git a/src/frontends/tensorflow_lite/tests/test_models/gen_wrapper.py b/src/frontends/tensorflow_lite/tests/test_models/gen_wrapper.py new file mode 100644 index 00000000000..98fd235ff8b --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/test_models/gen_wrapper.py @@ -0,0 +1,21 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import os +import subprocess +import sys + +if len(sys.argv) < 4: + print("Script[model in pbtxt format], output folder and mark file must be specified as arguments", str(sys.argv)) + exit(1) + +gen_script = sys.argv[1] +out_folder = sys.argv[2] +mark_file = sys.argv[3] + +assert gen_script.endswith('.py'), "Unexpected script: " + gen_script +subprocess.run([sys.executable, gen_script, out_folder], env=os.environ) + +# Create mark file indicating that script was executed +with open(mark_file, "w") as fp: + pass diff --git a/src/frontends/tensorflow_lite/tests/tf_utils.hpp b/src/frontends/tensorflow_lite/tests/tf_utils.hpp new file mode 100644 index 00000000000..dce326f08b1 --- /dev/null +++ b/src/frontends/tensorflow_lite/tests/tf_utils.hpp @@ -0,0 +1,9 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +static const std::string TF_LITE_FE = "tflite"; diff --git a/src/frontends/tests/frontend/shared/src/conversion.cpp b/src/frontends/tests/frontend/shared/src/conversion.cpp index de2849672a3..63c93f43131 100644 --- a/src/frontends/tests/frontend/shared/src/conversion.cpp +++ b/src/frontends/tests/frontend/shared/src/conversion.cpp @@ -56,6 +56,15 @@ TEST_P(FrontEndConversionExtensionTest, TestConversionExtension) { auto res = std::make_shared(ng_input); return {res}; })); + } else if (m_param.m_frontEndName == "tflite") { + frontend->add_extension( + std::make_shared(m_param.m_translatorName, + [&](const ov::frontend::NodeContext& node) -> ov::OutputVector { + invoked = true; + auto input = node.get_input(0); + auto res = std::make_shared(input); + return {res}; + })); } else if (m_param.m_frontEndName == "onnx") { frontend->add_extension( std::make_shared(m_param.m_translatorName, diff --git a/src/frontends/tests/frontend/shared/src/convert_model.cpp b/src/frontends/tests/frontend/shared/src/convert_model.cpp index 72a08e82dfe..34624269648 100644 --- a/src/frontends/tests/frontend/shared/src/convert_model.cpp +++ b/src/frontends/tests/frontend/shared/src/convert_model.cpp @@ -5,6 +5,7 @@ #include "convert_model.hpp" #include "common_test_utils/ngraph_test_utils.hpp" +#include "openvino/pass/visualize_tree.hpp" #include "utils.hpp" using namespace ngraph; @@ -47,7 +48,7 @@ TEST_P(FrontEndConvertModelTest, test_convert_partially_equal_convert) { FunctionsComparator func_comparator = FunctionsComparator::with_default(); // TODO: enable name comparison for tf when TransposeSinking is fixed, ticket 68960 - if (m_frontEnd->get_name() != "tf") { + if (m_frontEnd->get_name() != "tf" && m_frontEnd->get_name() != "tflite") { func_comparator.enable(FunctionsComparator::NAMES); } const FunctionsComparator::Result res = func_comparator(function, function_ref); @@ -66,7 +67,7 @@ TEST_P(FrontEndConvertModelTest, test_decode_convert_equal_convert) { FunctionsComparator func_comparator = FunctionsComparator::with_default(); // TODO: enable name comparison for tf when TransposeSinking is fixed, ticket 68960 - if (m_frontEnd->get_name() != "tf") { + if (m_frontEnd->get_name() != "tf" && m_frontEnd->get_name() != "tflite") { func_comparator.enable(FunctionsComparator::NAMES); } const FunctionsComparator::Result res = func_comparator(function, function_ref); diff --git a/src/frontends/tests/frontend/shared/test_builtin_extensions/CMakeLists.txt b/src/frontends/tests/frontend/shared/test_builtin_extensions/CMakeLists.txt index a447da5b82d..231ecfc5836 100644 --- a/src/frontends/tests/frontend/shared/test_builtin_extensions/CMakeLists.txt +++ b/src/frontends/tests/frontend/shared/test_builtin_extensions/CMakeLists.txt @@ -25,6 +25,11 @@ if (ENABLE_OV_TF_FRONTEND) list(APPEND DEFINITIONS ENABLE_OV_TF_FRONTEND) endif() +if (ENABLE_OV_TF_LITE_FRONTEND) + list(APPEND DEPENDENCIES openvino::frontend::tensorflow_lite) + list(APPEND DEFINITIONS ENABLE_OV_TF_LITE_FRONTEND) +endif() + if (ENABLE_OV_PADDLE_FRONTEND) list(APPEND DEPENDENCIES openvino::frontend::paddle) list(APPEND DEFINITIONS ENABLE_OV_PADDLE_FRONTEND) diff --git a/src/frontends/tests/frontend/shared/test_builtin_extensions/builtin_extensions.cpp b/src/frontends/tests/frontend/shared/test_builtin_extensions/builtin_extensions.cpp index 04d90f32e23..a80f1488342 100644 --- a/src/frontends/tests/frontend/shared/test_builtin_extensions/builtin_extensions.cpp +++ b/src/frontends/tests/frontend/shared/test_builtin_extensions/builtin_extensions.cpp @@ -33,6 +33,16 @@ # define TF_EXT #endif +#ifdef ENABLE_OV_TF_LITE_FRONTEND +# include +# define TF_LITE_EXT \ + std::make_shared("NewCustomOp_6", \ + CustomTranslatorTensorflow), \ + std::make_shared("RELU", ReluToSwishTranslator), +#else +# define TF_LITE_EXT +#endif + ov::OutputVector CustomTranslatorCommon_1(const ov::frontend::NodeContext& node) { return ov::OutputVector(); } @@ -64,4 +74,4 @@ std::map CustomTranslatorPaddle(const ov::fronten OPENVINO_CREATE_EXTENSIONS(std::vector( {std::make_shared("NewCustomOp_1", CustomTranslatorCommon_1), std::make_shared("NewCustomOp_2", CustomTranslatorCommon_2), - ONNX_EXT PADDLE_EXT TF_EXT})); + ONNX_EXT PADDLE_EXT TF_EXT TF_LITE_EXT})); diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/compare_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/compare_test.cpp index 9dd369b9103..e6ee2947b57 100644 --- a/src/plugins/intel_cpu/tests/unit/shape_inference_test/compare_test.cpp +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/compare_test.cpp @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // -#pragma once - #include #include "compare.hpp" diff --git a/src/tests/ie_test_utils/functional_test_utils/layer_tests_summary/run_parallel.py b/src/tests/ie_test_utils/functional_test_utils/layer_tests_summary/run_parallel.py index 1df16e9e76d..bdd5d2323ab 100644 --- a/src/tests/ie_test_utils/functional_test_utils/layer_tests_summary/run_parallel.py +++ b/src/tests/ie_test_utils/functional_test_utils/layer_tests_summary/run_parallel.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2022 Intel Corporation +# Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 from utils import utils diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 1f9a4c49483..ea12663f909 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -258,7 +258,7 @@ if(ENABLE_OV_PADDLE_FRONTEND OR ENABLE_OV_ONNX_FRONTEND OR ENABLE_OV_TF_FRONTEND set(Protobuf_LIBRARIES protobuf::libprotobuf) set(PROTOC_EXECUTABLE protobuf::protoc) - foreach(target ${PROTOC_EXECUTABLE} ${Protobuf_LIBRARIES} ${Protobuf_LITE_LIBRARIES}) + foreach(target IN LISTS PROTOC_EXECUTABLE Protobuf_LIBRARIES Protobuf_LITE_LIBRARIES) set_property(TARGET ${target} PROPERTY IMPORTED_GLOBAL ON) endforeach() else() @@ -285,6 +285,32 @@ if(ENABLE_OV_PADDLE_FRONTEND OR ENABLE_OV_ONNX_FRONTEND OR ENABLE_OV_TF_FRONTEND endforeach() endif() +# +# Flat Buffers +# + +if(ENABLE_OV_TF_LITE_FRONTEND) + if(ENABLE_SYSTEM_FLATBUFFERS) + find_package(Flatbuffers REQUIRED) + + # we don't actually use library files (.so | .dylib | .a) itself, only headers + set(flatbuffers_LIBRARY flatbuffers::flatbuffers) + set(flatbuffers_COMPILER flatbuffers::flatc) + + foreach(target IN LISTS flatbuffers_LIBRARY flatbuffers_COMPILER) + set_property(TARGET ${target} PROPERTY IMPORTED_GLOBAL ON) + endforeach() + endif() + + if(NOT Flatbuffers_FOUND) + add_subdirectory(flatbuffers EXCLUDE_FROM_ALL) + endif() + + set(flatbuffers_INCLUDE_DIRECTORIES $ PARENT_SCOPE) + set(flatbuffers_COMPILER ${flatbuffers_COMPILER} PARENT_SCOPE) + set(flatbuffers_DEPENDENCY ${flatbuffers_DEPENDENCY} PARENT_SCOPE) +endif() + # # ONNX # diff --git a/thirdparty/flatbuffers/CMakeLists.txt b/thirdparty/flatbuffers/CMakeLists.txt new file mode 100644 index 00000000000..3f9f7f64cfc --- /dev/null +++ b/thirdparty/flatbuffers/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 +# + +set(FLATBUFFERS_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(FLATBUFFERS_BUILD_FLATLIB OFF CACHE BOOL "" FORCE) +set(FLATBUFFERS_BUILD_FLATHASH OFF CACHE BOOL "" FORCE) +set(FLATBUFFERS_INSTALL OFF CACHE BOOL "" FORCE) +set(FLATBUFFERS_CPP_STD ${CMAKE_CXX_STANDARD}) + +# note: HOST_AARCH64 AND X86_64 are not handled for Apple explicitly, becuase it can work via Rosetta +if(CMAKE_CROSSCOMPILING OR (APPLE AND (HOST_X86_64 AND AARCH64)) ) + set(FLATBUFFERS_BUILD_FLATC OFF CACHE BOOL "" FORCE) +else() + set(FLATBUFFERS_BUILD_FLATC ON CACHE BOOL "" FORCE) +endif() + +# build flatc when we don't use cross-compilation + +add_subdirectory(flatbuffers EXCLUDE_FROM_ALL) + +# build flatc using cross-compilation + +if(FLATBUFFERS_BUILD_FLATC) + if(CMAKE_COMPILER_IS_GNUCXX) + set_target_properties(flatc PROPERTIES COMPILE_OPTIONS "-Wno-shadow") + endif() + + set(flatbuffers_COMPILER $ PARENT_SCOPE) + set(flatbuffers_DEPENDENCY flatc PARENT_SCOPE) +else() + set(HOST_FLATC_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/install") + + ov_native_compile_external_project( + TARGET_NAME host_flatc + NATIVE_INSTALL_DIR "${HOST_FLATC_INSTALL_DIR}" + CMAKE_ARGS "-DFLATBUFFERS_BUILD_TESTS=${FLATBUFFERS_BUILD_TESTS}" + "-DFLATBUFFERS_BUILD_FLATLIB=${FLATBUFFERS_BUILD_FLATLIB}" + "-DFLATBUFFERS_CPP_STD=${FLATBUFFERS_CPP_STD}" + "-DFLATBUFFERS_BUILD_FLATHASH=${FLATBUFFERS_BUILD_FLATHASH}" + NATIVE_SOURCE_SUBDIR "flatbuffers" + NATIVE_TARGETS flatc) + + set(flatbuffers_COMPILER "${HOST_FLATC_INSTALL_DIR}/bin/flatc") + add_executable(flatbuffers::flatc IMPORTED GLOBAL) + set_property(TARGET flatbuffers::flatc APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(flatbuffers::flatc PROPERTIES + IMPORTED_LOCATION_RELEASE "${flatbuffers_COMPILER}") + set_target_properties(flatbuffers::flatc PROPERTIES + MAP_IMPORTED_CONFIG_DEBUG Release + MAP_IMPORTED_CONFIG_MINSIZEREL Release + MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release) + add_dependencies(flatbuffers::flatc host_flatc) + + set(flatbuffers_DEPENDENCY host_flatc PARENT_SCOPE) + set(flatbuffers_COMPILER "${flatbuffers_COMPILER}" PARENT_SCOPE) +endif() + +# set parent scope + +set(flatbuffers_LIBRARY FlatBuffers PARENT_SCOPE) diff --git a/thirdparty/flatbuffers/flatbuffers b/thirdparty/flatbuffers/flatbuffers new file mode 160000 index 00000000000..2ee0c7d4a7b --- /dev/null +++ b/thirdparty/flatbuffers/flatbuffers @@ -0,0 +1 @@ +Subproject commit 2ee0c7d4a7b644bee4d388998456ba42114f28f2 diff --git a/thirdparty/protobuf/CMakeLists.txt b/thirdparty/protobuf/CMakeLists.txt index 830f6fd39e5..382052a9f91 100644 --- a/thirdparty/protobuf/CMakeLists.txt +++ b/thirdparty/protobuf/CMakeLists.txt @@ -68,94 +68,19 @@ if(protobuf_VERSION VERSION_LESS "3.9") message(FATAL_ERROR "Minimum supported version of protobuf-lite library is 3.9.0 (provided ${protobuf_VERSION})") endif() -# build protoc separatelly for host system processor +# build protoc separatelly for build system processor if(NOT protobuf_BUILD_PROTOC_BINARIES) - include(ExternalProject) - - if(YOCTO_AARCH64) - # need to unset several variables which can set env to cross-environment - foreach(var SDKTARGETSYSROOT CONFIG_SITE OECORE_NATIVE_SYSROOT OECORE_TARGET_SYSROOT - OECORE_ACLOCAL_OPTS OECORE_BASELIB OECORE_TARGET_ARCH OECORE_TARGET_OS CC CXX - CPP AS LD GDB STRIP RANLIB OBJCOPY OBJDUMP READELF AR NM M4 TARGET_PREFIX - CONFIGURE_FLAGS CFLAGS CXXFLAGS LDFLAGS CPPFLAGS KCFLAGS OECORE_DISTRO_VERSION - OECORE_SDK_VERSION ARCH CROSS_COMPILE OE_CMAKE_TOOLCHAIN_FILE OPENSSL_CONF - OE_CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX PKG_CONFIG_SYSROOT_DIR PKG_CONFIG_PATH) - if(DEFINED ENV{${var}}) - list(APPEND cmake_env --unset=${var}) - endif() - endforeach() - - # filter out PATH from yocto locations - string(REPLACE ":" ";" custom_path "$ENV{PATH}") - foreach(path IN LISTS custom_path) - if(NOT path MATCHES "^$ENV{OECORE_NATIVE_SYSROOT}") - list(APPEND clean_path "${path}") - endif() - endforeach() - - find_host_program(HOST_CMAKE_COMMAND - NAMES cmake - PATHS ${clean_path} - DOC "Host cmake" - REQUIRED - NO_DEFAULT_PATH) - else() - set(HOST_CMAKE_COMMAND "${CMAKE_COMMAND}") - endif() - - # if env has CMAKE_TOOLCHAIN_FILE, we need to skip it - if(DEFINED ENV{CMAKE_TOOLCHAIN_FILE}) - list(APPEND cmake_env --unset=CMAKE_TOOLCHAIN_FILE) - endif() - - # compile flags - if(CMAKE_COMPILER_IS_GNUCXX) - set(compile_flags "-Wno-undef -Wno-error -Wno-deprecated-declarations") - endif() - - set(HOST_PROTOC_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build") set(HOST_PROTOC_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/install") - set(HOST_PROTOC_CMAKE_COMMAND "${CMAKE_COMMAND}" -E env ${cmake_env} "${HOST_CMAKE_COMMAND}") - ExternalProject_Add(host_protoc - # Directory Options - SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" - PREFIX "${CMAKE_CURRENT_BINARY_DIR}" - BINARY_DIR "${HOST_PROTOC_BINARY_DIR}" - INSTALL_DIR "${HOST_PROTOC_INSTALL_DIR}" - # Configure Step Options: - CMAKE_COMMAND - ${HOST_PROTOC_CMAKE_COMMAND} - CMAKE_ARGS - "-DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}" - "-DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}" - "-DCMAKE_CXX_LINKER_LAUNCHER=${CMAKE_CXX_LINKER_LAUNCHER}" - "-DCMAKE_C_LINKER_LAUNCHER=${CMAKE_C_LINKER_LAUNCHER}" - "-DCMAKE_CXX_FLAGS=${compile_flags}" - "-DCMAKE_C_FLAGS=${compile_flags}" - "-DCMAKE_POLICY_DEFAULT_CMP0069=NEW" - "-DCMAKE_INSTALL_PREFIX=${HOST_PROTOC_INSTALL_DIR}" - "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" - # protoc specific - "-DTHREADS_PREFER_PTHREAD_FLAG=${THREADS_PREFER_PTHREAD_FLAG}" - "-Dprotobuf_VERBOSE=${protobuf_VERBOSE}" - "-Dprotobuf_BUILD_TESTS=${protobuf_BUILD_TESTS}" - "-Dprotobuf_WITH_ZLIB=${protobuf_WITH_ZLIB}" - CMAKE_GENERATOR "${CMAKE_GENERATOR}" - SOURCE_SUBDIR - "protobuf/cmake" - # Build Step Options: - BUILD_COMMAND - ${HOST_PROTOC_CMAKE_COMMAND} - --build "${HOST_PROTOC_BINARY_DIR}" - --config Release - -- protoc libprotobuf-lite - # Test Step Options: - TEST_EXCLUDE_FROM_MAIN ON - # Target Options: - EXCLUDE_FROM_ALL ON - ) + ov_native_compile_external_project( + TARGET_NAME host_protoc + NATIVE_INSTALL_DIR "${HOST_PROTOC_INSTALL_DIR}" + CMAKE_ARGS "-Dprotobuf_VERBOSE=${protobuf_VERBOSE}" + "-Dprotobuf_BUILD_TESTS=${protobuf_BUILD_TESTS}" + "-Dprotobuf_WITH_ZLIB=${protobuf_WITH_ZLIB}" + NATIVE_SOURCE_SUBDIR "protobuf/cmake" + NATIVE_TARGETS protoc libprotobuf-lite) set(PROTOC_EXECUTABLE "${HOST_PROTOC_INSTALL_DIR}/bin/protoc") add_executable(protobuf::protoc IMPORTED GLOBAL) diff --git a/tools/mo/CMakeLists.txt b/tools/mo/CMakeLists.txt index 9546fb895e7..daec2b6c417 100644 --- a/tools/mo/CMakeLists.txt +++ b/tools/mo/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2022 Intel Corporation +# Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 if(NOT ENABLE_PYTHON)