diff --git a/compiler/mio-tflite2121/CMakeLists.txt b/compiler/mio-tflite2121/CMakeLists.txt index 371118be83c..e7b308524e4 100644 --- a/compiler/mio-tflite2121/CMakeLists.txt +++ b/compiler/mio-tflite2121/CMakeLists.txt @@ -15,7 +15,8 @@ endif(NOT TensorFlowSource_FOUND) message(STATUS "Build mio-tflite2121: TRUE") message(STATUS "Build mio-tflite2121: with ${TensorFlowSource_DIR}") -set(SCHEMA_FILE "${TensorFlowSource_DIR}/tensorflow/lite/schema/schema.fbs") +#set(SCHEMA_FILE "${TensorFlowSource_DIR}/tensorflow/lite/schema/schema.fbs") +set(SCHEMA_FILE "${NNAS_PROJECT_SOURCE_DIR}/res/TensorFlowLiteSchema/2.16.1/schema.fbs") # NOTE Use copy of schema.fbs as to provide unified way for circle also add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/schema.fbs" diff --git a/compiler/tflchef/core/src/ModelChef.cpp b/compiler/tflchef/core/src/ModelChef.cpp index 4025ed2306f..5a7d1961a1d 100644 --- a/compiler/tflchef/core/src/ModelChef.cpp +++ b/compiler/tflchef/core/src/ModelChef.cpp @@ -47,38 +47,6 @@ using namespace souschef; namespace { -class GeneratedModelImpl final : public tflchef::GeneratedModel::Impl -{ -public: - GeneratedModelImpl(std::unique_ptr &&builder) - : _builder{std::move(builder)} - { - // DO NOTHING - } - -public: - const char *base(void) const override - { - // Return the base address of generated flatbuffer model - return reinterpret_cast(_builder->GetBufferPointer()); - } - -public: - size_t size(void) const override - { - // Return the size of generated flatbuffer model - return _builder->GetSize(); - } - -private: - std::unique_ptr _builder; -}; - -} // namespace - -namespace -{ - struct DataChefRegistry final : public Registry { }; @@ -209,17 +177,66 @@ std::set gather_customcode_set(const ::tflchef::ModelRecipe &model_ namespace { -struct CookParams +class ModelChef { - std::vector> &buffer_vec; - std::vector> &code_vec; - std::vector> &subgraph_vec; - std::unique_ptr &flatbuffer_builder; - std::map &builtin_code_map; - std::vector &custom_code_vec; - std::string noname; +public: + ModelChef() = default; + +public: + void init(void); + void cook(const ::tflchef::ModelRecipe &model_recipe); + +private: + void prepare_initial_buffer(void); + void gather_operator_codes(const ::tflchef::ModelRecipe &model_recipe); + void gather_signature_defs(const ::tflchef::ModelRecipe &model_recipe); + + template void cook_operands(const T &graph); + + template + void cook_operations(const T &graph, std::map &symbol_table); + + template + void cook_graph(const T &graph, std::map &symbol_table); + +public: + const char *GetBufferPointer(void) const + { + return reinterpret_cast(_flatbuffer_builder->GetBufferPointer()); + } + + size_t GetSize(void) const { return _flatbuffer_builder->GetSize(); } + +private: + std::unique_ptr _flatbuffer_builder; + + std::vector> _signdef_vec; + std::vector> _buffer_vec; + std::vector> _code_vec; + std::vector> _subgraph_vec; + std::map _builtin_code_map; + std::vector _custom_code_vec; + // _symbol_tables stores symbol_table of each sub graph + // this is used to find tensor ID(index) with tensor name + std::vector> _symbol_tables; + + // per graph that needs clear afer graph is processed + // Operand-related + std::vector> _tensor_vec; + // Operation-related + std::vector> _operator_vec; + + std::string _graph_name; + + bool _ext_offset; }; +void ModelChef::init(void) +{ + _flatbuffer_builder = + std::unique_ptr(new flatbuffers::FlatBufferBuilder(1024)); +} + std::vector> make_dim_metadata_vec(flatbuffers::FlatBufferBuilder *flatbuffer_builder, int32_t dims_count, const std::vector &traversal_order_vec, @@ -255,59 +272,24 @@ make_dim_metadata_vec(flatbuffers::FlatBufferBuilder *flatbuffer_builder, int32_ return dim_metadata_vec; } -template std::map cook_graph(const T &graph, CookParams &cp) +template void ModelChef::cook_operands(const T &graph) { - LOGGER(l); - - std::vector> &buffer_vec = cp.buffer_vec; - std::vector> &code_vec = cp.code_vec; - std::vector> &subgraph_vec = cp.subgraph_vec; - std::unique_ptr &flatbuffer_builder = cp.flatbuffer_builder; - std::map &builtin_code_map = cp.builtin_code_map; - std::vector &custom_code_vec = cp.custom_code_vec; - - // Operand-related - std::vector> tensor_vec; - - // Operation-related - std::vector> operator_vec; - - // default name for graph - std::string graph_name = cp.noname; - if (graph.has_name()) - graph_name = graph.name(); - - // Tensor Name -> Tensor ID mapping (per Graph) - std::map symbol_table; - - auto lookup = [&symbol_table, &graph_name](const std::string &name) { - if (symbol_table.find(name) != symbol_table.end()) - return symbol_table.at(name); - else if (name == "") - return -1; // -1 in TFLite means that optional input tensor is empty. - else - { - std::string msg = "tflchef : input not found in " + graph_name + " graph"; - throw std::runtime_error(msg.c_str()); - } - }; - - int32_t buffer_start = buffer_vec.size(); + int32_t buffer_start = _buffer_vec.size(); int32_t buffer_index = 0; // Create buffer(s) 1~n(I) for input(s) const auto size_input = graph.input_size(); for (int ci = 0; ci < size_input; ++ci) { - tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; - buffer_vec.emplace_back(buffer_builder.Finish()); + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + _buffer_vec.emplace_back(buffer_builder.Finish()); } // Create buffer(s) n(I)+1~n(I)+n(O) for output(s) const auto size_output = graph.output_size(); for (int co = 0; co < size_output; ++co) { - tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; - buffer_vec.emplace_back(buffer_builder.Finish()); + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + _buffer_vec.emplace_back(buffer_builder.Finish()); } auto input_names = as_dataset(graph.input()).vectorize(); @@ -326,10 +308,10 @@ template std::map cook_graph(const T &graph, if (operand.has_shape()) { dims = as_dims(operand.shape()); - shape = flatbuffer_builder->CreateVector(dims); + shape = _flatbuffer_builder->CreateVector(dims); } - auto name = flatbuffer_builder->CreateString(operand.name()); + auto name = _flatbuffer_builder->CreateString(operand.name()); buffer_index = 0; @@ -380,31 +362,31 @@ template std::map cook_graph(const T &graph, sparse_uint8.emplace_back(arr[b]); } } - auto data = flatbuffer_builder->CreateVector(sparse_uint8); + auto data = _flatbuffer_builder->CreateVector(sparse_uint8); // Create Buffer - tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; buffer_builder.add_data(data); auto buffer = buffer_builder.Finish(); // Update Buffer Index & Vector - buffer_index = buffer_vec.size(); - buffer_vec.emplace_back(buffer); + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); // save SparsityParameters - auto traversal_order = flatbuffer_builder->CreateVector(traversal_order_vec); + auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); // Create block map std::vector block_map_vec{}; - auto block_map = flatbuffer_builder->CreateVector(block_map_vec); + auto block_map = _flatbuffer_builder->CreateVector(block_map_vec); // Create dimension metadata const auto &dim_metadata_src = converter.GetDimMetadata(); auto dim_metadata_vec = - make_dim_metadata_vec(flatbuffer_builder.get(), dims_count, traversal_order_vec, + make_dim_metadata_vec(_flatbuffer_builder.get(), dims_count, traversal_order_vec, format_vec, dim_metadata_src); - auto dim_metadata = flatbuffer_builder->CreateVector(dim_metadata_vec); - sparsity_index = tflite::CreateSparsityParameters(*flatbuffer_builder, traversal_order, + auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec); + sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order, block_map, dim_metadata); } else if (operand.type() == tflchef::FLOAT16) @@ -423,31 +405,31 @@ template std::map cook_graph(const T &graph, sparse_uint8.emplace_back(arr[b]); } } - auto data = flatbuffer_builder->CreateVector(sparse_uint8); + auto data = _flatbuffer_builder->CreateVector(sparse_uint8); // Create Buffer - tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; buffer_builder.add_data(data); auto buffer = buffer_builder.Finish(); // Update Buffer Index & Vector - buffer_index = buffer_vec.size(); - buffer_vec.emplace_back(buffer); + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); // save SparsityParameters - auto traversal_order = flatbuffer_builder->CreateVector(traversal_order_vec); + auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); // Create block map std::vector block_map_vec{}; - auto block_map = flatbuffer_builder->CreateVector(block_map_vec); + auto block_map = _flatbuffer_builder->CreateVector(block_map_vec); // Create dimension metadata const auto &dim_metadata_src = converter.GetDimMetadata(); auto dim_metadata_vec = - make_dim_metadata_vec(flatbuffer_builder.get(), dims_count, traversal_order_vec, + make_dim_metadata_vec(_flatbuffer_builder.get(), dims_count, traversal_order_vec, format_vec, dim_metadata_src); - auto dim_metadata = flatbuffer_builder->CreateVector(dim_metadata_vec); - sparsity_index = tflite::CreateSparsityParameters(*flatbuffer_builder, traversal_order, + auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec); + sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order, block_map, dim_metadata); } else @@ -472,16 +454,18 @@ template std::map cook_graph(const T &graph, data_vec = data_packed; } - auto data = flatbuffer_builder->CreateVector(data_vec); + // TODO check use_buffer_offset + + auto data = _flatbuffer_builder->CreateVector(data_vec); // Create Buffer - tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; buffer_builder.add_data(data); auto buffer = buffer_builder.Finish(); // Update Buffer Index & Vector - buffer_index = buffer_vec.size(); - buffer_vec.emplace_back(buffer); + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); } } else @@ -511,10 +495,10 @@ template std::map cook_graph(const T &graph, if (buffer_index == 0) { // we couldn't find the buffer; create an empty buffer for this tensor - buffer_index = buffer_vec.size(); + buffer_index = _buffer_vec.size(); - tflite::BufferBuilder buffer_builder{*flatbuffer_builder}; - buffer_vec.emplace_back(buffer_builder.Finish()); + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + _buffer_vec.emplace_back(buffer_builder.Finish()); } } assert(buffer_index != 0); @@ -542,13 +526,13 @@ template std::map cook_graph(const T &graph, for (uint32_t i = 0; i < quant.zero_point_size(); ++i) quant_zero_point_vec.at(i) = quant.zero_point(i); - auto quant_max = flatbuffer_builder->CreateVector(quant_max_vec); - auto quant_min = flatbuffer_builder->CreateVector(quant_min_vec); - auto quant_scale = flatbuffer_builder->CreateVector(quant_scale_vec); - auto quant_zero_point = flatbuffer_builder->CreateVector(quant_zero_point_vec); + auto quant_max = _flatbuffer_builder->CreateVector(quant_max_vec); + auto quant_min = _flatbuffer_builder->CreateVector(quant_min_vec); + auto quant_scale = _flatbuffer_builder->CreateVector(quant_scale_vec); + auto quant_zero_point = _flatbuffer_builder->CreateVector(quant_zero_point_vec); // Create QuantizationParameters - tflite::QuantizationParametersBuilder quant_builder{*flatbuffer_builder}; + tflite::QuantizationParametersBuilder quant_builder{*_flatbuffer_builder}; quant_builder.add_max(quant_max); quant_builder.add_min(quant_min); quant_builder.add_scale(quant_scale); @@ -566,12 +550,12 @@ template std::map cook_graph(const T &graph, // Create traversal order std::vector traversal_order_vec{sparsity.traversal_order().dim().begin(), sparsity.traversal_order().dim().end()}; - auto traversal_order = flatbuffer_builder->CreateVector(traversal_order_vec); + auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); // Create block map std::vector block_map_vec{sparsity.block_map().dim().begin(), sparsity.block_map().dim().end()}; - auto block_map = flatbuffer_builder->CreateVector(block_map_vec); + auto block_map = _flatbuffer_builder->CreateVector(block_map_vec); // Create dimension metadata std::vector> dim_metadata_vec; @@ -580,13 +564,13 @@ template std::map cook_graph(const T &graph, { // Create array segments auto tflite_array_segments = - as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_segments()); + as_tflite_sparse_index_vec(*_flatbuffer_builder, dm.array_segments()); // Create array indices auto tflite_array_indices = - as_tflite_sparse_index_vec(*flatbuffer_builder, dm.array_indices()); + as_tflite_sparse_index_vec(*_flatbuffer_builder, dm.array_indices()); - auto tflite_dim_metadata_builder = tflite::DimensionMetadataBuilder{*flatbuffer_builder}; + auto tflite_dim_metadata_builder = tflite::DimensionMetadataBuilder{*_flatbuffer_builder}; tflite_dim_metadata_builder.add_format(as_tflite_dimensiontype(dm.format())); tflite_dim_metadata_builder.add_dense_size(dm.dense_size()); tflite_dim_metadata_builder.add_array_segments(tflite_array_segments); @@ -598,9 +582,9 @@ template std::map cook_graph(const T &graph, auto tflite_dim_metadata = tflite_dim_metadata_builder.Finish(); dim_metadata_vec.emplace_back(tflite_dim_metadata); } - auto dim_metadata = flatbuffer_builder->CreateVector(dim_metadata_vec); + auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec); - sparsity_index = tflite::CreateSparsityParameters(*flatbuffer_builder, traversal_order, + sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order, block_map, dim_metadata); } @@ -608,11 +592,11 @@ template std::map cook_graph(const T &graph, if (operand.has_shape_signature()) { auto signature = as_dims(operand.shape_signature()); - shape_signature = flatbuffer_builder->CreateVector(signature); + shape_signature = _flatbuffer_builder->CreateVector(signature); } // Create Tensor - tflite::TensorBuilder tensor_builder{*flatbuffer_builder}; + tflite::TensorBuilder tensor_builder{*_flatbuffer_builder}; tensor_builder.add_shape(shape); tensor_builder.add_type(as_tflite_tensortype(operand.type())); @@ -626,16 +610,24 @@ template std::map cook_graph(const T &graph, tensor_builder.add_shape_signature(shape_signature); // Append! - tensor_vec.emplace_back(tensor_builder.Finish()); - - // Update Tensor Name -> Tensor Index Map - int32_t tensor_index = symbol_table.size(); - const auto &tensor_name = operand.name(); - - INFO(l) << "Symbol [" << tensor_name << "] = Tensor " << tensor_index << std::endl; - - symbol_table[tensor_name] = tensor_index; + _tensor_vec.emplace_back(tensor_builder.Finish()); } +} + +template +void ModelChef::cook_operations(const T &graph, std::map &symbol_table) +{ + auto lookup = [&](const std::string &name) { + if (symbol_table.find(name) != symbol_table.end()) + return symbol_table.at(name); + else if (name == "") + return -1; // -1 in TFLite means that optional input tensor is empty. + else + { + std::string msg = "tflchef : input not found in " + _graph_name + " graph"; + throw std::runtime_error(msg.c_str()); + } + }; // Create Operator for (const auto &operation : graph.operation()) @@ -650,39 +642,39 @@ template std::map cook_graph(const T &graph, // Create 'inputs' std::vector input_vec = as_dataset(operation.input()).map(lookup).vectorize(); - auto inputs = flatbuffer_builder->CreateVector(input_vec); + auto inputs = _flatbuffer_builder->CreateVector(input_vec); // Create 'outputs' std::vector output_vec = as_dataset(operation.output()).map(lookup).vectorize(); - auto outputs = flatbuffer_builder->CreateVector(output_vec); + auto outputs = _flatbuffer_builder->CreateVector(output_vec); // Create Option - auto options = op_chef->value(*flatbuffer_builder); + auto options = op_chef->value(*_flatbuffer_builder); // Create Custom option - auto circle_custom_options = op_chef->custom_value(*flatbuffer_builder); + auto circle_custom_options = op_chef->custom_value(*_flatbuffer_builder); // Create Operator - tflite::OperatorBuilder op_builder{*flatbuffer_builder}; + tflite::OperatorBuilder op_builder{*_flatbuffer_builder}; // Note that opcode_index is an index into the operator_codes vector. // operator_codes consists of buildtin_code and custom_code, which is inserted sequentially. uint32_t opcode_index = 0; - auto op_it = builtin_code_map.find(op_chef->code()); + auto op_it = _builtin_code_map.find(op_chef->code()); // builtin operator - if (op_it != builtin_code_map.end()) + if (op_it != _builtin_code_map.end()) { - opcode_index = std::distance(builtin_code_map.begin(), op_it); + opcode_index = std::distance(_builtin_code_map.begin(), op_it); } // custom operator else { assert(not operation.custom_code().empty()); auto custom_code = operation.custom_code(); - auto op_it = std::find(custom_code_vec.begin(), custom_code_vec.end(), custom_code); - assert(op_it != custom_code_vec.end()); - opcode_index = builtin_code_map.size(); - opcode_index += std::distance(custom_code_vec.begin(), op_it); + auto op_it = std::find(_custom_code_vec.begin(), _custom_code_vec.end(), custom_code); + assert(op_it != _custom_code_vec.end()); + opcode_index = _builtin_code_map.size(); + opcode_index += std::distance(_custom_code_vec.begin(), op_it); } op_builder.add_opcode_index(opcode_index); @@ -693,56 +685,737 @@ template std::map cook_graph(const T &graph, op_builder.add_custom_options(circle_custom_options); op_builder.add_custom_options_format(tflite::CustomOptionsFormat_FLEXBUFFERS); // Append Operator - operator_vec.emplace_back(op_builder.Finish()); + _operator_vec.emplace_back(op_builder.Finish()); } +} - // Create network input/output vector - std::vector input_vec = as_dataset(graph.input()).map(lookup).vectorize(); - std::vector output_vec = as_dataset(graph.output()).map(lookup).vectorize(); +template +void ModelChef::cook_graph(const T &graph, std::map &symbol_table) +{ + LOGGER(l); - // Create "SubGraph" arguments - auto tensors = flatbuffer_builder->CreateVector(tensor_vec); - auto inputs = flatbuffer_builder->CreateVector(input_vec); - auto outputs = flatbuffer_builder->CreateVector(output_vec); - auto operators = flatbuffer_builder->CreateVector(operator_vec); - auto name = flatbuffer_builder->CreateString(graph_name); + // default name for graph + if (graph.has_name()) + _graph_name = graph.name(); + + auto lookup = [&](const std::string &name) { + if (symbol_table.find(name) != symbol_table.end()) + return symbol_table.at(name); + else if (name == "") + return -1; // -1 in TFLite means that optional input tensor is empty. + else + { + std::string msg = "tflchef : input not found in " + _graph_name + " graph"; + throw std::runtime_error(msg.c_str()); + } + }; - tflite::SubGraphBuilder subgraph_builder{*flatbuffer_builder}; +#if 0 + int32_t buffer_start = _buffer_vec.size(); + int32_t buffer_index = 0; - subgraph_builder.add_tensors(tensors); - subgraph_builder.add_inputs(inputs); - subgraph_builder.add_outputs(outputs); - subgraph_builder.add_operators(operators); - subgraph_builder.add_name(name); + // Create buffer(s) 1~n(I) for input(s) + const auto size_input = graph.input_size(); + for (int ci = 0; ci < size_input; ++ci) + { + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + _buffer_vec.emplace_back(buffer_builder.Finish()); + } + // Create buffer(s) n(I)+1~n(I)+n(O) for output(s) + const auto size_output = graph.output_size(); + for (int co = 0; co < size_output; ++co) + { + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + _buffer_vec.emplace_back(buffer_builder.Finish()); + } - subgraph_vec.emplace_back(subgraph_builder.Finish()); + auto input_names = as_dataset(graph.input()).vectorize(); + auto output_names = as_dataset(graph.output()).vectorize(); - return symbol_table; -} + for (const auto &operand : graph.operand()) + { + assert(operand.has_name()); -} // namespace + assert(operand.has_type()); -namespace tflchef -{ + flatbuffers::Offset sparsity_index; -/** - * @brief Generate a (in-memory) TensorFlow Lite model from a given model recipe - */ -GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) -{ -// Initialize Op Chef Registry -#define OP_CHEF(NAME, FACTORY_CLASS) \ - op_chef_registry().add(#NAME, std::unique_ptr(new FACTORY_CLASS())); -#include "OpChef.def" -#undef OP_CHEF + flatbuffers::Offset> shape; + std::vector dims; + if (operand.has_shape()) + { + dims = as_dims(operand.shape()); + shape = _flatbuffer_builder->CreateVector(dims); + } -// Initialize Data Chef Registry -#define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \ - data_chef_registry(::tflchef::TYPE) \ - .add(#NAME, std::unique_ptr(new FACTORY_CLASS())); -#include "DataChef.def" -#undef DATA_CHEF + auto name = _flatbuffer_builder->CreateString(operand.name()); + + buffer_index = 0; + + // Create Buffer if filler is specified + if (operand.has_filler()) + { + const auto &filler = operand.filler(); + + assert(filler.has_tag()); + + auto args = ranged_arguments(filler.arg().begin(), filler.arg().end()); + auto chef = data_chef_registry(operand.type()).lookup(filler.tag()).create(args); + + assert(chef != nullptr); + + // Create Data + int32_t count = (element_count(dims) > 0) ? element_count(dims) : filler.arg_size(); + auto data_vec = chef->generate(count); + + if (operand.has_make_sparse() && operand.make_sparse()) + { + assert(not operand.has_sparsity()); + assert(operand.has_shape()); + assert(operand.type() != tflchef::TensorType::INT4); + + const int32_t dims_count = dims.size(); + std::vector traversal_order_vec; + std::vector format_vec; + for (int32_t o = 0; o < dims_count; ++o) + traversal_order_vec.push_back(o); + for (int32_t o = 0; o < dims_count - 1; ++o) + format_vec.push_back(sparsity::kTfLiteDimDense); + format_vec.push_back(sparsity::kTfLiteDimSparseCSR); + + if (operand.type() == tflchef::FLOAT32) + { + ::sparsity::FormatConverter converter(dims, traversal_order_vec, format_vec); + converter.DenseToSparse(reinterpret_cast(data_vec.data())); + const auto &sparse_data = converter.GetData(); + + std::vector sparse_uint8; + for (int c = 0; c < sparse_data.size(); ++c) + { + const float value = sparse_data.at(c); + const uint8_t *arr = reinterpret_cast(&value); + for (uint32_t b = 0; b < sizeof(float); ++b) + { + sparse_uint8.emplace_back(arr[b]); + } + } + auto data = _flatbuffer_builder->CreateVector(sparse_uint8); + + // Create Buffer + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); + + // save SparsityParameters + auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); + + // Create block map + std::vector block_map_vec{}; + auto block_map = _flatbuffer_builder->CreateVector(block_map_vec); + + // Create dimension metadata + const auto &dim_metadata_src = converter.GetDimMetadata(); + auto dim_metadata_vec = + make_dim_metadata_vec(_flatbuffer_builder.get(), dims_count, traversal_order_vec, + format_vec, dim_metadata_src); + auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec); + sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order, + block_map, dim_metadata); + } + else if (operand.type() == tflchef::FLOAT16) + { + ::sparsity::FormatConverter converter(dims, traversal_order_vec, format_vec); + converter.DenseToSparse(reinterpret_cast(data_vec.data())); + const auto &sparse_data = converter.GetData(); + + std::vector sparse_uint8; + for (int c = 0; c < sparse_data.size(); ++c) + { + const uint16_t value = sparse_data.at(c); + const uint8_t *arr = reinterpret_cast(&value); + for (uint32_t b = 0; b < sizeof(uint16_t); ++b) + { + sparse_uint8.emplace_back(arr[b]); + } + } + auto data = _flatbuffer_builder->CreateVector(sparse_uint8); + + // Create Buffer + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); + + // save SparsityParameters + auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); + + // Create block map + std::vector block_map_vec{}; + auto block_map = _flatbuffer_builder->CreateVector(block_map_vec); + + // Create dimension metadata + const auto &dim_metadata_src = converter.GetDimMetadata(); + auto dim_metadata_vec = + make_dim_metadata_vec(_flatbuffer_builder.get(), dims_count, traversal_order_vec, + format_vec, dim_metadata_src); + auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec); + sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order, + block_map, dim_metadata); + } + else + { + throw std::runtime_error{"NYI: unsupported operand type"}; + } + } + else + { + // pack for INT4 and replace data_vec + if (operand.type() == tflchef::TensorType::INT4) + { + uint32_t packed = (count + 1) / 2; + std::vector data_packed(packed); + for (uint32_t idx = 0; idx < packed; ++idx) + { + uint32_t sidx = idx * 2; + data_packed[idx] = data_vec[sidx++] & 0x0f; + if (sidx < count) + data_packed[idx] |= data_vec[sidx] << 4; + } + data_vec = data_packed; + } + + // TODO check use_buffer_offset + + auto data = _flatbuffer_builder->CreateVector(data_vec); + + // Create Buffer + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + buffer_builder.add_data(data); + auto buffer = buffer_builder.Finish(); + + // Update Buffer Index & Vector + buffer_index = _buffer_vec.size(); + _buffer_vec.emplace_back(buffer); + } + } + else + { + // if this is input or output, assign to that buffer_index + int idx = 0; + for (auto it = input_names.begin(); it != input_names.end(); ++it, ++idx) + { + if (*it == operand.name()) + { + buffer_index = buffer_start + idx; + break; + } + } + if (buffer_index == 0) + { + idx = 0; + for (auto it = output_names.begin(); it != output_names.end(); ++it, ++idx) + { + if (*it == operand.name()) + { + buffer_index = buffer_start + size_input + idx; + break; + } + } + } + if (buffer_index == 0) + { + // we couldn't find the buffer; create an empty buffer for this tensor + buffer_index = _buffer_vec.size(); + + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + _buffer_vec.emplace_back(buffer_builder.Finish()); + } + } + assert(buffer_index != 0); + + flatbuffers::Offset quant_index; + + // Create QuantizationParameters if quant is specified + if (operand.has_quant()) + { + const auto &quant = operand.quant(); + + // Create each parameters + // NOTE if some parameters are not given, those will be set to default value + std::vector quant_max_vec(quant.max_size()); + std::vector quant_min_vec(quant.min_size()); + std::vector quant_scale_vec(quant.scale_size()); + std::vector quant_zero_point_vec(quant.zero_point_size()); + + for (uint32_t i = 0; i < quant.max_size(); ++i) + quant_max_vec.at(i) = quant.max(i); + for (uint32_t i = 0; i < quant.min_size(); ++i) + quant_min_vec.at(i) = quant.min(i); + for (uint32_t i = 0; i < quant.scale_size(); ++i) + quant_scale_vec.at(i) = quant.scale(i); + for (uint32_t i = 0; i < quant.zero_point_size(); ++i) + quant_zero_point_vec.at(i) = quant.zero_point(i); + + auto quant_max = _flatbuffer_builder->CreateVector(quant_max_vec); + auto quant_min = _flatbuffer_builder->CreateVector(quant_min_vec); + auto quant_scale = _flatbuffer_builder->CreateVector(quant_scale_vec); + auto quant_zero_point = _flatbuffer_builder->CreateVector(quant_zero_point_vec); + + // Create QuantizationParameters + tflite::QuantizationParametersBuilder quant_builder{*_flatbuffer_builder}; + quant_builder.add_max(quant_max); + quant_builder.add_min(quant_min); + quant_builder.add_scale(quant_scale); + quant_builder.add_zero_point(quant_zero_point); + quant_builder.add_quantized_dimension(quant.quantized_dimension()); + + // Update QuantizationParameters Index + quant_index = quant_builder.Finish(); + } + + if (operand.has_sparsity()) + { + const auto &sparsity = operand.sparsity(); + + // Create traversal order + std::vector traversal_order_vec{sparsity.traversal_order().dim().begin(), + sparsity.traversal_order().dim().end()}; + auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec); + + // Create block map + std::vector block_map_vec{sparsity.block_map().dim().begin(), + sparsity.block_map().dim().end()}; + auto block_map = _flatbuffer_builder->CreateVector(block_map_vec); + + // Create dimension metadata + std::vector> dim_metadata_vec; + auto recipe_dim_metadata = sparsity.dim_metadata(); + for (const auto &dm : recipe_dim_metadata) + { + // Create array segments + auto tflite_array_segments = + as_tflite_sparse_index_vec(*_flatbuffer_builder, dm.array_segments()); + + // Create array indices + auto tflite_array_indices = + as_tflite_sparse_index_vec(*_flatbuffer_builder, dm.array_indices()); + + auto tflite_dim_metadata_builder = tflite::DimensionMetadataBuilder{*_flatbuffer_builder}; + tflite_dim_metadata_builder.add_format(as_tflite_dimensiontype(dm.format())); + tflite_dim_metadata_builder.add_dense_size(dm.dense_size()); + tflite_dim_metadata_builder.add_array_segments(tflite_array_segments); + tflite_dim_metadata_builder.add_array_segments_type( + as_tflite_sparse_idx_vec_type(dm.array_segments().type())); + tflite_dim_metadata_builder.add_array_indices(tflite_array_indices); + tflite_dim_metadata_builder.add_array_indices_type( + as_tflite_sparse_idx_vec_type(dm.array_indices().type())); + auto tflite_dim_metadata = tflite_dim_metadata_builder.Finish(); + dim_metadata_vec.emplace_back(tflite_dim_metadata); + } + auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec); + + sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order, + block_map, dim_metadata); + } + + flatbuffers::Offset> shape_signature; + if (operand.has_shape_signature()) + { + auto signature = as_dims(operand.shape_signature()); + shape_signature = _flatbuffer_builder->CreateVector(signature); + } + + // Create Tensor + tflite::TensorBuilder tensor_builder{*_flatbuffer_builder}; + + tensor_builder.add_shape(shape); + tensor_builder.add_type(as_tflite_tensortype(operand.type())); + tensor_builder.add_buffer(buffer_index); + tensor_builder.add_name(name); + tensor_builder.add_is_variable(operand.is_variable()); + if (operand.has_quant()) + tensor_builder.add_quantization(quant_index); + tensor_builder.add_sparsity(sparsity_index); + if (operand.has_shape_signature()) + tensor_builder.add_shape_signature(shape_signature); + + // Append! + tensor_vec.emplace_back(tensor_builder.Finish()); + + // Update Tensor Name -> Tensor Index Map + int32_t tensor_index = symbol_table.size(); + const auto &tensor_name = operand.name(); + + INFO(l) << "Symbol [" << tensor_name << "] = Tensor " << tensor_index << std::endl; + + symbol_table[tensor_name] = tensor_index; + } +#endif + + cook_operands(graph); + + for (const auto &operand : graph.operand()) + { + // Update Tensor Name -> Tensor Index Map + int32_t tensor_index = symbol_table.size(); + const auto &tensor_name = operand.name(); + + INFO(l) << "Symbol [" << tensor_name << "] = Tensor " << tensor_index << std::endl; + + symbol_table[tensor_name] = tensor_index; + } + +#if 0 + // Create Operator + for (const auto &operation : graph.operation()) + { + assert(operation.has_type()); + + std::string op_type = operation.type(); + if (not operation.custom_code().empty()) + op_type = operation.custom_code(); + + auto op_chef = op_chef_registry().lookup(op_type).create(&operation); + + // Create 'inputs' + std::vector input_vec = as_dataset(operation.input()).map(lookup).vectorize(); + auto inputs = _flatbuffer_builder->CreateVector(input_vec); + + // Create 'outputs' + std::vector output_vec = as_dataset(operation.output()).map(lookup).vectorize(); + auto outputs = _flatbuffer_builder->CreateVector(output_vec); + + // Create Option + auto options = op_chef->value(*_flatbuffer_builder); + + // Create Custom option + auto circle_custom_options = op_chef->custom_value(*_flatbuffer_builder); + + // Create Operator + tflite::OperatorBuilder op_builder{*_flatbuffer_builder}; + + // Note that opcode_index is an index into the operator_codes vector. + // operator_codes consists of buildtin_code and custom_code, which is inserted sequentially. + uint32_t opcode_index = 0; + auto op_it = _builtin_code_map.find(op_chef->code()); + // builtin operator + if (op_it != _builtin_code_map.end()) + { + opcode_index = std::distance(_builtin_code_map.begin(), op_it); + } + // custom operator + else + { + assert(not operation.custom_code().empty()); + auto custom_code = operation.custom_code(); + auto op_it = std::find(_custom_code_vec.begin(), _custom_code_vec.end(), custom_code); + assert(op_it != _custom_code_vec.end()); + opcode_index = _builtin_code_map.size(); + opcode_index += std::distance(_custom_code_vec.begin(), op_it); + } + + op_builder.add_opcode_index(opcode_index); + op_builder.add_inputs(inputs); + op_builder.add_outputs(outputs); + op_builder.add_builtin_options_type(op_chef->type()); + op_builder.add_builtin_options(options); + op_builder.add_custom_options(circle_custom_options); + op_builder.add_custom_options_format(tflite::CustomOptionsFormat_FLEXBUFFERS); + // Append Operator + operator_vec.emplace_back(op_builder.Finish()); + } +#endif + + // Create network input/output vector + std::vector input_vec = as_dataset(graph.input()).map(lookup).vectorize(); + std::vector output_vec = as_dataset(graph.output()).map(lookup).vectorize(); + + cook_operations(graph, symbol_table); + + // Create "SubGraph" arguments + auto tensors = _flatbuffer_builder->CreateVector(_tensor_vec); + auto inputs = _flatbuffer_builder->CreateVector(input_vec); + auto outputs = _flatbuffer_builder->CreateVector(output_vec); + auto operators = _flatbuffer_builder->CreateVector(_operator_vec); + auto name = _flatbuffer_builder->CreateString(_graph_name); + + tflite::SubGraphBuilder subgraph_builder{*_flatbuffer_builder}; + + subgraph_builder.add_tensors(tensors); + subgraph_builder.add_inputs(inputs); + subgraph_builder.add_outputs(outputs); + subgraph_builder.add_operators(operators); + subgraph_builder.add_name(name); + + _subgraph_vec.emplace_back(subgraph_builder.Finish()); + + // clear _tensor_vec, _operator_vec as these per graph and reused + _tensor_vec.clear(); + _operator_vec.clear(); +} + +void ModelChef::gather_operator_codes(const ::tflchef::ModelRecipe &model_recipe) +{ + // Create OperatorCode with Builtin Operator + _builtin_code_map = gather_builtincode_map(model_recipe); + for (auto const &opcode : _builtin_code_map) + { + tflite::OperatorCodeBuilder code_builder{*_flatbuffer_builder}; + // 127 is BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES + // This is the way to handle deprecated builtin code + // See + // https://github.com/tensorflow/tensorflow/blob/a0afe8f9218be5eb9ed5dffc2dff652996da8c28/tensorflow/lite/schema/schema.fbs#L1061-L1077 + if (opcode.first < 127) + { + code_builder.add_deprecated_builtin_code(opcode.first); + } + else + { + code_builder.add_deprecated_builtin_code( + ::tflite::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES); + } + code_builder.add_version(opcode.second); + code_builder.add_builtin_code(opcode.first); + auto code = code_builder.Finish(); + // Update OperatorCode vector + _code_vec.emplace_back(code); + } + + // Create OperatorCode with Custom Operator + { + std::set custom_code_set = gather_customcode_set(model_recipe); + std::vector custom_code_vec{custom_code_set.begin(), custom_code_set.end()}; + _custom_code_vec = custom_code_vec; + } + + for (auto opcode : _custom_code_vec) + { + auto custom_code = _flatbuffer_builder->CreateString(opcode); + tflite::OperatorCodeBuilder code_builder{*_flatbuffer_builder}; + code_builder.add_deprecated_builtin_code(tflite::BuiltinOperator_CUSTOM); + code_builder.add_custom_code(custom_code); + code_builder.add_builtin_code(tflite::BuiltinOperator_CUSTOM); + auto code = code_builder.Finish(); + // Update OperatorCode vector + _code_vec.emplace_back(code); + } +} + +void ModelChef::prepare_initial_buffer(void) +{ + // Create an Empty Buffer + // + // Buffer 0 SHOULD be an empty buffer in TensorFlow Lite model file + // (Please refer to the comment for Tensor.buffer field in schema) + tflite::BufferBuilder buffer_builder{*_flatbuffer_builder}; + _buffer_vec.emplace_back(buffer_builder.Finish()); +} + +void ModelChef::gather_signature_defs(const ::tflchef::ModelRecipe &model_recipe) +{ + for (int s = 0; s < model_recipe.signature_def_size(); ++s) + { + // load from recipe + const auto &rec_signature_def = model_recipe.signature_def(s); + + std::vector> tensormap_inputs; + std::vector> tensormap_outputs; + + // which subgraph index to cook + auto subgraph_index = 0; + if (rec_signature_def.has_subgraph_index()) + { + subgraph_index = rec_signature_def.subgraph_index(); + } + assert(subgraph_index < _symbol_tables.size()); + auto &symbol_table = _symbol_tables[subgraph_index]; + + // cook for inputs + for (int si = 0; si < rec_signature_def.inputs_size(); ++si) + { + // recipe for input TensorMap + auto rec_tm_input = rec_signature_def.inputs(si); + auto name = _flatbuffer_builder->CreateString(rec_tm_input.name()); + uint32_t tensor_index = 0; + // either tensor or tensor_index should exist + assert(rec_tm_input.has_tensor() || rec_tm_input.has_tensor_index()); + if (rec_tm_input.has_tensor()) + { + // we can get tensor_index from symbol_table + auto tensor = rec_tm_input.tensor(); + tensor_index = symbol_table[tensor]; + } + else + { + // or we can use tensor_index itself + tensor_index = rec_tm_input.tensor_index(); + } + + ::tflite::TensorMapBuilder tensormap_builder{*_flatbuffer_builder}; + tensormap_builder.add_name(name); + tensormap_builder.add_tensor_index(tensor_index); + tensormap_inputs.push_back(tensormap_builder.Finish()); + } + // cook for outputs, same as inputs + for (int so = 0; so < rec_signature_def.outputs_size(); ++so) + { + auto rec_tm_output = rec_signature_def.outputs(so); + auto name = _flatbuffer_builder->CreateString(rec_tm_output.name()); + uint32_t tensor_index = 0; + assert(rec_tm_output.has_tensor() || rec_tm_output.has_tensor_index()); + if (rec_tm_output.has_tensor()) + { + auto tensor = rec_tm_output.tensor(); + tensor_index = symbol_table[tensor]; + } + else + { + tensor_index = rec_tm_output.tensor_index(); + } + + ::tflite::TensorMapBuilder tensormap_builder{*_flatbuffer_builder}; + tensormap_builder.add_name(name); + tensormap_builder.add_tensor_index(tensor_index); + tensormap_outputs.push_back(tensormap_builder.Finish()); + } + + auto inputs = _flatbuffer_builder->CreateVector(tensormap_inputs); + auto outputs = _flatbuffer_builder->CreateVector(tensormap_outputs); + auto signature_key = _flatbuffer_builder->CreateString(rec_signature_def.signature_key()); + // TODO add validation for signature_key + + ::tflite::SignatureDefBuilder signature_def_builder{*_flatbuffer_builder}; + signature_def_builder.add_inputs(inputs); + signature_def_builder.add_outputs(outputs); + signature_def_builder.add_signature_key(signature_key); + signature_def_builder.add_subgraph_index(rec_signature_def.subgraph_index()); + + _signdef_vec.emplace_back(signature_def_builder.Finish()); + } +} + +void ModelChef::cook(const ::tflchef::ModelRecipe &model_recipe) +{ + // use Custom/Buffer offset + _ext_offset = model_recipe.has_ext_offset() ? model_recipe.ext_offset() : false; + + prepare_initial_buffer(); + + gather_operator_codes(model_recipe); + + // + // Create Main graph + // + + _graph_name = "main"; + // Tensor Name -> Tensor ID mapping (per Graph) + std::map symbol_table; + cook_graph<::tflchef::ModelRecipe>(model_recipe, symbol_table); + _symbol_tables.push_back(symbol_table); + + // + // Create subgraphs if exist + // + for (int g = 0; g < model_recipe.graph_size(); ++g) + { + const auto &graph = model_recipe.graph(g); + + std::ostringstream stringStream; + stringStream << "sub_" << (g + 1); + _graph_name = stringStream.str(); + symbol_table.clear(); + cook_graph<::tflchef::Graph>(graph, symbol_table); + _symbol_tables.push_back(symbol_table); + } + + gather_signature_defs(model_recipe); + + // Create "Model" arguments + auto buffers = _flatbuffer_builder->CreateVector(_buffer_vec); + auto signdefs = _flatbuffer_builder->CreateVector(_signdef_vec); + auto operator_codes = _flatbuffer_builder->CreateVector(_code_vec); + auto subgraphs = _flatbuffer_builder->CreateVector(_subgraph_vec); + auto description = _flatbuffer_builder->CreateString("Generated by tflchef"); + + // Create "Model" + tflite::ModelBuilder model_builder{*_flatbuffer_builder}; + + model_builder.add_version(3); + model_builder.add_operator_codes(operator_codes); + model_builder.add_signature_defs(signdefs); + model_builder.add_subgraphs(subgraphs); + model_builder.add_description(description); + model_builder.add_buffers(buffers); + + auto model = model_builder.Finish(); + + // Finalize + ::tflite::FinishModelBuffer(*_flatbuffer_builder, model); +} + +} // namespace + +namespace +{ + +class GeneratedModelImpl final : public tflchef::GeneratedModel::Impl +{ +public: + GeneratedModelImpl() + { + // DO NOTHING + } + +public: + const char *base(void) const override { return _mc.GetBufferPointer(); } + + size_t size(void) const override { return _mc.GetSize(); } + +public: + ModelChef &model_chef(void) { return _mc; } + +private: + ModelChef _mc; +}; + +} // namespace + +namespace tflchef +{ + +/** + * @brief Generate a (in-memory) TensorFlow Lite model from a given model recipe + */ +GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) +{ +// Initialize Op Chef Registry +#define OP_CHEF(NAME, FACTORY_CLASS) \ + op_chef_registry().add(#NAME, std::unique_ptr(new FACTORY_CLASS())); +#include "OpChef.def" +#undef OP_CHEF + +// Initialize Data Chef Registry +#define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \ + data_chef_registry(::tflchef::TYPE) \ + .add(#NAME, std::unique_ptr(new FACTORY_CLASS())); +#include "DataChef.def" +#undef DATA_CHEF + + std::unique_ptr gen_model(new GeneratedModelImpl()); + + ModelChef &mc = gen_model->model_chef(); + + mc.init(); + mc.cook(model_recipe); +#if 0 // // Create FlatBufferBuilder // @@ -818,10 +1491,9 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) // // Create Main graph // - CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, - builtin_code_map, custom_code_vec, "main"}; - auto table = cook_graph<::tflchef::ModelRecipe>(model_recipe, cp); + graph_name = "main"; + auto table = cook_graph<::tflchef::ModelRecipe>(model_recipe, mc); symbol_tables.push_back(table); // @@ -834,10 +1506,9 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) std::ostringstream stringStream; stringStream << "sub_" << (g + 1); - CookParams cp{buffer_vec, code_vec, subgraph_vec, flatbuffer_builder, - builtin_code_map, custom_code_vec, stringStream.str()}; + graph_name = stringStream.str(); - auto table = cook_graph<::tflchef::Graph>(graph, cp); + auto table = cook_graph<::tflchef::Graph>(graph, mc); symbol_tables.push_back(table); } @@ -944,10 +1615,10 @@ GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe) // Finalize ::tflite::FinishModelBuffer(*flatbuffer_builder, model); +#endif // Return "GenerateModel" - return GeneratedModel{ - std::unique_ptr(new GeneratedModelImpl(std::move(flatbuffer_builder)))}; + return GeneratedModel{std::move(gen_model)}; } } // namespace tflchef diff --git a/compiler/tflchef/proto/tflchef.proto b/compiler/tflchef/proto/tflchef.proto index e4ae5d9b65b..9dc02158c3d 100644 --- a/compiler/tflchef/proto/tflchef.proto +++ b/compiler/tflchef/proto/tflchef.proto @@ -719,4 +719,5 @@ message ModelRecipe { optional uint32 version = 6 [default = 1]; repeated Graph graph = 7; repeated SignatureDef signature_def = 8; + optional bool ext_offset = 9 [default = false]; } diff --git a/compiler/tflchef/tests/ext_offset/test.recipe b/compiler/tflchef/tests/ext_offset/test.recipe new file mode 100644 index 00000000000..4216210cb4a --- /dev/null +++ b/compiler/tflchef/tests/ext_offset/test.recipe @@ -0,0 +1,45 @@ +operand { + name: "ifm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 2 } +} +operand { + name: "ker" + type: FLOAT32 + shape { dim: 1 dim: 1 dim: 1 dim: 2 } + filler { + tag: "gaussian" + arg: "0.0" + arg: "1.0" + } +} +operand { + name: "bias" + type: FLOAT32 + shape { dim: 1 } + filler { + tag: "constant" + arg: "1.1" + } +} +operand { + name: "ofm" + type: FLOAT32 + shape { dim: 1 dim: 3 dim: 3 dim: 1 } +} +operation { + type: "Conv2D" + conv2d_options { + padding: VALID + stride_w: 1 + stride_h: 1 + } + input: "ifm" + input: "ker" + input: "bias" + output: "ofm" +} +input: "ifm" +input: "ker" +output: "ofm" +ext_offset: true diff --git a/res/TensorFlowLiteSchema/2.16.1/schema.fbs b/res/TensorFlowLiteSchema/2.16.1/schema.fbs new file mode 100644 index 00000000000..382462f938d --- /dev/null +++ b/res/TensorFlowLiteSchema/2.16.1/schema.fbs @@ -0,0 +1,1642 @@ +// 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. +// Version 3c: Move constant tensor buffers & custom op buffers outside from +// Flatbuffers. Has backward compatibility with version 3, 3a and +// 3b. + +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. + + // This field is added to distinguish between scalars and tensors of unknown + // ranks (both of which shape is []). + // For scalars (rank = 0), shape = [] and has_rank = true. + // For tensors with known rank (rank > 0) and shape, shape = [...] and + // has_rank = true. + // For tensors with unknown rank and shape, shape = [] and has_rank = false. + 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, + BITCAST = 159, + BITWISE_XOR = 160, + RIGHT_SHIFT = 161, + // All Operators start with STABLEHLO_ prefixes are subject to change + // Many of the ops below can not be executed by TFlite runtime + STABLEHLO_LOGISTIC = 162, // WARNING: Do not have runtime support + STABLEHLO_ADD = 163, + STABLEHLO_DIVIDE = 164, // WARNING: No runtime support yet + STABLEHLO_MULTIPLY = 165, + STABLEHLO_MAXIMUM = 166, + STABLEHLO_RESHAPE = 167, // WARNING: No runtime support yet + STABLEHLO_CLAMP = 168, // WARNING: No runtime support + STABLEHLO_CONCATENATE = 169, // WARNING: No runtime support + STABLEHLO_BROADCAST_IN_DIM = 170, // WARNING: No runtime support + STABLEHLO_CONVOLUTION = 171, // WARNING: No runtime support + STABLEHLO_SLICE = 172, // WARNING: No runtime support + STABLEHLO_CUSTOM_CALL = 173, // WARNING: No runtime support + STABLEHLO_REDUCE = 174, // WARNING: No runtime support + STABLEHLO_ABS = 175, // WARNING: No runtime support + STABLEHLO_AND = 176, // WARNING: No runtime support + STABLEHLO_COSINE = 177, // WARNING: No runtime support + STABLEHLO_EXPONENTIAL = 178, // WARNING: No runtime support + STABLEHLO_FLOOR = 179, // WARNING: No runtime support + STABLEHLO_LOG = 180, // WARNING: No runtime support + STABLEHLO_MINIMUM = 181, + STABLEHLO_NEGATE = 182, // WARNING: No runtime support + STABLEHLO_OR = 183, // WARNING: No runtime support + STABLEHLO_POWER = 184, // WARNING: No runtime support + STABLEHLO_REMAINDER = 185, // WARNING: No runtime support + STABLEHLO_RSQRT = 186, // WARNING: No runtime support + STABLEHLO_SELECT = 187, // WARNING: No runtime support + STABLEHLO_SUBTRACT = 188, // WARNING: No runtime support + STABLEHLO_TANH = 189, // WARNING: No runtime support + STABLEHLO_SCATTER = 190, + STABLEHLO_COMPARE = 191, // WARNING: No runtime support + STABLEHLO_CONVERT = 192, // WARNING: No runtime support + STABLEHLO_DYNAMIC_SLICE = 193, // WARNING: No runtime support + STABLEHLO_DYNAMIC_UPDATE_SLICE = 194, // WARNING: No runtime support + STABLEHLO_PAD = 195, + STABLEHLO_IOTA = 196, // WARNING: No runtime support + STABLEHLO_DOT_GENERAL = 197, // WARNING: No runtime support + STABLEHLO_REDUCE_WINDOW = 198, + STABLEHLO_SORT = 199, // WARNING: No runtime support + STABLEHLO_WHILE = 200, // WARNING: No runtime support + STABLEHLO_GATHER = 201, + STABLEHLO_TRANSPOSE = 202, // WARNING: No runtime support + DILATE = 203, + STABLEHLO_RNG_BIT_GENERATOR = 204, + REDUCE_WINDOW = 205 (deprecated), +} +// 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, + BitcastOptions, + BitwiseXorOptions, + RightShiftOptions, + // DO NOT add new options this union, will cause failure in Java api + // generation otherwise + // Add new builtin options into builtin options 2 instead +} + +union BuiltinOptions2{ + StablehloConcatenateOptions, + StablehloBroadcastInDimOptions, + StablehloSliceOptions, + StablehloConvolutionOptions, + StablehloCustomCallOptions, + StablehloReduceOptions, + StablehloScatterOptions, + StablehloCompareOptions, + StablehloDynamicSliceOptions, + StablehloPadOptions, + StablehloIotaOptions, + StablehloDotGeneralOptions, + StablehloReduceWindowOptions, + StablehloSortOptions, + StablehloWhileOptions, + StablehloGatherOptions, + StablehloTransposeOptions, + DilateOptions, + StablehloRngBitGeneratorOptions, + ReduceWindowOptions (deprecated), +} + +table StablehloGatherOptions{ + offset_dims : [long]; + collapsed_slice_dims : [long]; + start_index_map : [long]; + index_vector_dim : long; + slice_sizes : [long]; + indices_are_sorted : bool; +} + +table StablehloTransposeOptions{ + permutation : [long]; +} + +enum StablehloPrecisionConfig : uint { + DEFAULT, + HIGH, + HIGHEST, +} + +table StablehloDotGeneralOptions{ + lhs_batching_dimensions : [long]; + rhs_batching_dimensions : [long]; + lhs_contracting_dimensions : [long]; + rhs_contracting_dimensions : [long]; + precision_config : [StablehloPrecisionConfig]; +} + +table StablehloReduceWindowOptions{ + window_dimensions : [long]; + window_strides : [long]; + base_dilations : [long]; + window_dilations : [long]; + padding : [long]; + body_subgraph_index : int; +} + +table StablehloWhileOptions{ + cond_subgraph_index : int; + body_subgraph_index : int; +} + +table StablehloSortOptions{ + dimension : long; + is_stable : bool; + comparator_subgraph_index : int; +} + +table StablehloConcatenateOptions { + dimension : long; +} + +table StablehloBroadcastInDimOptions{ + broadcast_dimensions : [long]; +} + +enum StablehloComparisonDirection : uint { + STABLEHLO_COMPARISON_DIRECTION_EQ, + STABLEHLO_COMPARISON_DIRECTION_NE, + STABLEHLO_COMPARISON_DIRECTION_GE, + STABLEHLO_COMPARISON_DIRECTION_GT, + STABLEHLO_COMPARISON_DIRECTION_LE, + STABLEHLO_COMPARISON_DIRECTION_LT, + +} + +enum StablehloComparisonType : uint { + STABLEHLO_COMPARISON_TYPE_NOTYPE, + STABLEHLO_COMPARISON_TYPE_FLOAT, + STABLEHLO_COMPARISON_TYPE_FLOAT_TOTAL_ORDER, + STABLEHLO_COMPARISON_TYPE_SIGNED, + STABLEHLO_COMPARISON_TYPE_UNSIGNED, +} + +table StablehloCompareOptions{ + comparison_direction : StablehloComparisonDirection; + compare_type : StablehloComparisonType; +} + +table StablehloDynamicSliceOptions{ + slice_sizes : [long]; +} + +table StablehloPadOptions{ + edge_padding_low : [long]; + edge_padding_high : [long]; + interior_padding : [long]; +} + +table StablehloIotaOptions{ + iota_dimension : long; +} + +table StablehloCustomCallOptions { + call_target_name : string; + has_side_effect : bool; + backend_config: string; + api_version : int; // will be decprecated + called_computations: [int]; // should point to subgraphs of the computations + custom_attributes : [ubyte]; +} + +table StablehloReduceOptions { + dimensions : [long]; + body_subgraph_index : int; +} + +table StablehloSliceOptions{ + start_indices : [long]; + limit_indices : [long]; + strides : [long]; +} + +table StablehloConvolutionOptions{ + window_strides : [long]; + padding : [long]; + lhs_dilation : [long]; + rhs_dilation : [long]; + window_reversal : [bool]; + input_batch_dimension : long; + input_feature_dimension : long; + input_spatial_dimensions : [long]; + kernel_input_feature_dimension : long; + kernel_output_feature_dimension : long; + kernel_spatial_dimensions : [long]; + output_batch_dimension : long; + output_feature_dimension : long; + output_spatial_dimensions : [long]; + feature_group_count : long; + batch_group_count : long; + precision_config : [StablehloPrecisionConfig]; +} + +table StablehloScatterOptions { + indices_are_sorted: bool; + update_window_dims: [long]; + inserted_window_dims: [long]; + scatter_dims_to_operand_dims: [long]; + index_vector_dim: long; + unique_indices: bool; + update_computation_subgraph_index: int; +} + +enum RngAlgorithm : byte { + // An algorithm auto-selected by the system according to device type. + DEFAULT = 0, + // The Philox algorithm, as described in paper + // ['Parallel Random Numbers: As Easy as 1, 2, 3'] + // (https://www.thesalmons.org/john/random123/papers/random123sc11.pdf) + PHILOX = 1, + // The ThreeFry algorithm, as described in paper + // ['Parallel Random Numbers: As Easy as 1, 2, 3'] + // (https://www.thesalmons.org/john/random123/papers/random123sc11.pdf) + THREEFRY = 2, +} + +table StablehloRngBitGeneratorOptions { + algorithm:RngAlgorithm; +} + +// 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; + // Parameters for Conv2D version 8 or above. + // When set, quantized_bias_type defines the dtype for both bias and accumulator. + quantized_bias_type: TensorType; +} + +// 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; + + // Parameters for FullyConnected version 11 or above. + // When set, quantized_bias_type defines the dtype for both bias and accumulator. + quantized_bias_type: TensorType; +} + +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; + + // Parameter for unidirectional sequence RNN version 4. + diagonal_recurrent_tensors: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; + // If true, then the end tensor is an offset of the begin tensor. + offset: bool; +} + +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 { + // Parameters supported by version 1, 2, 3: + padding:Padding; + stride_w:int; + stride_h:int; + + // Parameters supported by version 4: + fused_activation_function:ActivationFunctionType = NONE; + + // Parameters for TransposeConv version 5 or above. + // If set, use this for bias and accumulator. + // When set, quantized_bias_type defines the dtype for both bias and accumulator. + quantized_bias_type: TensorType; +} + +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 { +} + +table BitcastOptions { +} + +table BitwiseXorOptions { +} + +table RightShiftOptions { +} + +table DilateOptions { +} + +enum ReduceWindowFunction : int { + UNSUPPORTED, + ADD, + MUL, + MINIMUM, + MAXIMUM, + ALL, + ANY, +} + +table ReduceWindowOptions (deprecated) { + reduce_function: ReduceWindowFunction; +} + +// 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]; + + // When an op is using custom_options in a model that is larger than 2GB, then + // we instead use the following attributes to find the buffer location which + // is stored outside of flatbuffers, the offset is calculated relative to the + // beginning of the file and is only valid if > 1 + large_custom_options_offset: ulong; + large_custom_options_size: ulong; + + // Flatbuffers union struct has a 128 elements limit in JAVA, so a second + // union is added, in the case of where BuitlinOptions2 runs out, a third + // one can be added + builtin_options_2 : BuiltinOptions2; +} + +// 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); + + // In a model that is larger than 2GB, then buffers instead uses the following + // attributes to find stored data, which is outside of flatbuffers + // the offset is calculated relative to the beginning of the file and is only + // valid if > 1. + offset: ulong; + size: ulong; +} + +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;