summaryrefslogtreecommitdiff
path: root/compiler/tfinfo-v2
diff options
context:
space:
mode:
authorChunseok Lee <chunseok.lee@samsung.com>2020-12-14 14:43:04 +0900
committerChunseok Lee <chunseok.lee@samsung.com>2020-12-14 14:43:04 +0900
commit12d88feea8573f8490629cf62fc342b152e57d65 (patch)
tree3c734cc4d629834d2d523f4575ef84cd64684e57 /compiler/tfinfo-v2
parentd6b371e095d737922187a518b8faba1ef6f3a2b1 (diff)
downloadnnfw-12d88feea8573f8490629cf62fc342b152e57d65.tar.gz
nnfw-12d88feea8573f8490629cf62fc342b152e57d65.tar.bz2
nnfw-12d88feea8573f8490629cf62fc342b152e57d65.zip
Imported Upstream version 1.11.0upstream/1.11.0
Diffstat (limited to 'compiler/tfinfo-v2')
-rw-r--r--compiler/tfinfo-v2/CMakeLists.txt36
-rw-r--r--compiler/tfinfo-v2/README.md1
-rw-r--r--compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h43
-rw-r--r--compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h124
-rw-r--r--compiler/tfinfo-v2/proto/tfinfo-v2.proto46
-rw-r--r--compiler/tfinfo-v2/requires.cmake2
-rw-r--r--compiler/tfinfo-v2/src/TFInfo_v2.test.cpp246
-rw-r--r--compiler/tfinfo-v2/src/TensorInfoLoader.cpp179
-rw-r--r--compiler/tfinfo-v2/src/TensorSignature.cpp17
9 files changed, 694 insertions, 0 deletions
diff --git a/compiler/tfinfo-v2/CMakeLists.txt b/compiler/tfinfo-v2/CMakeLists.txt
new file mode 100644
index 000000000..cf438ea29
--- /dev/null
+++ b/compiler/tfinfo-v2/CMakeLists.txt
@@ -0,0 +1,36 @@
+nnas_find_package(Protobuf QUIET)
+
+if(NOT Protobuf_FOUND)
+ return()
+endif(NOT Protobuf_FOUND)
+
+# generating and building schema
+Protobuf_Generate(TFINFO_PROTO
+ "${CMAKE_CURRENT_BINARY_DIR}/generated"
+ "./proto"
+ tfinfo-v2.proto)
+
+add_library(tfinfo_v2_proto STATIC ${TFINFO_PROTO_SOURCES})
+set_target_properties(tfinfo_v2_proto PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(tfinfo_v2_proto PUBLIC ${TFINFO_PROTO_INCLUDE_DIRS})
+target_link_libraries(tfinfo_v2_proto PUBLIC libprotobuf)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(tfinfo_v2 STATIC ${SOURCES})
+set_target_properties(tfinfo_v2 PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(tfinfo_v2 PUBLIC include)
+target_link_libraries(tfinfo_v2 PRIVATE tfinfo_v2_proto)
+target_link_libraries(tfinfo_v2 PRIVATE oops)
+target_link_libraries(tfinfo_v2 PRIVATE stdex)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(tfinfo_v2_test ${TESTS})
+target_link_libraries(tfinfo_v2_test tfinfo_v2)
diff --git a/compiler/tfinfo-v2/README.md b/compiler/tfinfo-v2/README.md
new file mode 100644
index 000000000..72fbdb06a
--- /dev/null
+++ b/compiler/tfinfo-v2/README.md
@@ -0,0 +1 @@
+# tfinfo-v2
diff --git a/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h b/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h
new file mode 100644
index 000000000..ee3348e85
--- /dev/null
+++ b/compiler/tfinfo-v2/include/tfinfo-v2/TensorInfoLoader.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFINFO_TENSOR_INFO_LOADER_H__
+#define __TFINFO_TENSOR_INFO_LOADER_H__
+
+#include "TensorSignature.h"
+
+#include <memory>
+#include <vector>
+
+namespace tfinfo
+{
+inline namespace v2
+{
+
+/**
+ * @brief Function to create TensorSignatures defined in info file
+ */
+TensorSignatures load(const char *info_path);
+
+/**
+ * @brief Function to create TensorSignatures from stream
+ */
+TensorSignatures load(std::istream *stream, const char *path_for_error_msg);
+
+} // namespace v2
+} // namespace tfinfo
+
+#endif // __TFINFO_TENSOR_INFO_LOADER_H__
diff --git a/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h
new file mode 100644
index 000000000..f26d0354a
--- /dev/null
+++ b/compiler/tfinfo-v2/include/tfinfo-v2/TensorSignature.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __TFINFO_V2_TENSORSIGNATURE_H__
+#define __TFINFO_V2_TENSORSIGNATURE_H__
+
+#include <map>
+#include <vector>
+#include <memory>
+#include <string>
+#include <stdexcept>
+
+namespace tfinfo
+{
+inline namespace v2
+{
+
+/**
+ * @brief Supported Data Types
+ */
+enum class DataType
+{
+ UNKNOWN,
+
+ FLOAT32, // IEEE 32-bit floating point
+ /* To be added */
+};
+
+/**
+ * @brief Class to represent axis and size of dims.
+ * User should enter axis and size of dim(s) when input tensor(s) contain(s) unknown dim(s).
+ * Such axis and size of dim(s) will be stored in ShapeHint.
+ */
+class ShapeHint
+{
+ using AxisHint = uint32_t;
+ using SizeHint = uint64_t;
+
+public:
+ ShapeHint() = default;
+
+ void add(AxisHint axis, SizeHint size)
+ {
+ if (_dims.find(axis) != _dims.end())
+ throw std::runtime_error("dim value already exists");
+
+ _dims[axis] = size;
+ }
+
+ std::map<AxisHint, SizeHint>::const_iterator cbegin() const { return _dims.cbegin(); }
+
+ std::map<AxisHint, SizeHint>::const_iterator cend() const { return _dims.cend(); }
+
+ bool empty() { return _dims.size() == 0; }
+
+ size_t size() { return _dims.size(); }
+
+private:
+ std::map<AxisHint, SizeHint> _dims;
+};
+
+using TensorName = std::string;
+
+/**
+ * @brief Class to store input and output tensor information
+ */
+class TensorSignature final
+{
+public:
+ enum class Kind
+ {
+ Input,
+ Output
+ };
+
+ TensorSignature(const Kind kind, const std::string &name) : _kind(kind), _tensor_name()
+ {
+ // tensor name can be a form of "placeholder:0" or "placeholder".
+ // If tensor index is omitted, ":0" is appended
+ auto pos = name.find(":");
+ if (pos == std::string::npos)
+ _tensor_name.assign(name + ":0");
+ else
+ _tensor_name.assign(name);
+ }
+
+ TensorSignature(const Kind kind, const std::string &name, const ShapeHint &shape_hint)
+ : TensorSignature(kind, name)
+ {
+ _shape_hint = shape_hint;
+ }
+
+public:
+ Kind kind() const { return _kind; }
+
+ const TensorName &name() { return _tensor_name; }
+
+ ShapeHint &shapeHint() { return _shape_hint; }
+
+private:
+ Kind _kind;
+ std::string _tensor_name;
+ ShapeHint _shape_hint;
+};
+
+using TensorSignatures = std::vector<std::unique_ptr<TensorSignature>>;
+
+} // namespace v2
+} // namespace tfinfo
+
+#endif // __TFINFO_V2_TENSORSIGNATURE_H__
diff --git a/compiler/tfinfo-v2/proto/tfinfo-v2.proto b/compiler/tfinfo-v2/proto/tfinfo-v2.proto
new file mode 100644
index 000000000..4f7c47b4a
--- /dev/null
+++ b/compiler/tfinfo-v2/proto/tfinfo-v2.proto
@@ -0,0 +1,46 @@
+syntax = "proto3";
+
+package tfinfo_v2_proto;
+option cc_enable_arenas = true;
+
+/*
+Example of prototxt file is as follows:
+
+input {
+ name : "placeholder:0"
+}
+output {
+ name : "relu:0"
+}
+
+When a model has unknown dim in its input,
+value of all unknowm dims must be provided like the following:
+
+input {
+ name : "placeholder:0"
+ dim { axis: 0 size: 8 }
+ dim { axis: 3 size: 4 }
+}
+output {
+ name : "relu:0"
+}
+*/
+
+message Dim {
+ int32 axis = 1;
+ int64 size = 2; // tensorflow uses int64
+}
+
+message OutputDef {
+ string name = 1;
+}
+
+message InputDef {
+ string name = 1;
+ repeated Dim dim = 2;
+}
+
+message InfoDef {
+ repeated InputDef input = 1;
+ repeated OutputDef output = 2;
+}
diff --git a/compiler/tfinfo-v2/requires.cmake b/compiler/tfinfo-v2/requires.cmake
new file mode 100644
index 000000000..e7efab4fb
--- /dev/null
+++ b/compiler/tfinfo-v2/requires.cmake
@@ -0,0 +1,2 @@
+require("oops")
+require("stdex")
diff --git a/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp
new file mode 100644
index 000000000..02a2d9199
--- /dev/null
+++ b/compiler/tfinfo-v2/src/TFInfo_v2.test.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tfinfo-v2/TensorInfoLoader.h"
+
+#include "tfinfo-v2/TensorSignature.h"
+
+#include <gtest/gtest.h>
+
+#include <sstream>
+#include <map>
+
+#define TC_CASE(content) #content
+
+using namespace tfinfo::v2;
+
+namespace
+{
+
+// clang-format off
+const std::vector<std::string> success_cases =
+{
+ TC_CASE(
+ output {
+ name : "relu:0"
+ }
+ ),
+
+ TC_CASE(
+ input {
+ name : "placeholder:0"
+ }
+
+ input {
+ name : "placeholder:1"
+ dim { axis:0 size: 1 }
+ dim { axis:2 size: 4 }
+ }
+
+ output {
+ name : "relu:0"
+ }
+ ),
+ // clang-format on
+};
+
+} // namespace
+
+TEST(TFINFO_V2, success_0)
+{
+ std::stringstream ss{success_cases[0]};
+
+ auto tensors = load(&ss, "tfinfo_v2_test");
+
+ std::map<std::string, tfinfo::v2::TensorSignature *> m;
+
+ for (auto &tensor : tensors)
+ {
+ m[tensor->name()] = tensor.get();
+ }
+
+ ASSERT_EQ(m.size(), 1);
+
+ auto t1 = m["relu:0"];
+ {
+ ASSERT_EQ(t1->kind(), tfinfo::v2::TensorSignature::Kind::Output);
+ ASSERT_TRUE(t1->shapeHint().empty());
+ }
+}
+
+TEST(TFINFO_V2, success_1)
+{
+ std::stringstream ss{success_cases[1]};
+
+ auto tensors = load(&ss, "tfinfo_v2_test");
+
+ std::map<std::string, tfinfo::v2::TensorSignature *> m;
+
+ for (auto &tensor : tensors)
+ {
+ m[tensor->name()] = tensor.get();
+ }
+
+ ASSERT_EQ(m.size(), 3);
+
+ auto t1 = m["placeholder:0"];
+ {
+ ASSERT_EQ(t1->kind(), tfinfo::v2::TensorSignature::Kind::Input);
+ ASSERT_TRUE(t1->shapeHint().empty());
+ }
+
+ auto t2 = m["placeholder:1"];
+ {
+ ASSERT_EQ(t2->kind(), tfinfo::v2::TensorSignature::Kind::Input);
+ ASSERT_FALSE(t2->shapeHint().empty());
+
+ auto iter = t2->shapeHint().cbegin();
+
+ ASSERT_TRUE(iter != t2->shapeHint().cend());
+ ASSERT_EQ(iter->first, 0); // axis
+ ASSERT_EQ(iter->second, 1); // size
+
+ iter++;
+
+ ASSERT_TRUE(iter != t2->shapeHint().cend());
+ ASSERT_EQ(iter->first, 2); // axis
+ ASSERT_EQ(iter->second, 4); // size
+
+ iter++;
+
+ ASSERT_TRUE(iter == t2->shapeHint().cend());
+ }
+
+ auto t3 = m["relu:0"];
+ {
+ ASSERT_EQ(t3->kind(), tfinfo::v2::TensorSignature::Kind::Output);
+ ASSERT_TRUE(t3->shapeHint().empty());
+ }
+}
+
+namespace
+{
+
+// clang-format off
+const std::vector<std::string> fail_cases =
+ {
+ // no output
+ TC_CASE(
+ input {
+ name : "relu:0"
+ }
+ ),
+
+ // no name in input
+ TC_CASE(
+ input {
+ shape {
+ dim { size: 1 }
+ dim { size: 2 }
+ }
+ }
+ output {
+ name : "relu:0"
+ }
+ ),
+
+ // wrong name format - no tensor index
+ TC_CASE(
+ output {
+ name : "name_with_no_index"
+ }
+ ),
+
+ // wrong name format - no name but numbers
+ TC_CASE(
+ output {
+ name : "1"
+ }
+ ),
+
+ // duplicated node def - input, input
+ TC_CASE(
+ input {
+ name : "duplicated_name:0"
+ }
+
+ input {
+ name : "duplicated_name:0"
+ }
+ ),
+
+ // duplicated node def - input, output
+ TC_CASE(
+ input {
+ name : "duplicated_name:0"
+ }
+
+ output {
+ name : "duplicated_name:0"
+ }
+ ),
+
+ // wrong keyword ('in', 'out' instead of 'input', 'output')
+ TC_CASE(
+ in {
+ name : "a:0"
+ }
+
+ out {
+ name : "b:0"
+ }
+ ),
+
+ // wrong keyword ('input_name' instead of 'name')
+ TC_CASE(
+ input {
+ input_name : "a:0"
+ }
+
+ output {
+ name : "b:0"
+ }
+ ),
+
+ // using deprecated format
+ // (note that because of ",", macro TC_CASE cannot be used.)
+ R"(
+ input, a:0, TF_FLOAT, [2, 3 ,4]
+ output, b:0, TF_FLOAT, [2, 3 ,4]
+ )",
+ // clang-format on
+};
+
+} // namespace
+
+TEST(TFINFO_V2, failure)
+{
+ for (int i = 0; i < fail_cases.size(); i++)
+ {
+ std::stringstream ss{fail_cases[i]};
+
+ try
+ {
+ load(&ss, "tfinfo_v2_test");
+
+ FAIL();
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << ss.str() << std::endl << e.what() << '\n';
+ }
+ }
+}
diff --git a/compiler/tfinfo-v2/src/TensorInfoLoader.cpp b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp
new file mode 100644
index 000000000..0bf828773
--- /dev/null
+++ b/compiler/tfinfo-v2/src/TensorInfoLoader.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tfinfo-v2/TensorInfoLoader.h"
+
+#include "tfinfo-v2/TensorSignature.h"
+
+#include <oops/UserExn.h>
+#include <stdex/Memory.h>
+
+#include <tfinfo-v2.pb.h>
+
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <fstream>
+#include <fcntl.h>
+
+namespace
+{
+
+// for testing purpose
+bool load_text(std::istream *stream, tfinfo_v2_proto::InfoDef &info_def)
+{
+ google::protobuf::io::IstreamInputStream iis(stream);
+
+ return google::protobuf::TextFormat::Parse(&iis, &info_def);
+}
+
+bool is_num(const std::string &num)
+{
+ for (int i = 0; i < num.length(); i++)
+ if (!isdigit(num[i]))
+ return false;
+
+ return true;
+}
+
+void validate_tensor_name(const std::string &tensor_name, const char *path)
+{
+ // Note that Tensorflow tensor name format is
+ // operation name ":" index, e.g., "in/placeholder:0"
+ int pos = tensor_name.find(":");
+ if (pos == std::string::npos)
+ throw oops::UserExn("Missing index separator, ':'", "name", tensor_name, "file", path);
+
+ if (tensor_name.length() == pos + 1) // ':' is the last char
+ throw oops::UserExn("Missing tensor index after ':'", "name", tensor_name, "file", path);
+
+ // 1. Validating operation name.
+ // for naming format, refer to https://www.tensorflow.org/api_docs/python/tf/Operation#__init__
+ // First char is in the form of "[A-Za-z0-9.]"
+ // and the rest chars are in the form of "[A-Za-z0-9_.\\-/]*"
+ std::string op_name = tensor_name.substr(0, pos);
+
+ // first character
+ if (!(isalnum(op_name[0]) || op_name[0] == '.'))
+ throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path);
+
+ // and the rest chars
+ for (int i = 1; i < op_name.length(); i++)
+ if (!(isalnum(op_name[i]) || std::string("_.\\-/").find(op_name[i]) != std::string::npos))
+ throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path);
+
+ // 2. validating index after ":"
+ std::string index = tensor_name.substr(pos + 1, op_name.length() - pos - 1);
+
+ if (!is_num(index))
+ throw oops::UserExn("Wrong tensor name format", "name", tensor_name, "file", path);
+}
+
+void check_duplicate(tfinfo::v2::TensorSignatures &tensors, const char *path)
+{
+ std::map<std::string, bool> tool;
+ for (auto &tensor : tensors)
+ {
+ if (tool.find(tensor->name()) != tool.end())
+ throw oops::UserExn("Duplicate tensor definition", "name", tensor->name(), "file", path);
+ else
+ tool[tensor->name()] = true;
+ }
+}
+
+void convert(tfinfo_v2_proto::InfoDef &info_def, tfinfo::v2::TensorSignatures &tensors,
+ const char *path)
+{
+ // processing input. Note that there could be no input.
+ if (auto input_size = info_def.input_size())
+ {
+ for (int i = 0; i < input_size; i++)
+ {
+ auto input_def = info_def.input().Get(i);
+
+ auto name = input_def.name();
+ validate_tensor_name(name, path);
+
+ auto tensor = stdex::make_unique<tfinfo::v2::TensorSignature>(
+ tfinfo::v2::TensorSignature::Kind::Input, name);
+
+ // when there is dim attribute for unknown shape
+ if (input_def.dim_size() > 0)
+ {
+ for (int d = 0; d < input_def.dim().size(); d++)
+ {
+ auto dim = input_def.dim(d);
+ tensor->shapeHint().add(dim.axis(), dim.size());
+ }
+ }
+
+ tensors.emplace_back(std::move(tensor));
+ }
+ }
+
+ // processing output
+ auto output_size = info_def.output_size();
+ if (output_size == 0)
+ throw oops::UserExn("Missing output node. At least 1 output node must exist", "file", path);
+
+ if (auto output_node_size = info_def.output_size())
+ {
+ for (int i = 0; i < output_node_size; i++)
+ {
+ auto name = info_def.output().Get(i).name();
+ validate_tensor_name(name, path);
+
+ auto tensor = stdex::make_unique<tfinfo::v2::TensorSignature>(
+ tfinfo::v2::TensorSignature::Kind::Output, name);
+ tensors.emplace_back(std::move(tensor));
+ }
+ }
+
+ check_duplicate(tensors, path);
+}
+
+} // namespace
+
+namespace tfinfo
+{
+inline namespace v2
+{
+
+TensorSignatures load(const char *path)
+{
+ std::ifstream stream(path, std::ios::in | std::ios::binary);
+
+ return load(&stream, path);
+}
+
+TensorSignatures load(std::istream *stream, const char *path_for_error_msg)
+{
+ tfinfo_v2_proto::InfoDef info_def;
+
+ if (!load_text(stream, info_def))
+ {
+ throw oops::UserExn("Cannot parse the info file", "path", path_for_error_msg);
+ }
+
+ TensorSignatures tensors;
+
+ convert(info_def, tensors, path_for_error_msg);
+
+ return tensors;
+}
+
+} // namespace v2
+} // namespace tfinfo
diff --git a/compiler/tfinfo-v2/src/TensorSignature.cpp b/compiler/tfinfo-v2/src/TensorSignature.cpp
new file mode 100644
index 000000000..3107f33db
--- /dev/null
+++ b/compiler/tfinfo-v2/src/TensorSignature.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tfinfo-v2/TensorSignature.h"