# Eigen
/ONE/compiler/nnc/backends/soft_backend/code_snippets/eigen.def
+# Frontend test tools that are needed for release package build
+/ONE/compiler/circlechef
+/ONE/compiler/circle-verify
+/ONE/compiler/luci/tester
+
+# Exclude IR headers which have lots of similar patterns
+# TODO remove this when refactoring is possible
+/ONE/compiler/luci/lang/include/luci/IR/Nodes
+/ONE/compiler/luci/import/include/luci/Import/Nodes
+/ONE/compiler/loco/include/loco/IR
+/ONE/compiler/tflchef/tflite/src/Op/include
+
+# Exclude interpreter kernels which have similar patterns
+/ONE/compiler/luci-interpreter/src/kernels
+/ONE/compiler/locomotiv/src/Node
+
# Test codes
/ONE/tests
testCaseLanguage: CPP
testFW: GTEST
testCaseFolder:
- - /compute/test/cker
+ - /compute/test
+ - /runtime/libs/misc/test
+ - /runtime/libs/ndarray/test
- /runtime/onert/core/src/backend/basic
- /runtime/onert/frontend/nnapi
- - /runtime/onert/test/core/compiler
- - /runtime/onert/test/core/exec
- - /runtime/onert/test/core/interp
- - /runtime/onert/test/graph
- - /runtime/onert/test/graph/operand
- - /runtime/onert/test/graph/operation
- - /runtime/onert/test/graph/verifier
- - /runtime/onert/test/ir
- - /runtime/onert/test/util
- - /tests/nnfw_api/src
+ - /runtime/onert/test
+ - /tests/nnfw_api
testFile:
- extension: cpp
- functionName:
starts:
- TEST
+ - TYPED_TEST
- excludes :
- Verifier.dag_checker
- graph_operand_LayoutSet.layout_set_operators
std::string _long_name;
std::string _short_name;
std::vector<std::string> _names;
- std::string _type;
+ std::string _type = "string";
std::string _help_message;
std::function<void(void)> _func;
uint32_t _nargs{1};
/*
** print usage
*/
+ auto print_usage_arg = [&](const arser::Argument &arg) {
+ stream << " ";
+ std::string arg_name = arser::internal::remove_dash(arg._long_name);
+ std::for_each(arg_name.begin(), arg_name.end(),
+ [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); });
+ };
stream << "Usage: ./" << parser._program_name << " ";
// required optional argument
for (const auto &arg : parser._optional_arg_vec)
{
if (!arg._is_required)
continue;
- stream << arg._short_name << " ";
- std::string arg_name = arser::internal::remove_dash(arg._long_name);
- std::for_each(arg_name.begin(), arg_name.end(),
- [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); });
+ stream << arg._short_name;
+ print_usage_arg(arg);
stream << " ";
}
// rest of the optional argument
stream << "[" << arg._short_name;
if (arg._nargs)
{
- stream << " ";
- std::string arg_name = arser::internal::remove_dash(arg._long_name);
- std::for_each(arg_name.begin(), arg_name.end(),
- [&stream](const char &c) { stream << static_cast<char>(::toupper(c)); });
+ print_usage_arg(arg);
}
stream << "]"
<< " ";
}
const size_t message_width = 60;
- // positional argument
- if (!parser._positional_arg_vec.empty())
- {
- stream << "[Positional argument]" << std::endl;
- for (const auto &arg : parser._positional_arg_vec)
+ auto print_help_args = [&](const std::list<Argument> &args, const std::string &title) {
+ if (!args.empty())
{
- stream.width(length_of_longest_arg);
- stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t";
- for (size_t i = 0; i < arg._help_message.length(); i += message_width)
+ stream << title << std::endl;
+ for (const auto &arg : args)
{
- if (i)
- stream << std::string(length_of_longest_arg, ' ') << "\t";
- stream << arg._help_message.substr(i, message_width) << std::endl;
+ stream.width(length_of_longest_arg);
+ stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t";
+ for (size_t i = 0; i < arg._help_message.length(); i += message_width)
+ {
+ if (i)
+ stream << std::string(length_of_longest_arg, ' ') << "\t";
+ stream << arg._help_message.substr(i, message_width) << std::endl;
+ }
}
+ std::cout << std::endl;
}
- std::cout << std::endl;
- }
+ };
+ // positional argument
+ print_help_args(parser._positional_arg_vec, "[Positional argument]");
// optional argument
- if (!parser._optional_arg_vec.empty())
- {
- stream << "[Optional argument]" << std::endl;
- for (const auto &arg : parser._optional_arg_vec)
- {
- stream.width(length_of_longest_arg);
- stream << std::left << arser::internal::make_comma_concatenated(arg._names) << "\t";
- for (size_t i = 0; i < arg._help_message.length(); i += message_width)
- {
- if (i)
- stream << std::string(length_of_longest_arg, ' ') << "\t";
- stream << arg._help_message.substr(i, message_width) << std::endl;
- }
- }
- }
+ print_help_args(parser._optional_arg_vec, "[Optional argument]");
return stream;
}
return get_impl(arg_name, static_cast<T *>(nullptr));
}
+class Helper
+{
+public:
+ static void add_version(Arser &arser, const std::function<void(void)> &func)
+ {
+ arser.add_argument("--version")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("Show version information and exit")
+ .exit_with(func);
+ }
+
+ static void add_verbose(Arser &arser)
+ {
+ arser.add_argument("-V", "--verbose")
+ .nargs(0)
+ .required(false)
+ .default_value(false)
+ .help("output additional information to stdout or stderr");
+ }
+};
+
} // namespace arser
#endif // __ARSER_H__
if (to_lower_case(str).compare("mae") == 0)
return Metric::MAE;
+ if (to_lower_case(str).compare("mape") == 0)
+ return Metric::MAPE;
+
throw std::runtime_error("Unsupported metric.");
}
{
arser::Arser arser("Compare inference results of two circle models");
- arser.add_argument("--version")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("Show version information and exit")
- .exit_with(print_version);
-
- arser.add_argument("--first_model")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(true)
- .help("First input model filepath");
-
- arser.add_argument("--second_model")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(true)
- .help("Second input model filepath");
+ arser::Helper::add_version(arser, print_version);
+
+ arser.add_argument("--first_model").required(true).help("First input model filepath");
+
+ arser.add_argument("--second_model").required(true).help("Second input model filepath");
arser.add_argument("--first_input_data")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.help("Input data filepath for the first model. If not given, circle-eval-diff will run with "
"randomly generated data");
arser.add_argument("--second_input_data")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.help("Input data filepath for the second model. If not given, circle-eval-diff will run with "
"randomly generated data");
- arser.add_argument("--metric")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .default_value("MAE")
- .help("Metric for comparison (default: MAE)");
+ arser.add_argument("--metric").default_value("MAE").help("Metric for comparison (default: MAE)");
arser.add_argument("--input_data_format")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.default_value("h5")
.help("Input data format. h5/hdf5 (default) or directory");
enum class Metric
{
Undefined, // For debugging
- MAE,
+ MAE, // Mean Absolute Error
+ MAPE, // Mean Percentage Absolute Error
};
enum class InputFormat
case Metric::MAE:
metric = std::make_unique<MAEPrinter>();
break;
+ case Metric::MAPE:
+ metric = std::make_unique<MAPEPrinter>();
+ break;
default:
throw std::runtime_error("Unsupported metric.");
}
}
}
+// TODO Remove duplicate codes with MAEPrinter
+void MAPEPrinter::init(const luci::Module *first, const luci::Module *second)
+{
+ THROW_UNLESS(first != nullptr, "Invalid module.");
+ THROW_UNLESS(second != nullptr, "Invalid module.");
+
+ const auto first_output = loco::output_nodes(first->graph());
+ const auto second_output = loco::output_nodes(second->graph());
+
+ assert(first_output.size() == second_output.size()); // FIX_CALLER_UNLESS
+
+ for (uint32_t i = 0; i < first_output.size(); i++)
+ {
+ const auto first_node = loco::must_cast<luci::CircleNode *>(first_output[i]);
+ const auto second_node = loco::must_cast<luci::CircleNode *>(second_output[i]);
+ assert(same_shape(first_node, second_node)); // FIX_CALLER_UNLESS
+
+ // Create tensors to store intermediate results
+ _intermediate.emplace_back();
+ _intermediate.at(i).dtype(loco::DataType::FLOAT32);
+ // NOTE Use both first_node and second_node to avoid release build break
+ _intermediate.at(i).rank(first_node->rank());
+ uint32_t num_elems = 1;
+ for (uint32_t j = 0; j < second_node->rank(); j++)
+ {
+ _intermediate.at(i).dim(j) = second_node->dim(j);
+ num_elems *= second_node->dim(j).value();
+ }
+ _intermediate.at(i).size<loco::DataType::FLOAT32>(num_elems);
+
+ // Check the buffer is initilized with zero
+ for (uint32_t j = 0; j < num_elems; j++)
+ assert(_intermediate.at(i).at<loco::DataType::FLOAT32>(j) == 0.0);
+
+ // Save output names for logging
+ _output_names.emplace_back(first_node->name());
+ }
+}
+
+// Accumulate |(a - b) / a|
+void MAPEPrinter::accum_mean_absolute_error(uint32_t output_idx, const std::shared_ptr<Tensor> &a,
+ const std::shared_ptr<Tensor> &b)
+{
+ assert(a->dtype() == loco::DataType::FLOAT32 and
+ b->dtype() == loco::DataType::FLOAT32); // FIX_CALLER_UNLESS
+ assert(same_shape(a.get(), b.get())); // FIX_CALLER_UNLESS
+ assert(output_idx < _intermediate.size()); // FIX_CALLER_UNLESS
+
+ for (uint32_t i = 0; i < a->size<loco::DataType::FLOAT32>(); i++)
+ {
+ const auto a_val = a->at<loco::DataType::FLOAT32>(i);
+ const auto b_val = b->at<loco::DataType::FLOAT32>(i);
+ _intermediate.at(output_idx).at<loco::DataType::FLOAT32>(i) +=
+ std::abs((a_val - b_val) / a_val);
+ }
+}
+
+// Assumption
+// first: the result of fp32 model
+// second: the result of fake-quantized model
+void MAPEPrinter::accumulate(const std::vector<std::shared_ptr<Tensor>> &first,
+ const std::vector<std::shared_ptr<Tensor>> &second)
+{
+ assert(first.size() == second.size()); // FIX_CALLER_UNLESS
+ assert(first.size() == _intermediate.size()); // FIX_CALLER_UNLESS
+
+ for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++)
+ {
+ const auto first_output = first[output_idx];
+ const auto second_output = second[output_idx];
+
+ // Cast data to fp32 and then compute absolute error
+ const auto fp32_first_output = fp32(first_output);
+ const auto fp32_second_output = fp32(second_output);
+
+ accum_mean_absolute_error(output_idx, fp32_first_output, fp32_second_output);
+ }
+
+ _num_data++;
+}
+
+void MAPEPrinter::dump(std::ostream &os) const
+{
+ os << "Mean Absolute Percentage Error (MAPE)" << std::endl;
+
+ for (uint32_t output_idx = 0; output_idx < _intermediate.size(); output_idx++)
+ {
+ const auto name = _output_names.at(output_idx);
+ const auto &inter = _intermediate.at(output_idx);
+ assert(inter.dtype() == loco::DataType::FLOAT32); // FIX_ME_UNLESS
+ const auto elem_count = inter.size<loco::DataType::FLOAT32>();
+
+ // Compute MAPE
+ float mape = 0.0;
+ for (uint32_t elem_idx = 0; elem_idx < elem_count; elem_idx++)
+ mape += inter.at<loco::DataType::FLOAT32>(elem_idx);
+
+ mape = mape / elem_count;
+ mape = mape / _num_data;
+ mape *= 100.0;
+
+ os << "MAPE for " << name << " is " << mape << "%" << std::endl;
+ }
+}
+
} // namespace circle_eval_diff
#undef THROW_UNLESS
uint32_t _num_data = 0;
};
+// Mean Absolute Percentage Error
+class MAPEPrinter final : public MetricPrinter
+{
+public:
+ void init(const luci::Module *first, const luci::Module *second);
+
+ void accumulate(const std::vector<std::shared_ptr<Tensor>> &first,
+ const std::vector<std::shared_ptr<Tensor>> &second);
+
+ void dump(std::ostream &os) const;
+
+private:
+ void accum_mean_absolute_error(uint32_t index, const std::shared_ptr<Tensor> &a,
+ const std::shared_ptr<Tensor> &b);
+
+private:
+ // Store accumulated sum of absolute error for each output
+ std::vector<Tensor> _intermediate;
+ std::vector<std::string> _output_names;
+ uint32_t _num_data = 0;
+};
+
} // namespace circle_eval_diff
#endif // __CIRCLE_EVAL_DIFF_METRIC_PRINTER_H__
EXPECT_ANY_THROW(mae.init(nullptr, nullptr));
}
+TEST(CircleEvalMetricPrinterTest, MAPE_simple)
+{
+ luci::Module first;
+ AddOneGraph first_g;
+ first_g.init();
+
+ first.add(std::move(first_g.graph()));
+
+ luci::Module second;
+ AddTwoGraph second_g;
+ second_g.init();
+
+ second.add(std::move(second_g.graph()));
+
+ MAPEPrinter mape;
+
+ mape.init(&first, &second);
+
+ // This test does not actually evaluate the modules, but create
+ // fake results.
+ std::vector<std::shared_ptr<Tensor>> first_result;
+ {
+ auto output = output_tensor_with_value(&first, 2.0);
+ first_result.emplace_back(output);
+ }
+
+ std::vector<std::shared_ptr<Tensor>> second_result;
+ {
+ auto output = output_tensor_with_value(&second, 1.0);
+ second_result.emplace_back(output);
+ }
+
+ mape.accumulate(first_result, second_result);
+
+ std::stringstream ss;
+ mape.dump(ss);
+ std::string result = ss.str();
+
+ EXPECT_NE(std::string::npos, result.find("MAPE for output_0 is 50%"));
+}
+
+TEST(CircleEvalMetricPrinterTest, MAPE_init_with_null_NEG)
+{
+ MAPEPrinter mape;
+
+ EXPECT_ANY_THROW(mape.init(nullptr, nullptr));
+}
+
} // namespace circle_eval_diff
{
arser::Arser arser("circle_execution_plan provides model with execution plan meta information");
- arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model");
- arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model");
- arser.add_argument("--platform")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .default_value("linux")
- .help("Platform name: linux mcu cmsisnn");
+ arser.add_argument("input").help("Input circle model");
+ arser.add_argument("output").help("Output circle model");
+ arser.add_argument("--platform").default_value("linux").help("Platform name: linux mcu cmsisnn");
arser.add_argument("--use_dsp")
.nargs(1)
.type(arser::DataType::BOOL)
.help("Dump Conv2D series weight operators in circle file");
arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in circle file");
arser.add_argument("--tensor_dtype").nargs(0).help("Dump dtype of tensors");
- arser.add_argument("circle").type(arser::DataType::STR).help("Circle file to inspect");
+ arser.add_argument("circle").help("Circle file to inspect");
try
{
*/
#include "Dump.h"
-#include "Reader.h"
+
+#include <mio_circle/Helper.h>
+#include <mio_circle/Reader.h>
#include <ostream>
void DumpOperators::run(std::ostream &os, const circle::Model *model)
{
- circleinspect::Reader reader(model);
+ mio::circle::Reader reader(model);
const uint32_t subgraph_size = reader.num_subgraph();
namespace
{
-const circle::Operator *operator_match_output(circleinspect::Reader &reader, const int32_t tensor)
+const circle::Operator *operator_match_output(mio::circle::Reader &reader, const int32_t tensor)
{
auto ops = reader.operators();
{
const auto op = ops->Get(i);
- const std::vector<int32_t> &outputs = circleinspect::as_index_vector(op->outputs());
+ const std::vector<int32_t> &outputs = mio::circle::as_index_vector(op->outputs());
for (auto output : outputs)
{
return nullptr;
}
-size_t tensor_buffer_size(circleinspect::Reader &reader, const int32_t tensor_id)
+size_t tensor_buffer_size(mio::circle::Reader &reader, const int32_t tensor_id)
{
auto tensors = reader.tensors();
void DumpConv2DWeight::run(std::ostream &os, const circle::Model *model)
{
- circleinspect::Reader reader(model);
+ mio::circle::Reader reader(model);
const uint32_t subgraph_size = reader.num_subgraph();
if (bc == circle::BuiltinOperator_CONV_2D || bc == circle::BuiltinOperator_DEPTHWISE_CONV_2D)
{
- const std::vector<int32_t> &inputs = circleinspect::as_index_vector(op->inputs());
+ const std::vector<int32_t> &inputs = mio::circle::as_index_vector(op->inputs());
if (inputs.size() < 2)
{
throw std::runtime_error("Operator has invalid input");
{
std::map<std::string, int32_t> op_version_map;
- circleinspect::Reader reader(model);
+ mio::circle::Reader reader(model);
// This assert is subject to be changed later
assert(reader.num_subgraph() == 1);
void DumpTensorDType::run(std::ostream &os, const circle::Model *model)
{
- circleinspect::Reader reader(model);
+ mio::circle::Reader reader(model);
const uint32_t subgraph_size = reader.num_subgraph();
+++ /dev/null
-/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 __READER_H__
-#define __READER_H__
-
-#include <mio/circle/schema_generated.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace circleinspect
-{
-
-template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
-{
- std::vector<T> ret(flat_array->Length());
- for (uint32_t i = 0; i < flat_array->Length(); i++)
- {
- ret[i] = flat_array->Get(i);
- }
- return ret;
-}
-
-/**
- * @brief Loads Circle file and provides helpers to access attributes
- */
-class Reader
-{
-private:
- using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
- using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
- using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
- using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
-
-public:
- Reader(const circle::Model *model);
-
- Reader() = delete;
-
-public:
- const std::vector<const circle::OperatorCode *> &opcodes() { return _op_codes; }
- const CircleBuffers_t *buffers() { return _buffers; }
- const CircleTensors_t *tensors() { return _tensors; }
- const CircleOperators_t *operators() { return _operators; }
- const std::vector<int32_t> &inputs() const { return _inputs; }
- const std::vector<int32_t> &outputs() const { return _outputs; }
-
- uint32_t num_subgraph() const { return _subgraphs->Length(); }
-
- size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data);
- circle::BuiltinOperator builtin_code(const circle::Operator *op) const;
- std::string opcode_name(const circle::Operator *op) const;
- std::string tensor_name(const circle::Tensor *tensor) const;
- std::string tensor_dtype(const circle::Tensor *tensor) const;
-
-public:
- bool select_subgraph(uint32_t subgraph);
-
-private:
- const CircleSubGraphs_t *_subgraphs{nullptr};
- const CircleBuffers_t *_buffers{nullptr};
- const CircleTensors_t *_tensors{nullptr};
- const CircleOperators_t *_operators{nullptr};
-
- std::vector<const circle::OperatorCode *> _op_codes;
- std::vector<int32_t> _inputs;
- std::vector<int32_t> _outputs;
-};
-
-} // namespace circleinspect
-
-#endif // __READER_H__
arser::Arser arser("circle-opselector provides selecting operations in circle model");
- arser.add_argument("--version")
- .nargs(0)
- .default_value(false)
- .help("Show version information and exit")
- .exit_with(print_version);
+ arser::Helper::add_version(arser, print_version);
// TODO Add new options!
- arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model");
- arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model");
+ arser.add_argument("input").help("Input circle model");
+ arser.add_argument("output").help("Output circle model");
// select option
- arser.add_argument("--by_id")
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Input operation id to select nodes.");
- arser.add_argument("--by_name")
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Input operation name to select nodes.");
+ arser.add_argument("--by_id").help("Input operation id to select nodes.");
+ arser.add_argument("--by_name").help("Input operation name to select nodes.");
try
{
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable(circle-partitioner "${SOURCES}")
-target_link_libraries(circle-partitioner foder)
target_link_libraries(circle-partitioner crew)
target_link_libraries(circle-partitioner safemain)
target_link_libraries(circle-partitioner luci_lang)
# TODO remove circle_partitioner
add_executable(circle_partitioner "${SOURCES}")
-target_link_libraries(circle_partitioner foder)
target_link_libraries(circle_partitioner crew)
target_link_libraries(circle_partitioner safemain)
target_link_libraries(circle_partitioner luci_lang)
-require("foder")
require("crew")
require("pepper-csv2vec")
require("safemain")
#include "PartitionExport.h"
#include "HelperPath.h"
-#include <foder/FileLoader.h>
-
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
#include <luci/Service/Validate.h>
#include <luci/CircleExporter.h>
#include <luci/CircleFileExpContract.h>
void build_arser(arser::Arser &arser)
{
- arser.add_argument("--version")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("Show version information and exit")
- .exit_with(print_version);
-
- arser.add_argument(opt_bks)
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .help("Backends in CSV to use for partitioning");
-
- arser.add_argument(opt_def)
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .help("Default backend to assign");
-
- arser.add_argument(opt_part)
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Partition file which provides backend to assign");
- arser.add_argument(opt_input)
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Input circle model filename");
- arser.add_argument(opt_work)
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Work folder of partition, input files exist and output files are produced");
-}
+ arser::Helper::add_version(arser, print_version);
-std::unique_ptr<luci::Module> load_model(const std::string &input_path)
-{
- // Load model from the file
- foder::FileLoader file_loader{input_path};
- std::vector<char> model_data = file_loader.load();
+ arser.add_argument(opt_bks).help("Backends in CSV to use for partitioning");
- // Verify flatbuffers
- flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
- if (!circle::VerifyModelBuffer(verifier))
- {
- std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
- return nullptr;
- }
+ arser.add_argument(opt_def).help("Default backend to assign");
- const circle::Model *circle_model = circle::GetModel(model_data.data());
- if (circle_model == nullptr)
- {
- std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
- return nullptr;
- }
+ arser.add_argument(opt_part).help("Partition file which provides backend to assign");
+ arser.add_argument(opt_input).help("Input circle model filename");
+ arser.add_argument(opt_work).help(
+ "Work folder of partition, input files exist and output files are produced");
+}
+std::unique_ptr<luci::Module> load_model(const std::string &input_path)
+{
// Import from input Circle file
- luci::Importer importer;
- return importer.importModule(circle_model);
+ luci::ImporterEx importerex;
+ return importerex.importVerifyModule(input_path);
}
} // namespace
get_target_property(ARTIFACTS_BIN_PATH testDataGenerator BINARY_DIR)
set(options USE_QCONFIG)
-set(oneValueArgs DTYPE GRANULARITY)
+set(oneValueArgs DTYPE GRANULARITY INPUT_DTYPE OUTPUT_DTYPE)
set(multiValueArgs "")
macro(Add RECIPE)
set(QCONFIG_OPT "--config" "${ARTIFACTS_BIN_PATH}/${RECIPE}.qconf.json")
endif()
+ set(INPUT_DTYPE_OPT "")
+ if(ARG_INPUT_DTYPE)
+ set(INPUT_DTYPE_OPT "--input_type" "${ARG_INPUT_DTYPE}")
+ endif()
+
+ set(OUTPUT_DTYPE_OPT "")
+ if(ARG_OUTPUT_DTYPE)
+ set(OUTPUT_DTYPE_OPT "--output_type" "${ARG_OUTPUT_DTYPE}")
+ endif()
+
set(CIRCLE_PATH "${ARTIFACTS_BIN_PATH}/${RECIPE}.circle")
set(FAKE_QUANT_CIRCLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE}.fq.circle")
set(RECORDED_CIRCLE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${RECIPE}.recorded.circle")
add_custom_command(OUTPUT ${QUANT_CIRCLE_PATH}
COMMAND $<TARGET_FILE:circle-quantizer> --quantize_dequantize_weights float32 ${ARG_DTYPE} ${ARG_GRANULARITY} ${QCONFIG_OPT} ${CIRCLE_PATH} ${FAKE_QUANT_CIRCLE_PATH}
COMMAND $<TARGET_FILE:record-minmax> --input_model ${FAKE_QUANT_CIRCLE_PATH} --output_model ${RECORDED_CIRCLE_PATH}
- COMMAND $<TARGET_FILE:circle-quantizer> --quantize_with_minmax float32 ${ARG_DTYPE} ${ARG_GRANULARITY} ${QCONFIG_OPT} ${RECORDED_CIRCLE_PATH} ${QUANT_CIRCLE_PATH}
+ COMMAND $<TARGET_FILE:circle-quantizer>
+ --quantize_with_minmax float32 ${ARG_DTYPE} ${ARG_GRANULARITY}
+ ${QCONFIG_OPT} ${RECORDED_CIRCLE_PATH} ${QUANT_CIRCLE_PATH}
+ ${INPUT_DTYPE_OPT} ${OUTPUT_DTYPE_OPT}
DEPENDS
circle-quantizer
record-minmax
Add(Quant_Conv_Mul_Add_002 DTYPE uint8 GRANULARITY channel USE_QCONFIG)
Add(Quant_Split_Add_000 DTYPE uint8 GRANULARITY channel USE_QCONFIG)
Add(Quant_Split_Add_001 DTYPE uint8 GRANULARITY channel USE_QCONFIG)
+Add(Quant_Conv_000 DTYPE uint8 GRANULARITY channel INPUT_DTYPE float32)
+Add(Quant_Conv_001 DTYPE uint8 GRANULARITY channel OUTPUT_DTYPE float32)
+Add(Quant_Conv_002 DTYPE uint8 GRANULARITY channel INPUT_DTYPE float32 OUTPUT_DTYPE float32)
AddFakeQuant(Quant_Add_000)
target_include_directories(circle-quantizer PRIVATE ${Jsoncpp_INCLUDE_DIRS})
target_link_libraries(circle-quantizer ${Jsoncpp_STATIC_LIB})
-target_link_libraries(circle-quantizer foder)
target_link_libraries(circle-quantizer safemain)
target_link_libraries(circle-quantizer oops)
target_link_libraries(circle-quantizer loco)
-require("foder")
require("loco")
require("locop")
require("safemain")
* limitations under the License.
*/
-#include <foder/FileLoader.h>
-
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
#include <luci/CircleQuantizer.h>
#include <luci/Service/Validate.h>
#include <luci/CircleExporter.h>
arser::Arser arser("circle-quantizer provides circle model quantization");
- arser.add_argument("--version")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("Show version information and exit")
- .exit_with(print_version);
-
- arser.add_argument("-V", "--verbose")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("output additional information to stdout or stderr");
+ arser::Helper::add_version(arser, print_version);
+ arser::Helper::add_verbose(arser);
arser.add_argument(qdqw)
.nargs(3)
.type(arser::DataType::STR_VEC)
- .required(false)
.help("Quantize-dequantize weight values required action before quantization. "
"Three arguments required: input_model_dtype(float32) "
"output_model_dtype(uint8) granularity(layer, channel)");
arser.add_argument(qwmm)
.nargs(3)
.type(arser::DataType::STR_VEC)
- .required(false)
.help("Quantize with min/max values. "
"Three arguments required: input_model_dtype(float32) "
"output_model_dtype(uint8) granularity(layer, channel)");
arser.add_argument(tf_maxpool)
.nargs(0)
- .required(false)
.default_value(false)
.help("Force MaxPool Op to have the same input/output quantparams. NOTE: This feature can "
"degrade accuracy of some models");
arser.add_argument(fake_quant)
.nargs(0)
- .required(false)
.help("Convert a quantized model to a fake-quantized model. NOTE: This feature will "
"generate an fp32 model.");
arser.add_argument(rq)
.nargs(2)
.type(arser::DataType::STR_VEC)
- .required(false)
.help("Requantize a quantized model. "
"Two arguments required: input_model_dtype(int8) "
"output_model_dtype(uint8)");
arser.add_argument(fq)
.nargs(3)
.type(arser::DataType::STR_VEC)
- .required(false)
.accumulated(true)
.help("Write quantization parameters to the specified tensor. "
"Three arguments required: tensor_name(string), "
arser.add_argument(cq)
.nargs(2)
.type(arser::DataType::STR_VEC)
- .required(false)
.accumulated(true)
.help("Copy quantization parameter from a tensor to another tensor."
"Two arguments required: source_tensor_name(string), "
"destination_tensor_name(string)");
arser.add_argument("--input_type")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .help("Input type of quantized model (uint8 or int16)");
+ .help("Input type of quantized model (uint8, int16, or float32)");
arser.add_argument("--output_type")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .help("Output type of quantized model (uint8 or int16)");
+ .help("Output type of quantized model (uint8, int16, or float32)");
- arser.add_argument(cfg)
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .help("Path to the quantization configuration file");
+ arser.add_argument(cfg).help("Path to the quantization configuration file");
- arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model");
- arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model");
+ arser.add_argument("input").help("Input circle model");
+ arser.add_argument("output").help("Output circle model");
arser.add_argument(gpd).nargs(0).required(false).default_value(false).help(
"This will turn on profiling data generation.");
settings->set(luci::UserSettings::Key::ProfilingDataGen, true);
// Load model from the file
- foder::FileLoader file_loader{input_path};
- std::vector<char> model_data = file_loader.load();
-
- // Verify flatbuffers
- flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
- if (!circle::VerifyModelBuffer(verifier))
- {
- std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
+ luci::ImporterEx importerex;
+ auto module = importerex.importVerifyModule(input_path);
+ if (module.get() == nullptr)
return EXIT_FAILURE;
- }
-
- const circle::Model *circle_model = circle::GetModel(model_data.data());
- if (circle_model == nullptr)
- {
- std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
- return EXIT_FAILURE;
- }
-
- // Import from input Circle file
- luci::Importer importer;
- auto module = importer.importModule(circle_model);
for (size_t idx = 0; idx < module->size(); ++idx)
{
arser::Arser arser{
"circle-tensordump allows users to retrieve tensor information from a Circle model file"};
- arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Circle file path to dump");
+ arser.add_argument("circle").help("Circle file path to dump");
arser.add_argument("--tensors").nargs(0).help("Dump to console");
arser.add_argument("--tensors_to_hdf5")
- .nargs(1)
- .type(arser::DataType::STR)
.help("Dump to hdf5 file. Specify hdf5 file path to be dumped");
try
*/
#include "Dump.h"
-#include "Reader.h"
+
+#include <mio_circle/Reader.h>
#include <H5Cpp.h>
void DumpTensors::run(std::ostream &os, const circle::Model *model, const std::string &)
{
- circletensordump::Reader reader(model);
+ mio::circle::Reader reader(model);
uint32_t num_subgraph = reader.num_subgraph();
auto buffers = reader.buffers();
const std::string &output_path)
{
// loads a circle model
- circletensordump::Reader reader(model);
+ mio::circle::Reader reader(model);
uint32_t num_subgraph = reader.num_subgraph();
// create a hdf5 file
+++ /dev/null
-/*
- * 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 "Reader.h"
-
-#include <mio_circle/Helper.h>
-
-#include <sstream>
-#include <string>
-
-namespace circletensordump
-{
-
-Reader::Reader(const circle::Model *model)
-{
- _subgraphs = model->subgraphs();
- _buffers = model->buffers();
-
- auto opcodes = model->operator_codes();
- for (const ::circle::OperatorCode *opcode : *opcodes)
- {
- _op_codes.push_back(opcode);
- }
-}
-
-size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data)
-{
- if (buff_data != nullptr)
- {
- *buff_data = nullptr;
- }
-
- if (buf_idx == 0)
- return 0;
-
- if (auto *buffer = (*_buffers)[buf_idx])
- {
- if (auto *array = buffer->data())
- {
- if (size_t size = array->size())
- {
- if (buff_data != nullptr)
- {
- *buff_data = reinterpret_cast<const uint8_t *>(array->data());
- }
- return size;
- }
- }
- }
-
- return 0;
-}
-
-circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const
-{
- uint32_t index = op->opcode_index();
- assert(index < _op_codes.size());
- const circle::OperatorCode *opcode = _op_codes.at(index);
-
- return mio::circle::builtin_code_neutral(opcode);
-}
-
-std::string Reader::opcode_name(const circle::Operator *op) const
-{
- uint32_t index = op->opcode_index();
- assert(index < _op_codes.size());
- const circle::OperatorCode *opcode = _op_codes.at(index);
-
- if (!mio::circle::is_valid(opcode))
- {
- std::ostringstream oss;
- oss << "(invalid: " << index << ")";
- return oss.str();
- }
-
- return mio::circle::opcode_name(opcode);
-}
-
-bool Reader::select_subgraph(uint32_t sgindex)
-{
- _tensors = nullptr;
- _operators = nullptr;
-
- _inputs.clear();
- _outputs.clear();
-
- if (_subgraphs->Length() <= sgindex)
- {
- assert(false);
- return false;
- }
-
- const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
-
- _tensors = subgraph->tensors();
- _operators = subgraph->operators();
-
- _inputs = as_index_vector(subgraph->inputs());
- _outputs = as_index_vector(subgraph->outputs());
-
- return true;
-}
-
-} // namespace circletensordump
+++ /dev/null
-/*
- * 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 __CIRCLE_TENSORDUMP_READER_H__
-#define __CIRCLE_TENSORDUMP_READER_H__
-
-#include <mio/circle/schema_generated.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace circletensordump
-{
-
-template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
-{
- std::vector<T> ret(flat_array->Length());
- for (uint32_t i = 0; i < flat_array->Length(); i++)
- {
- ret[i] = flat_array->Get(i);
- }
- return ret;
-}
-
-/**
- * @brief Loads Circle file and provides helpers to access attributes
- */
-class Reader
-{
-private:
- using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
- using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
- using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
- using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
-
-public:
- Reader(const circle::Model *model);
-
- Reader() = delete;
-
-public:
- const std::vector<const circle::OperatorCode *> &opcodes() { return _op_codes; }
- const CircleBuffers_t *buffers() { return _buffers; }
- const CircleTensors_t *tensors() { return _tensors; }
- const CircleOperators_t *operators() { return _operators; }
- const std::vector<int32_t> &inputs() const { return _inputs; }
- const std::vector<int32_t> &outputs() const { return _outputs; }
-
- uint32_t num_subgraph() const { return _subgraphs->Length(); }
-
- size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data);
- circle::BuiltinOperator builtin_code(const circle::Operator *op) const;
- std::string opcode_name(const circle::Operator *op) const;
-
-public:
- bool select_subgraph(uint32_t subgraph);
-
-private:
- const CircleSubGraphs_t *_subgraphs{nullptr};
- const CircleBuffers_t *_buffers{nullptr};
- const CircleTensors_t *_tensors{nullptr};
- const CircleOperators_t *_operators{nullptr};
-
- std::vector<const circle::OperatorCode *> _op_codes;
- std::vector<int32_t> _inputs;
- std::vector<int32_t> _outputs;
-};
-
-} // namespace circletensordump
-
-#endif // __CIRCLE_TENSORDUMP_READER_H__
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("circle").type(arser::DataType::STR).help("Circle file path to verify");
+ arser.add_argument("circle").help("Circle file path to verify");
try
{
add_executable(circle2circle "${SOURCES}")
target_include_directories(circle2circle PRIVATE src)
-target_link_libraries(circle2circle foder)
target_link_libraries(circle2circle nncc_common)
target_link_libraries(circle2circle safemain)
target_link_libraries(circle2circle oops)
GTest_AddTest(circle2circle_test ${TESTS} ${SOURCES})
target_include_directories(circle2circle_test PRIVATE src)
-target_link_libraries(circle2circle_test foder)
target_link_libraries(circle2circle_test nncc_common)
target_link_libraries(circle2circle_test oops)
target_link_libraries(circle2circle_test hermes)
-require("foder")
require("loco")
require("locop")
require("logo-core")
* limitations under the License.
*/
-#include <foder/FileLoader.h>
-
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
#include <luci/CircleOptimizer.h>
#include <luci/Service/ChangeOutputs.h>
#include <luci/Service/Validate.h>
arser::Arser arser("circle2circle provides circle model optimization and transformations");
- arser.add_argument("--version")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("Show version information and exit")
- .exit_with(print_version);
-
- arser.add_argument("-V", "--verbose")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("output additional information to stdout or stderr");
+ arser::Helper::add_version(arser, print_version);
+ arser::Helper::add_verbose(arser);
arser.add_argument("--O1").nargs(0).required(false).default_value(false).help(
"Enable O1 optimize options");
arser.add_argument("--fold_add_v2")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fold AddV2 operators with constant inputs");
arser.add_argument("--fold_cast")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fold Cast operators with constant input");
arser.add_argument("--fold_dequantize")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fold dequantize op");
arser.add_argument("--fold_dwconv")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fold Depthwise Convolution operator with constant inputs");
arser.add_argument("--fold_gather")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fold Gather operator");
arser.add_argument("--fold_sparse_to_dense")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fold SparseToDense operator");
arser.add_argument("--forward_reshape_to_unaryop")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will move Reshape after UnaryOp for centain condition");
arser.add_argument("--fuse_activation_function")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse Activation function to a preceding operator");
arser.add_argument("--fuse_add_with_fully_connected")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse Add operator to FullyConnected operator");
arser.add_argument("--fuse_add_with_tconv")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse Add operator to Transposed Convolution operator");
arser.add_argument("--fuse_batchnorm_with_conv")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse BatchNorm operators to Convolution operator");
arser.add_argument("--fuse_batchnorm_with_dwconv")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse BatchNorm operators to Depthwise Convolution operator");
arser.add_argument("--fuse_batchnorm_with_tconv")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse BatchNorm operators to Transposed Convolution operator");
arser.add_argument("--fuse_bcq")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse operators and apply Binary Coded Quantization");
arser.add_argument("--fuse_instnorm")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse operators to InstanceNorm operator");
arser.add_argument("--fuse_mean_with_mean")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse two Mean operations when they follow one by one."
"This will fold them into one operation and merge reduction indices.");
arser.add_argument("--fuse_transpose_with_mean")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse Mean operation with a preceding Transpose under certain conditions.");
arser.add_argument("--make_batchnorm_gamma_positive")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will make negative gamma of BatchNorm into a small positive value (1e-10). Note "
"that this pass can change the execution result of the model. So, use it only when the "
arser.add_argument("--fuse_preactivation_batchnorm")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse BatchNorm operators of pre-activations to Convolution operator");
arser.add_argument("--remove_fakequant")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will remove FakeQuant operators");
arser.add_argument("--remove_quantdequant")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will remove Quantize-Dequantize sequence");
arser.add_argument("--remove_redundant_quantize")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will remove redundant Quantize operators");
arser.add_argument("--remove_redundant_reshape")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse or remove subsequent Reshape operators");
arser.add_argument("--remove_redundant_transpose")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will fuse or remove subsequent Transpose operators");
arser.add_argument("--remove_unnecessary_reshape")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will remove unnecessary reshape operators");
arser.add_argument("--remove_unnecessary_slice")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will remove unnecessary slice operators");
arser.add_argument("--remove_unnecessary_strided_slice")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will remove unnecessary strided slice operators");
arser.add_argument("--remove_unnecessary_split")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will remove unnecessary split operators");
arser.add_argument("--replace_cw_mul_add_with_depthwise_conv")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will replace channel-wise mul/add with DepthwiseConv2D operator");
arser.add_argument("--replace_sub_with_add")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will replace sub with add operator");
arser.add_argument("--resolve_customop_add")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert Custom(Add) to Add operator");
arser.add_argument("--resolve_customop_batchmatmul")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert Custom(BatchMatmul) to BatchMatmul operator");
arser.add_argument("--resolve_customop_matmul")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert Custom(Matmul) to Matmul operator");
arser.add_argument("--resolve_customop_max_pool_with_argmax")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert Custom(MaxPoolWithArgmax) to equivalent set of operators");
arser.add_argument("--shuffle_weight_to_16x1float32")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert weight format of FullyConnected to SHUFFLED16x1FLOAT32. Note that "
"it only converts weights whose row is a multiple of 16");
+ arser.add_argument("--replace_non_const_fc_with_batch_matmul")
+ .nargs(0)
+ .default_value(false)
+ .help("Replace FullyConnected with BatchMatMul when its weight is non-constant");
+
arser.add_argument("--substitute_pack_to_reshape")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert single input Pack to Reshape");
arser.add_argument("--substitute_padv2_to_pad")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert certain condition PadV2 to Pad");
arser.add_argument("--substitute_splitv_to_split")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert certain condition SplitV to Split operator");
arser.add_argument("--substitute_squeeze_to_reshape")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert certain condition Squeeze to Reshape");
arser.add_argument("--substitute_strided_slice_to_reshape")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert certain condition Strided_Slice to Reshape");
arser.add_argument("--substitute_transpose_to_reshape")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will convert single input Transpose to Reshape");
arser.add_argument("--expand_broadcast_const")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will expand broadcastable constant inputs");
arser.add_argument("--convert_nchw_to_nhwc")
.nargs(0)
- .required(false)
.default_value(false)
.help("Experimental: This will convert NCHW operators to NHWC under the assumption that "
"input model is NCHW.");
arser.add_argument("--nchw_to_nhwc_input_shape")
.nargs(0)
- .required(false)
.default_value(false)
.help("Convert the input shape of the model (argument for --convert_nchw_to_nhwc).");
arser.add_argument("--nchw_to_nhwc_output_shape")
.nargs(0)
- .required(false)
.default_value(false)
.help("Convert the output shape of the model (argument for --convert_nchw_to_nhwc).");
arser.add_argument("--transform_min_max_to_relu6")
.nargs(0)
- .required(false)
.default_value(false)
.help("Transform Minimum(6)-Maximum(0) pattern to Relu6 operator");
arser.add_argument("--transform_min_relu_to_relu6")
.nargs(0)
- .required(false)
.default_value(false)
.help("Transform Minimum(6)-Relu pattern to Relu6 operator");
arser.add_argument("--mute_warnings")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will turn off warning messages");
arser.add_argument("--disable_validation")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will turn off operator validations. May help input model investigation.");
arser.add_argument("--generate_profile_data")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will turn on profiling data generation.");
arser.add_argument("--change_outputs")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.help("Experimental: Change first subgraph output nodes to CSV names");
- arser.add_argument("input").nargs(1).type(arser::DataType::STR).help("Input circle model");
- arser.add_argument("output").nargs(1).type(arser::DataType::STR).help("Output circle model");
+ arser.add_argument("input").help("Input circle model");
+ arser.add_argument("output").help("Output circle model");
// sparsification argument
- arser.add_argument("--sparsify_tensor")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .help("Tensor name that you want to sparsify");
+ arser.add_argument("--sparsify_tensor").help("Tensor name that you want to sparsify");
arser.add_argument("--sparsify_traversal_order")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.default_value("0,1,2,3")
.help("Traversal order of dimensions. Default value: 0,1,2,3");
arser.add_argument("--sparsify_format")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.default_value("d,s")
.help("Format of each dimension. 'd' stands for dense, 's' stands for sparse(CSR). Default "
"value: d,s");
- arser.add_argument("--sparsify_block_size")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
- .help("Size of each block dimension");
+ arser.add_argument("--sparsify_block_size").help("Size of each block dimension");
arser.add_argument("--sparsify_block_map")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.default_value("0,1")
.help("Map from block dimension to the original tensor dimension. Default value: 0,1");
options->enable(Algorithms::ResolveCustomOpMaxPoolWithArgmax);
if (arser.get<bool>("--shuffle_weight_to_16x1float32"))
options->enable(Algorithms::ShuffleWeightTo16x1Float32);
+ if (arser.get<bool>("--replace_non_const_fc_with_batch_matmul"))
+ options->enable(Algorithms::ReplaceNonConstFCWithBatchMatMul);
if (arser.get<bool>("--substitute_pack_to_reshape"))
options->enable(Algorithms::SubstitutePackToReshape);
if (arser.get<bool>("--substitute_padv2_to_pad"))
csv_tokenize(csv_nodes, new_outputs);
}
- // Load model from the file
- foder::FileLoader file_loader{input_path};
- std::vector<char> model_data;
-
- try
- {
- model_data = file_loader.load();
- }
- catch (const std::runtime_error &err)
- {
- std::cerr << err.what() << std::endl;
- return EXIT_FAILURE;
- }
-
- flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
- if (!circle::VerifyModelBuffer(verifier))
- {
- std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
- return EXIT_FAILURE;
- }
-
- const circle::Model *circle_model = circle::GetModel(model_data.data());
- if (circle_model == nullptr)
- {
- std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
- return EXIT_FAILURE;
- }
-
// Import from input Circle file
- luci::Importer importer;
- auto module = importer.importModule(circle_model);
+ luci::ImporterEx importerex;
+ auto module = importerex.importVerifyModule(input_path);
+ if (module.get() == nullptr)
+ return EXIT_FAILURE;
if (change_outputs)
{
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("recipe")
- .type(arser::DataType::STR)
- .help("Source recipe file path to convert");
- arser.add_argument("circle").type(arser::DataType::STR).help("Target circle file path");
+ arser.add_argument("recipe").help("Source recipe file path to convert");
+ arser.add_argument("circle").help("Target circle file path");
try
{
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("circle")
- .type(arser::DataType::STR)
- .help("Source circle file path to convert");
- arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path");
+ arser.add_argument("circle").help("Source circle file path to convert");
+ arser.add_argument("recipe").help("Target recipe file path");
try
{
add_executable(circledump ${DRIVER} ${SOURCES})
target_include_directories(circledump PRIVATE include)
target_link_libraries(circledump arser)
+target_link_libraries(circledump foder)
target_link_libraries(circledump mio_circle04)
target_link_libraries(circledump mio_circle04_helper)
target_link_libraries(circledump safemain)
*/
#include <arser/arser.h>
-#include <circleread/Model.h>
+#include <foder/FileLoader.h>
#include <circledump/Dump.h>
#include <iostream>
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("circle").type(arser::DataType::STR).help("Circle file path to dump");
+ arser.add_argument("circle").help("Circle file path to dump");
try
{
std::string circle_path = arser.get<std::string>("circle");
// Load Circle model from a circle file
- std::unique_ptr<circleread::Model> model = circleread::load_circle(circle_path);
- if (model == nullptr)
- {
- std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl;
- return 255;
- }
-
- const circle::Model *circlemodel = model->model();
+ foder::FileLoader fileLoader{circle_path};
+ std::vector<char> modelData = fileLoader.load();
+ const circle::Model *circlemodel = circle::GetModel(modelData.data());
+ // const circle::Model *circlemodel = model->model();
if (circlemodel == nullptr)
{
std::cerr << "ERROR: Failed to load circle '" << circle_path << "'" << std::endl;
require("arser")
+require("foder")
require("mio-circle04")
require("safemain")
#include <circledump/Dump.h>
#include <mio_circle/Helper.h>
+#include <mio_circle/Reader.h>
-#include "Read.h"
#include "OpPrinter.h"
#include "MetadataPrinter.h"
return os;
}
-void dump_sub_graph(std::ostream &os, circleread::Reader &reader)
+void dump_sub_graph(std::ostream &os, mio::circle::Reader &reader)
{
auto tensors = reader.tensors();
auto operators = reader.operators();
std::vector<int32_t> dims = {-1};
if (tensor->shape())
- dims = circleread::as_index_vector(tensor->shape());
+ dims = mio::circle::as_index_vector(tensor->shape());
os << "T(" << reader.subgraph_index() << ":" << i << ") " << mio::circle::tensor_type(tensor)
<< " ";
os << "(" << dims << ") ";
if (tensor->shape_signature())
{
- std::vector<int32_t> dims_sig = circleread::as_index_vector(tensor->shape_signature());
+ std::vector<int32_t> dims_sig = mio::circle::as_index_vector(tensor->shape_signature());
os << "(" << dims_sig << ") ";
}
os << "B(" << tensor->buffer() << ") ";
const auto op = operators->Get(i);
circle::BuiltinOperator builtincode = reader.builtin_code(op);
- const std::vector<int32_t> &inputs = circleread::as_index_vector(op->inputs());
- const std::vector<int32_t> &outputs = circleread::as_index_vector(op->outputs());
+ const std::vector<int32_t> &inputs = mio::circle::as_index_vector(op->inputs());
+ const std::vector<int32_t> &outputs = mio::circle::as_index_vector(op->outputs());
auto op_name = reader.opcode_name(op);
os << "O(" << reader.subgraph_index() << ":" << i << ") " << op_name << " ";
void dump_model(std::ostream &os, const circle::Model *model)
{
- circleread::Reader reader(model);
+ mio::circle::Reader reader(model);
uint32_t num_subgraph = reader.num_subgraph();
+++ /dev/null
-/*
- * 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 <circleread/Model.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-namespace
-{
-
-class MemoryMappedModel final : public circleread::Model
-{
-public:
- /**
- * @require fd and data SHOULD be valid
- */
- explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size}
- {
- // DO NOTHING
- }
-
-public:
- ~MemoryMappedModel()
- {
- munmap(_data, _size);
- close(_fd);
- }
-
-public:
- MemoryMappedModel(const MemoryMappedModel &) = delete;
- MemoryMappedModel(MemoryMappedModel &&) = delete;
-
-public:
- const ::circle::Model *model(void) const override { return ::circle::GetModel(_data); }
-
-private:
- int _fd = -1;
- void *_data = nullptr;
- size_t _size = 0;
-};
-
-class FileDescriptor final
-{
-public:
- FileDescriptor(int value) : _value{value}
- {
- // DO NOTHING
- }
-
-public:
- // NOTE Copy is not allowed
- FileDescriptor(const FileDescriptor &) = delete;
-
-public:
- // NOTE Move is allowed
- FileDescriptor(FileDescriptor &&fd) { _value = fd.release(); }
-
-public:
- ~FileDescriptor()
- {
- if (_value != -1)
- {
- // Close on destructor
- close(_value);
- }
- }
-
-public:
- int value(void) const { return _value; }
-
-public:
- int release(void)
- {
- auto res = _value;
- _value = -1;
- return res;
- }
-
-private:
- int _value = -1;
-};
-
-} // namespace
-
-namespace circleread
-{
-
-std::unique_ptr<Model> load_circle(const std::string &path)
-{
- FileDescriptor fd = open(path.c_str(), O_RDONLY);
-
- if (fd.value() == -1)
- {
- // Return nullptr on open failure
- return nullptr;
- }
-
- struct stat st;
- if (fstat(fd.value(), &st) == -1)
- {
- // Return nullptr on fstat failure
- return nullptr;
- }
-
- auto size = st.st_size;
- auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.value(), 0);
-
- if (data == MAP_FAILED)
- {
- // Return nullptr on mmap failure
- return nullptr;
- }
-
- return std::unique_ptr<circleread::Model>{new MemoryMappedModel(fd.release(), data, size)};
-}
-
-} // namespace circleread
*/
#include "OpPrinter.h"
-#include "Read.h"
+
+#include <mio_circle/Helper.h>
#include <memory>
{
if (auto *reshape_params = op->builtin_options_as_ReshapeOptions())
{
- auto new_shape = circleread::as_index_vector(reshape_params->new_shape());
+ auto new_shape = mio::circle::as_index_vector(reshape_params->new_shape());
os << " ";
os << "NewShape(" << new_shape << ")";
os << std::endl;
+++ /dev/null
-/*
- * 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 "Read.h"
-
-#include <mio_circle/Helper.h>
-
-#include <sstream>
-#include <string>
-
-namespace circleread
-{
-
-Reader::Reader(const circle::Model *model)
-{
- _version = model->version();
- _subgraphs = model->subgraphs();
- _buffers = model->buffers();
- _metadata = model->metadata();
- _signature_defs = model->signature_defs();
-
- auto opcodes = model->operator_codes();
- for (const ::circle::OperatorCode *opcode : *opcodes)
- {
- _op_codes.push_back(opcode);
- }
-}
-
-size_t Reader::buffer_info(uint32_t buf_idx, const uint8_t **buff_data)
-{
- *buff_data = nullptr;
-
- if (buf_idx == 0)
- return 0;
-
- if (auto *buffer = (*_buffers)[buf_idx])
- {
- if (auto *array = buffer->data())
- {
- if (size_t size = array->size())
- {
- *buff_data = reinterpret_cast<const uint8_t *>(array->data());
- return size;
- }
- }
- }
-
- return 0;
-}
-
-circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const
-{
- uint32_t index = op->opcode_index();
- assert(index < _op_codes.size());
- const circle::OperatorCode *opcode = _op_codes.at(index);
-
- return opcode->builtin_code();
-}
-
-std::string Reader::opcode_name(const circle::Operator *op) const
-{
- uint32_t index = op->opcode_index();
- assert(index < _op_codes.size());
- const circle::OperatorCode *opcode = _op_codes.at(index);
-
- if (!mio::circle::is_valid(opcode))
- {
- std::ostringstream oss;
- oss << "(invalid: " << index << ")";
- return oss.str();
- }
-
- return mio::circle::opcode_name(opcode);
-}
-
-bool Reader::select_subgraph(uint32_t sgindex)
-{
- _subgraph_index = sgindex;
- _tensors = nullptr;
- _operators = nullptr;
-
- _inputs.clear();
- _outputs.clear();
-
- if (_subgraphs->Length() <= sgindex)
- {
- assert(false);
- return false;
- }
-
- const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
-
- auto name = subgraph->name();
- _subgraph_name = name ? name->c_str() : "(noname)";
-
- _tensors = subgraph->tensors();
- _operators = subgraph->operators();
- _data_format = subgraph->data_format();
-
- _inputs = as_index_vector(subgraph->inputs());
- _outputs = as_index_vector(subgraph->outputs());
-
- return true;
-}
-
-} // namespace circleread
nnas_find_package(GTest QUIET)
-GTest_AddTEst(cli_test ${TESTS})
+GTest_AddTest(cli_test ${TESTS})
target_link_libraries(cli_test cli)
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("circle").type(arser::DataType::STR).help("Circle file you want to test");
- arser.add_argument("--input_data")
- .required(true)
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Path to generate input data h5 file");
+ arser.add_argument("circle").help("Circle file you want to test");
+ arser.add_argument("--input_data").required(true).help("Path to generate input data h5 file");
arser.add_argument("--expected_data")
.required(true)
- .nargs(1)
- .type(arser::DataType::STR)
.help("Path to generate expected data h5 file");
arser.add_argument("--fixed_seed")
- .required(false)
.nargs(0)
.help("Put a fixed seed into the random number generator");
arser.add_argument("--input_range")
- .required(false)
.nargs(3)
.type(arser::DataType::STR_VEC)
.help("Set random number range [min max] for the input as 'name min max'");
{
using namespace loco;
- auto encoder = encode_node->encoder();
- assert(encoder != nullptr);
-
auto decode_node = dynamic_cast<loco::FeatureDecode *>(encode_node->input());
if (decode_node == nullptr)
{
}
assert(decode_node->input() != nullptr);
+ auto encoder = encode_node->encoder();
+ assert(encoder != nullptr);
+
auto decoder = decode_node->decoder();
assert(decoder != nullptr);
{
using namespace loco;
- auto encoder = encode_node->encoder();
- assert(encoder != nullptr);
-
auto decode_node = dynamic_cast<loco::MatrixDecode *>(encode_node->input());
if (decode_node == nullptr)
{
}
assert(decode_node->input() != nullptr);
+ auto encoder = encode_node->encoder();
+ assert(encoder != nullptr);
+
auto decoder = decode_node->decoder();
assert(decoder != nullptr);
* limitations under the License.
*/
-#include <luci/Importer.h>
+#include <luci/ImporterEx.h>
#include <luci_interpreter/Interpreter.h>
#include <luci/CircleExporter.h>
#include <luci/CircleFileExpContract.h>
}
}
-std::unique_ptr<luci::Module> importModel(const std::string &filename)
-{
- std::ifstream fs(filename, std::ifstream::binary);
- if (fs.fail())
- {
- throw std::runtime_error("Cannot open model file \"" + filename + "\".\n");
- }
- std::vector<char> model_data((std::istreambuf_iterator<char>(fs)),
- std::istreambuf_iterator<char>());
- return luci::Importer().importModule(circle::GetModel(model_data.data()));
-}
-
template <typename NodeT> size_t getTensorSize(const NodeT *node)
{
uint32_t tensor_size = loco::size(node->dtype());
const char *output_file = argv[4];
// Load model from the file
- std::unique_ptr<luci::Module> module = importModel(filename);
+ luci::ImporterEx importer;
+ std::unique_ptr<luci::Module> module = importer.importVerifyModule(filename);
if (module == nullptr)
{
std::cerr << "ERROR: Failed to load '" << filename << "'" << std::endl;
std::unique_ptr<Kernel> build_kernel_CircleAdd(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleAdd *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleAdd *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleArgMax(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleArgMax *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleArgMax *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->input());
const Tensor *axis = helper.getInputTensor(node->dimension());
std::unique_ptr<Kernel> build_kernel_CircleAveragePool2D(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleAveragePool2D *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleAveragePool2D *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->value());
std::unique_ptr<Kernel> build_kernel_CircleBatchMatMul(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleBatchMatMul *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleBatchMatMul *>(circle_node);
assert(node->arity() == 2);
const Tensor *lhs = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleBatchToSpaceND(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleBatchToSpaceND *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleBatchToSpaceND *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleCast(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleCast *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleCast *>(circle_node);
assert(node->arity() == 1);
std::unique_ptr<Kernel> build_kernel_CircleConcatenation(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleConcatenation *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleConcatenation *>(circle_node);
std::vector<const Tensor *> inputs(node->numValues());
for (uint32_t i = 0; i < node->numValues(); ++i)
{
std::unique_ptr<Kernel> build_kernel_CircleConv2D(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleConv2D *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleConv2D *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleDepthToSpace(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleDepthToSpace *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleDepthToSpace *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleDepthwiseConv2D(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleDepthwiseConv2D *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleDepthwiseConv2D *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleDequantize(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleDequantize *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleDequantize *>(circle_node);
const Tensor *input = helper.getInputTensor(node->input());
Tensor *output = helper.getOutputTensor(node);
std::unique_ptr<Kernel> build_kernel_CircleDiv(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleDiv *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleDiv *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
const Tensor *input2 = helper.getInputTensor(node->y());
std::unique_ptr<Kernel> build_kernel_CircleElu(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleElu *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleElu *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->features());
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleEqual *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleEqual *>(circle_node);
assert(node->arity() == 2);
const Tensor *x = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleExp(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleExp *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleExp *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleFloor(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleFloor *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleFloor *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleFloorDiv(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleFloorDiv *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleFloorDiv *>(circle_node);
assert(node->arity() == 2);
const Tensor *x = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleFullyConnected(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleFullyConnected *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleFullyConnected *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleGather(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleGather *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleGather *>(circle_node);
assert(node->arity() == 2);
const Tensor *params = helper.getInputTensor(node->params());
std::unique_ptr<Kernel> build_kernel_CircleGreater(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleGreater *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleGreater *>(circle_node);
assert(node->arity() == 2);
const Tensor *x = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleGreaterEqual(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleGreaterEqual *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleGreaterEqual *>(circle_node);
assert(node->arity() == 2);
const Tensor *x = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleIf(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleIf *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleIf *>(circle_node);
auto output_nodes = collectOutputNodes<luci::CircleIfOut>(node);
assert(node->arity() == 1 + node->input_count());
assert(output_nodes.size() == static_cast<size_t>(node->output_count()));
std::unique_ptr<Kernel> build_kernel_CircleInstanceNorm(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleInstanceNorm *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleInstanceNorm *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleL2Normalize(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleL2Normalize *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleL2Normalize *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleL2Pool2D(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleL2Pool2D *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleL2Pool2D *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->value());
std::unique_ptr<Kernel> build_kernel_CircleLeakyRelu(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLeakyRelu *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLeakyRelu *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->features());
Tensor *output = helper.getOutputTensor(node);
std::unique_ptr<Kernel> build_kernel_CircleLess(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLess *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLess *>(circle_node);
assert(node->arity() == 2);
const Tensor *x = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleLessEqual(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLessEqual *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLessEqual *>(circle_node);
assert(node->arity() == 2);
const Tensor *x = helper.getInputTensor(node->x());
build_kernel_CircleLocalResponseNormalization(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLocalResponseNormalization *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLocalResponseNormalization *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->input());
Tensor *output = helper.getOutputTensor(node);
std::unique_ptr<Kernel> build_kernel_CircleLogSoftmax(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLogSoftmax *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLogSoftmax *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->logits());
std::unique_ptr<Kernel> build_kernel_CircleLogicalAnd(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLogicalAnd *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLogicalAnd *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleLogicalNot(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLogicalNot *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLogicalNot *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleLogicalOr(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLogicalOr *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLogicalOr *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleLogistic(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleLogistic *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleLogistic *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleMaxPool2D(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleMaxPool2D *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleMaxPool2D *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->value());
std::unique_ptr<Kernel> build_kernel_CircleMaximum(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleMaximum *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleMaximum *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleMean(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleMean *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleMean *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleMinimum(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleMinimum *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleMinimum *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleMirrorPad(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleMirrorPad *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleMirrorPad *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleMul(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleMul *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleMul *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleNeg(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleNeg *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleNeg *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleNotEqual(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleNotEqual *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleNotEqual *>(circle_node);
assert(node->arity() == 2);
const Tensor *x = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CirclePRelu(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CirclePRelu *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CirclePRelu *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CirclePack(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CirclePack *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CirclePack *>(circle_node);
assert(node->arity() == node->values_count());
std::vector<const Tensor *> inputs(node->values_count());
std::unique_ptr<Kernel> build_kernel_CirclePad(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CirclePad *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CirclePad *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CirclePadV2(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CirclePadV2 *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CirclePadV2 *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CirclePow(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CirclePow *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CirclePow *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleQuantize(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleQuantize *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleQuantize *>(circle_node);
+ assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->input());
Tensor *output = helper.getOutputTensor(node);
std::unique_ptr<Kernel> build_kernel_CircleRelu(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleRelu *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleRelu *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->features());
std::unique_ptr<Kernel> build_kernel_CircleRelu6(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleRelu6 *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleRelu6 *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->features());
std::unique_ptr<Kernel> build_kernel_CircleReshape(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleReshape *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleReshape *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->tensor());
std::unique_ptr<Kernel> build_kernel_CircleResizeBilinear(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleResizeBilinear *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleResizeBilinear *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->input());
build_kernel_CircleResizeNearestNeighbor(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleResizeNearestNeighbor *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleResizeNearestNeighbor *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleReverseV2(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleReverseV2 *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleReverseV2 *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->tensor());
std::unique_ptr<Kernel> build_kernel_CircleRsqrt(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleRsqrt *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleRsqrt *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleSVDF(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSVDF *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSVDF *>(circle_node);
+ assert(node->arity() == 5);
const Tensor *input = helper.getInputTensor(node->input());
const Tensor *feature = helper.getInputTensor(node->weight_feature());
std::unique_ptr<Kernel> build_kernel_CircleSlice(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSlice *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSlice *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleSoftmax(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSoftmax *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSoftmax *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->logits());
std::unique_ptr<Kernel> build_kernel_CircleSpaceToBatchND(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSpaceToBatchND *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSpaceToBatchND *>(circle_node);
assert(node->arity() == 3);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleSpaceToDepth(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSpaceToDepth *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSpaceToDepth *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleSplit(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSplit *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSplit *>(circle_node);
auto output_nodes = collectOutputNodes<luci::CircleSplitOut>(node);
assert(node->arity() == 2);
assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
std::unique_ptr<Kernel> build_kernel_CircleSplitV(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSplitV *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSplitV *>(circle_node);
auto output_nodes = collectOutputNodes<luci::CircleSplitVOut>(node);
assert(node->arity() == 3);
assert(output_nodes.size() == static_cast<size_t>(node->num_split()));
std::unique_ptr<Kernel> build_kernel_CircleSqrt(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSqrt *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSqrt *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleSquare(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSquare *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSquare *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleSquaredDifference(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSquaredDifference *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSquaredDifference *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleSqueeze(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSqueeze *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSqueeze *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleStridedSlice(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleStridedSlice *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleStridedSlice *>(circle_node);
assert(node->arity() == 4);
const Tensor *input = helper.getInputTensor(node->input());
std::unique_ptr<Kernel> build_kernel_CircleSub(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleSub *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleSub *>(circle_node);
assert(node->arity() == 2);
const Tensor *input1 = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleTanh(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleTanh *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleTanh *>(circle_node);
assert(node->arity() == 1);
const Tensor *input = helper.getInputTensor(node->x());
std::unique_ptr<Kernel> build_kernel_CircleTranspose(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleTranspose *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleTranspose *>(circle_node);
assert(node->arity() == 2);
const Tensor *input = helper.getInputTensor(node->a());
std::unique_ptr<Kernel> build_kernel_CircleTransposeConv(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleTransposeConv *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleTransposeConv *>(circle_node);
assert(node->arity() == 4);
const Tensor *input_sizes = helper.getInputTensor(node->inputSizes());
std::unique_ptr<Kernel> build_kernel_CircleUnpack(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleUnpack *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleUnpack *>(circle_node);
auto output_nodes = collectOutputNodes<luci::CircleUnpackOut>(node);
assert(node->arity() == 1);
assert(output_nodes.size() == static_cast<size_t>(node->num()));
std::unique_ptr<Kernel> build_kernel_CircleWhile(const luci::CircleNode *circle_node,
KernelBuilderHelper &helper)
{
- const auto *node = dynamic_cast<const luci::CircleWhile *>(circle_node);
- if (node == nullptr)
- throw std::runtime_error("wrong builder for operation");
+ const auto *node = loco::must_cast<const luci::CircleWhile *>(circle_node);
auto output_nodes = collectOutputNodes<luci::CircleWhileOut>(node);
assert(node->arity() == node->input_count());
target_link_libraries(luci_import PRIVATE luci_logex)
target_link_libraries(luci_import PRIVATE nncc_common)
target_link_libraries(luci_import PRIVATE locop)
+target_link_libraries(luci_import PRIVATE foder)
target_link_libraries(luci_import PRIVATE oops)
target_link_libraries(luci_import PRIVATE mio_circle04_helper)
install(TARGETS luci_import DESTINATION lib)
/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef __TFLREAD_MODEL_H__
-#define __TFLREAD_MODEL_H__
+#ifndef __LUCI_IMPORTER_EX_H__
+#define __LUCI_IMPORTER_EX_H__
-#include <mio/tflite/schema_generated.h>
+#include "luci/IR/Module.h"
#include <memory>
+#include <string>
-namespace tflread
+namespace luci
{
-struct Model
+class ImporterEx final
{
- virtual ~Model() = default;
+public:
+ ImporterEx() = default;
- virtual const ::tflite::Model *model(void) const = 0;
+public:
+ std::unique_ptr<Module> importVerifyModule(const std::string &input_path) const;
};
-/**
- * @brief Load TensorFlow Lite model (as a raw Model) from a given path
- *
- * @note May return a nullptr
- */
-std::unique_ptr<Model> load_tflite(const std::string &path);
-
-} // namespace tflread
+} // namespace luci
-#endif // __TFLREAD_MODEL_H__
+#endif // __LUCI_IMPORTER_EX_H__
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Importer.h"
+#include "luci/ImporterEx.h"
+
+#include <foder/FileLoader.h>
+
+#include <memory>
+#include <iostream>
+
+namespace luci
+{
+
+std::unique_ptr<Module> ImporterEx::importVerifyModule(const std::string &input_path) const
+{
+ foder::FileLoader file_loader{input_path};
+ std::vector<char> model_data;
+
+ try
+ {
+ model_data = file_loader.load();
+ }
+ catch (const std::runtime_error &err)
+ {
+ std::cerr << err.what() << std::endl;
+ return nullptr;
+ }
+
+ flatbuffers::Verifier verifier{reinterpret_cast<uint8_t *>(model_data.data()), model_data.size()};
+ if (!circle::VerifyModelBuffer(verifier))
+ {
+ std::cerr << "ERROR: Invalid input file '" << input_path << "'" << std::endl;
+ return nullptr;
+ }
+
+ const circle::Model *circle_model = circle::GetModel(model_data.data());
+ if (circle_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load circle '" << input_path << "'" << std::endl;
+ return nullptr;
+ }
+
+ Importer importer;
+ return importer.importModule(circle_model);
+}
+
+} // namespace luci
ShuffleWeightTo16x1Float32,
RemoveRedundantTranspose,
ReplaceMulAddWithDepthwiseConv,
+ ReplaceNonConstFCWithBatchMatMul,
ReplaceSubWithAdd,
SubstitutePackToReshape,
SubstitutePadV2ToPad,
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef __CIRCLEREAD_MODEL_H__
-#define __CIRCLEREAD_MODEL_H__
+#ifndef __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__
+#define __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__
-#include <mio/circle/schema_generated.h>
+#include <logo/Pass.h>
-#include <memory>
-
-namespace circleread
+namespace luci
{
-struct Model
+/**
+ * @brief Class to replace "FC with non-const weight" with Batched MatMul
+ */
+struct ReplaceNonConstFCWithBatchMatMulPass final : public logo::Pass
{
- virtual ~Model() = default;
+ const char *name(void) const final { return "luci::ReplaceNonConstFCWithBatchMatMulPass"; }
- virtual const ::circle::Model *model(void) const = 0;
+ bool run(loco::Graph *g) final;
};
-/**
- * @brief Load Circle model (as a raw Model) from a given path
- *
- * @note May return a nullptr
- */
-std::unique_ptr<Model> load_circle(const std::string &path);
-
-} // namespace circleread
+} // namespace luci
-#endif // __CIRCLEREAD_MODEL_H__
+#endif // __LUCI_REPLACE_NONCONST_FC_WITH_BATCH_MATMUL_PASS_H__
#include "luci/Pass/RemoveUnnecessarySlicePass.h"
#include "luci/Pass/RemoveUnnecessaryStridedSlicePass.h"
#include "luci/Pass/RemoveUnnecessarySplitPass.h"
+#include "luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h"
#include "luci/Pass/ReplaceMulAddWithDepthwiseConvPass.h"
#include "luci/Pass/ReplaceSubWithAddPass.h"
#include "luci/Pass/ResolveCustomOpAddPass.h"
{
phase.emplace_back(std::make_unique<luci::RemoveRedundantQuantizePass>());
}
+ if (_options->query(Options::Algorithm::ReplaceNonConstFCWithBatchMatMul))
+ {
+ phase.emplace_back(std::make_unique<luci::ReplaceNonConstFCWithBatchMatMulPass>());
+ }
if (_options->query(Options::Algorithm::ReplaceMulAddWithDepthwiseConv))
{
phase.emplace_back(std::make_unique<luci::ReplaceMulAddWithDepthwiseConvPass>());
static const std::vector<std::string> qwmm_supported_input_model_dtype{"float32"};
static const std::vector<std::string> qwmm_supported_output_model_dtype{"uint8", "int16"};
static const std::vector<std::string> qwmm_supported_granularity{"layer", "channel"};
- static const std::vector<std::string> qwmm_supported_input_type{"uint8", "int16"};
- static const std::vector<std::string> qwmm_supported_output_type{"uint8", "int16"};
+ static const std::vector<std::string> qwmm_supported_input_type{"uint8", "int16", "float32"};
+ static const std::vector<std::string> qwmm_supported_output_type{"uint8", "int16", "float32"};
auto input_model_dtype =
_options->param(Options::AlgorithmParameters::Quantize_input_model_dtype);
// and dequantize the node
void visit(luci::CircleConv2D *node) { fq_activation(node); }
void visit(luci::CircleAdd *node) { fq_activation(node); }
+ void visit(luci::CircleAveragePool2D *node) { fq_activation(node); }
+ void visit(luci::CircleBatchMatMul *node) { fq_activation(node); }
+ // TODO Move Conv2D here
+ void visit(luci::CircleDepthwiseConv2D *node) { fq_activation(node); }
+ void visit(luci::CircleFullyConnected *node) { fq_activation(node); }
+ void visit(luci::CircleInstanceNorm *node) { fq_activation(node); }
+ void visit(luci::CircleLogistic *node) { fq_activation(node); }
+ void visit(luci::CircleMaxPool2D *node) { fq_activation(node); }
+ void visit(luci::CircleMul *node) { fq_activation(node); }
+ void visit(luci::CircleNeg *node) { fq_activation(node); }
+ void visit(luci::CirclePad *node) { fq_activation(node); }
+ void visit(luci::CirclePRelu *node) { fq_activation(node); }
+ void visit(luci::CircleMean *node) { fq_activation(node); }
+ void visit(luci::CircleRelu *node) { fq_activation(node); }
+ void visit(luci::CircleRelu6 *node) { fq_activation(node); }
+ void visit(luci::CircleResizeBilinear *node) { fq_activation(node); }
+ void visit(luci::CircleResizeNearestNeighbor *node) { fq_activation(node); }
+ void visit(luci::CircleSoftmax *node) { fq_activation(node); }
+ void visit(luci::CircleTanh *node) { fq_activation(node); }
+ void visit(luci::CircleTransposeConv *node) { fq_activation(node); }
};
#undef RETURN_UNLESS
}
private:
+ bool condition_common_1_5(uint32_t ifm_channel_depth);
+ bool condition_common_3_4();
+
+private:
template <enum PatternVersion> bool match();
public:
if (not(condition)) \
return false;
-template <> bool InstanceNormPattern::match<InstanceNormPattern::PatternVersion::Version_1>()
+bool InstanceNormPattern::condition_common_1_5(uint32_t ifm_channel_depth)
{
- CHECK_OR_FALSE(luci::fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal));
- CHECK_OR_FALSE(luci::fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm));
-
- auto ifm_circle = loco::must_cast<luci::CircleNode *>(ifm);
- CHECK_OR_FALSE(ifm_circle->shape_status() == luci::ShapeStatus::VALID);
- CHECK_OR_FALSE(ifm_circle->rank() == 4);
- CHECK_OR_FALSE(ifm_circle->dim(3).known());
- uint32_t ifm_channel_depth = ifm_circle->dim(3).value();
-
- CHECK_OR_FALSE(luci::fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma));
-
- CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth));
-
add_as_variance = dynamic_cast<luci::CircleAdd *>(rsqrt->x());
CHECK_OR_FALSE(add_as_variance);
CHECK_OR_FALSE(const_as_beta);
CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth));
+ return true;
+}
+
+bool InstanceNormPattern::condition_common_3_4()
+{
+ // check left sub
+ ifm = sub->x();
+ CHECK_OR_FALSE(ifm);
+
+ luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm);
+ CHECK_OR_FALSE(ifm_node->rank() == 4);
+ CHECK_OR_FALSE(ifm_node->dim(3).known());
+
+ mean_of_ifm = dynamic_cast<luci::CircleMean *>(sub->y());
+ CHECK_OR_FALSE(mean_of_ifm);
+ CHECK_OR_FALSE(ifm == mean_of_ifm->input());
+
+ // continue search from add_as_variance
+ CHECK_OR_FALSE(luci::fill(&sqrt, &const_as_epsilon).with_commutative_args_of(add_as_variance));
+ CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
+ // TODO Support regarding broadcast
+ CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
+
+ mean_as_variance = dynamic_cast<luci::CircleMean *>(sqrt->x());
+ CHECK_OR_FALSE(mean_as_variance);
+
+ square = dynamic_cast<luci::CircleSquare *>(mean_as_variance->input());
+ CHECK_OR_FALSE(square);
+
+ sub_2 = dynamic_cast<luci::CircleSub *>(square->x());
+ CHECK_OR_FALSE(sub_2);
+ CHECK_OR_FALSE(ifm == sub_2->x());
+
+ mean_of_ifm_2 = dynamic_cast<luci::CircleMean *>(sub_2->y());
+ CHECK_OR_FALSE(mean_of_ifm_2);
+ CHECK_OR_FALSE(ifm == mean_of_ifm_2->input());
+
+ loco::Node *ifm_should_be = nullptr;
+ luci::CircleMean *mean_of_ifm_2_should_be = nullptr;
+ CHECK_OR_FALSE(
+ luci::fill(&ifm_should_be, &mean_of_ifm_2_should_be).with_commutative_args_of(sub_2));
+ CHECK_OR_FALSE(ifm == ifm_should_be);
+ CHECK_OR_FALSE(mean_of_ifm_2 == mean_of_ifm_2_should_be);
+
+ return true;
+}
+
+template <> bool InstanceNormPattern::match<InstanceNormPattern::PatternVersion::Version_1>()
+{
+ CHECK_OR_FALSE(luci::fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal));
+ CHECK_OR_FALSE(luci::fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm));
+
+ auto ifm_circle = loco::must_cast<luci::CircleNode *>(ifm);
+ CHECK_OR_FALSE(ifm_circle->shape_status() == luci::ShapeStatus::VALID);
+ CHECK_OR_FALSE(ifm_circle->rank() == 4);
+ CHECK_OR_FALSE(ifm_circle->dim(3).known());
+ uint32_t ifm_channel_depth = ifm_circle->dim(3).value();
+
+ CHECK_OR_FALSE(luci::fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma));
+
+ CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth));
+
+ CHECK_OR_FALSE(condition_common_1_5(ifm_channel_depth));
+
luci::CircleMul *mul_gamma_should_be = nullptr;
luci::CircleMean *mean_of_ifm_should_be = nullptr;
CHECK_OR_FALSE(luci::fill(&div, &const_as_gamma).with_commutative_args_of(mul_gamma));
CHECK_OR_FALSE(luci::fill(&sub, &add_as_variance).with_commutative_args_of(div));
- // check left sub
- ifm = sub->x();
- CHECK_OR_FALSE(ifm);
-
- luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm);
- CHECK_OR_FALSE(ifm_node->rank() == 4);
- CHECK_OR_FALSE(ifm_node->dim(3).known());
-
- mean_of_ifm = dynamic_cast<luci::CircleMean *>(sub->y());
- CHECK_OR_FALSE(mean_of_ifm);
- CHECK_OR_FALSE(ifm == mean_of_ifm->input());
-
- // continue search from add_as_variance
- CHECK_OR_FALSE(luci::fill(&sqrt, &const_as_epsilon).with_commutative_args_of(add_as_variance));
- CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
- // TODO Support regarding broadcast
- CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
-
- mean_as_variance = dynamic_cast<luci::CircleMean *>(sqrt->x());
- CHECK_OR_FALSE(mean_as_variance);
-
- square = dynamic_cast<luci::CircleSquare *>(mean_as_variance->input());
- CHECK_OR_FALSE(square);
-
- sub_2 = dynamic_cast<luci::CircleSub *>(square->x());
- CHECK_OR_FALSE(sub_2);
- CHECK_OR_FALSE(ifm == sub_2->x());
-
- mean_of_ifm_2 = dynamic_cast<luci::CircleMean *>(sub_2->y());
- CHECK_OR_FALSE(mean_of_ifm_2);
- CHECK_OR_FALSE(ifm == mean_of_ifm_2->input());
-
- loco::Node *ifm_should_be = nullptr;
- luci::CircleMean *mean_of_ifm_2_should_be = nullptr;
- CHECK_OR_FALSE(
- luci::fill(&ifm_should_be, &mean_of_ifm_2_should_be).with_commutative_args_of(sub_2));
- CHECK_OR_FALSE(ifm == ifm_should_be);
- CHECK_OR_FALSE(mean_of_ifm_2 == mean_of_ifm_2_should_be);
+ CHECK_OR_FALSE(condition_common_3_4());
_matched = true;
return true;
CHECK_OR_FALSE(div);
CHECK_OR_FALSE(luci::fill(&sub, &add_as_variance).with_commutative_args_of(div));
- // check left sub
- ifm = sub->x();
- CHECK_OR_FALSE(ifm);
-
- luci::CircleNode *ifm_node = loco::must_cast<luci::CircleNode *>(ifm);
- CHECK_OR_FALSE(ifm_node->rank() == 4);
- CHECK_OR_FALSE(ifm_node->dim(3).known());
-
- mean_of_ifm = dynamic_cast<luci::CircleMean *>(sub->y());
- CHECK_OR_FALSE(mean_of_ifm);
- CHECK_OR_FALSE(ifm == mean_of_ifm->input());
-
- // continue search from add_as_variance
- CHECK_OR_FALSE(luci::fill(&sqrt, &const_as_epsilon).with_commutative_args_of(add_as_variance));
- CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
- // TODO Support regarding broadcast
- CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
-
- mean_as_variance = dynamic_cast<luci::CircleMean *>(sqrt->x());
- CHECK_OR_FALSE(mean_as_variance);
-
- square = dynamic_cast<luci::CircleSquare *>(mean_as_variance->input());
- CHECK_OR_FALSE(square);
-
- sub_2 = dynamic_cast<luci::CircleSub *>(square->x());
- CHECK_OR_FALSE(sub_2);
- CHECK_OR_FALSE(ifm == sub_2->x());
-
- mean_of_ifm_2 = dynamic_cast<luci::CircleMean *>(sub_2->y());
- CHECK_OR_FALSE(mean_of_ifm_2);
- CHECK_OR_FALSE(ifm == mean_of_ifm_2->input());
-
- loco::Node *ifm_should_be = nullptr;
- luci::CircleMean *mean_of_ifm_2_should_be = nullptr;
- CHECK_OR_FALSE(
- luci::fill(&ifm_should_be, &mean_of_ifm_2_should_be).with_commutative_args_of(sub_2));
- CHECK_OR_FALSE(ifm == ifm_should_be);
- CHECK_OR_FALSE(mean_of_ifm_2 == mean_of_ifm_2_should_be);
+ CHECK_OR_FALSE(condition_common_3_4());
assert(const_as_gamma == nullptr);
assert(const_as_beta == nullptr);
CHECK_OR_FALSE(ifm_circle->dim(3).known());
uint32_t ifm_channel_depth = ifm_circle->dim(3).value();
- add_as_variance = dynamic_cast<luci::CircleAdd *>(rsqrt->x());
- CHECK_OR_FALSE(add_as_variance);
-
- CHECK_OR_FALSE(
- luci::fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance));
-
- CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
- // TODO Support regarding broadcast
- CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
-
- CHECK_OR_FALSE(is_instance_mean_v1(mean_as_variance));
-
- sqdiff = dynamic_cast<luci::CircleSquaredDifference *>(mean_as_variance->input());
- CHECK_OR_FALSE(sqdiff);
-
- loco::Node *ifm_should_be = nullptr;
- CHECK_OR_FALSE(luci::fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff));
- CHECK_OR_FALSE(ifm == ifm_should_be);
- CHECK_OR_FALSE(is_instance_mean_v1(mean_of_ifm));
- CHECK_OR_FALSE(ifm == mean_of_ifm->input());
-
- const_as_beta = dynamic_cast<luci::CircleConst *>(sub->x());
- CHECK_OR_FALSE(const_as_beta);
- CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth));
+ CHECK_OR_FALSE(condition_common_1_5(ifm_channel_depth));
luci::CircleRsqrt *rsqrt_should_be = nullptr;
luci::CircleMean *mean_of_ifm_should_be = nullptr;
return quantize;
}
+// Create Dequantize Op whose shape is the same with node
+luci::CircleDequantize *create_dequantize(luci::CircleNode *node)
+{
+ auto dequantize = node->graph()->nodes()->create<luci::CircleDequantize>();
+ dequantize->name(node->name() + "_Dequantize");
+ dequantize->dtype(loco::DataType::FLOAT32);
+ dequantize->rank(node->rank());
+ for (uint32_t i = 0; i < node->rank(); i++)
+ dequantize->dim(i).set(node->dim(i).value());
+
+ dequantize->shape_status(luci::ShapeStatus::VALID);
+
+ luci::add_origin(dequantize, luci::get_origin(node));
+
+ return dequantize;
+}
+
} // namespace
namespace luci
luci::add_origin(quant_op, luci::get_origin(succ));
}
- // Requantize input
+ // Update qparam of input
+ // This step is skipped if input_type is float32
+ if (_ctx->input_type != loco::DataType::FLOAT32)
{
auto quantparam = input->quantparam();
assert(quantparam);
assert(_ctx->input_type == loco::DataType::S16);
compute_sym_scale_zp(min, max, scaling_factor, zp, nudged_min, nudged_max);
}
- input->dtype(_ctx->input_type);
input->quantparam()->scale[0] = scaling_factor;
input->quantparam()->zerop[0] = zp;
}
+ // Update dtype of input
+ input->dtype(_ctx->input_type);
+
auto graph_input = inputs->at(input->index());
graph_input->dtype(_ctx->input_type);
}
if (not from->quantparam())
continue;
- // Insert Quantize Op
+ // Insert Dequantize Op for float32 output_type
+ if (_ctx->output_type == loco::DataType::FLOAT32)
+ {
+ auto dequant_op = create_dequantize(from);
+ loco::replace(from).with(dequant_op);
+ dequant_op->input(from);
+ }
+ else
+ {
+ // clang-format off
+ // Insert Quantize Op for non-float32 output_type
auto quant_op = create_quantize_op(from, _ctx->output_type);
loco::replace(from).with(quant_op);
quant_op->input(from);
// TODO Set a proper origin (Quantize should have its own Origin)
luci::add_origin(quant_op, luci::get_origin(from));
+ // clang-format on
+ }
+
+ // Update dtype of output
+ output->dtype(_ctx->output_type);
auto graph_output = outputs->at(output->index());
graph_output->dtype(_ctx->output_type);
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/Profile/CircleNodeOrigin.h>
+#include <luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h>
+
+namespace
+{
+
+// TODO move to global helper list if needed
+/**
+ * @brief Create a node with `inp` as input from fused activation fucntion `act`
+ */
+luci::CircleNode *fromActivation(luci::CircleNode *inp, luci::FusedActFunc act)
+{
+ switch (act)
+ {
+ case luci::FusedActFunc::NONE:
+ return inp;
+ case luci::FusedActFunc::RELU:
+ {
+ auto n = inp->graph()->nodes()->create<luci::CircleRelu>();
+ n->features(inp);
+ return n;
+ }
+ case luci::FusedActFunc::RELU6:
+ {
+ auto n = inp->graph()->nodes()->create<luci::CircleRelu6>();
+ n->features(inp);
+ return n;
+ }
+ case luci::FusedActFunc::RELU_N1_TO_1:
+ {
+ auto n = inp->graph()->nodes()->create<luci::CircleReluN1To1>();
+ n->features(inp);
+ return n;
+ }
+ case luci::FusedActFunc::TANH:
+ {
+ auto n = inp->graph()->nodes()->create<luci::CircleTanh>();
+ n->x(inp);
+ return n;
+ }
+ case luci::FusedActFunc::SIGN_BIT:
+ {
+ throw std::invalid_argument("no matching node to create from fused activation");
+ }
+ default:
+ throw std::invalid_argument("invalid fused activation");
+ }
+}
+
+/**
+ * Replace Fully Connected with Batched MatMul
+ *
+ * BEFORE
+ *
+ * [Node1] [Node2]
+ * | |
+ * [transpose]? [transpose]?
+ * \ /
+ * [FullyConnected]
+ *
+ * AFTER
+ *
+ * [Node1] [Node2]
+ * \ /
+ * [BatchMatMul] [BiasValue]?
+ * \ /
+ * [Add]?
+ * |
+ * [Activation]?
+ *
+ * Nodes with "?" denote optional elements
+ */
+bool replace_fc_with_matmul(luci::CircleFullyConnected *fc)
+{
+ luci::CircleNode *x = nullptr;
+ luci::CircleNode *y = nullptr;
+ luci::CircleNode *b = nullptr;
+ luci::CircleTranspose *ty = nullptr;
+ luci::CircleTranspose *tx = nullptr;
+ bool adj_x = false;
+ bool adj_y = true;
+
+ if (dynamic_cast<luci::CircleConst *>(fc->weights()))
+ return false; // NonConst
+
+ if ((ty = dynamic_cast<luci::CircleTranspose *>(fc->weights()))) // is y a transpose?
+ {
+ adj_y = false;
+ if (dynamic_cast<luci::CircleConst *>(ty->a()))
+ return false;
+ else
+ y = loco::must_cast<luci::CircleNode *>(ty->a());
+ }
+ else
+ { // y is not transpose and not const
+ y = loco::must_cast<luci::CircleNode *>(fc->weights());
+ }
+ if ((tx = dynamic_cast<luci::CircleTranspose *>(fc->input())))
+ {
+ adj_x = true;
+ x = loco::must_cast<luci::CircleNode *>(tx->a());
+ }
+ else
+ {
+ x = loco::must_cast<luci::CircleNode *>(fc->input());
+ }
+
+ b = loco::must_cast<luci::CircleNode *>(fc->bias());
+
+ if (x->dtype() != loco::DataType::FLOAT32 || y->dtype() != loco::DataType::FLOAT32 ||
+ b->dtype() != loco::DataType::FLOAT32)
+ return false;
+
+ auto name = fc->name();
+ assert(name.length() > 0);
+
+ auto matmul = fc->graph()->nodes()->create<luci::CircleBatchMatMul>();
+ matmul->x(x);
+ matmul->y(y);
+ matmul->adj_x(adj_x);
+ matmul->adj_y(adj_y);
+ matmul->name(name);
+ matmul->dtype(fc->dtype());
+
+ luci::add_origin(matmul, luci::get_origin(fc));
+
+ auto all_zero = [](const luci::CircleConst *c) {
+ bool ac = true;
+ for (uint32_t i = 0; i < c->size<loco::DataType::FLOAT32>() && ac; i++)
+ {
+ ac &= c->at<loco::DataType::FLOAT32>(i) == 0.0f;
+ }
+ return ac;
+ };
+
+ auto bc = dynamic_cast<luci::CircleConst *>(b);
+ if ((nullptr != bc) && !all_zero(bc))
+ {
+ auto bias_add = fc->graph()->nodes()->create<luci::CircleAdd>();
+ bias_add->x(matmul);
+ bias_add->y(b);
+ bias_add->name(fc->name() + "/bias_add");
+ bias_add->dtype(fc->dtype());
+ add_origin(bias_add, get_origin(fc));
+ bias_add->fusedActivationFunction(fc->fusedActivationFunction());
+ loco::replace(fc).with(bias_add);
+ }
+ else
+ {
+ auto n = fromActivation(matmul, fc->fusedActivationFunction());
+ add_origin(n, luci::get_origin(fc));
+ n->name(fc->name() + "fusedActivation");
+ n->dtype(fc->dtype());
+ loco::replace(fc).with(n);
+ }
+
+ return true;
+}
+} // namespace
+
+namespace luci
+{
+
+bool ReplaceNonConstFCWithBatchMatMulPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ if (auto fc = dynamic_cast<luci::CircleFullyConnected *>(node))
+ {
+ if (replace_fc_with_matmul(fc))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+} // namespace luci
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "luci/Pass/ReplaceNonConstFCWithBatchMatMulPass.h"
+
+#include <luci/test/TestIOGraph.h>
+#include <luci/IR/CircleNodes.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+using namespace luci::test;
+
+// TODO Reduce duplicate codes in ResolveCustomOpMatMulPass.cpp
+template <typename T>
+luci::CircleConst *create_const_node(loco::Graph *g, const loco::DataType dtype,
+ const std::vector<uint32_t> &shape,
+ const std::vector<T> &values)
+{
+ auto node = g->nodes()->create<luci::CircleConst>();
+ node->dtype(dtype);
+ node->rank(shape.size());
+
+ uint32_t size = 1;
+ for (uint32_t i = 0; i < shape.size(); ++i)
+ {
+ node->dim(i) = shape.at(i);
+ size *= shape.at(i);
+ }
+ node->shape_status(luci::ShapeStatus::VALID);
+
+#define INIT_VALUES(DT) \
+ { \
+ node->size<DT>(size); \
+ for (uint32_t i = 0; i < values.size(); ++i) \
+ node->at<DT>(i) = values[i]; \
+ }
+
+ switch (dtype)
+ {
+ case loco::DataType::U8:
+ INIT_VALUES(loco::DataType::U8);
+ break;
+ case loco::DataType::S16:
+ INIT_VALUES(loco::DataType::S16);
+ break;
+ case loco::DataType::S32:
+ INIT_VALUES(loco::DataType::S32);
+ break;
+ case loco::DataType::FLOAT32:
+ INIT_VALUES(loco::DataType::FLOAT32)
+ break;
+ default:
+ INTERNAL_EXN("create_const_node called with unsupported type");
+ break;
+ }
+ return node;
+}
+
+/**
+ * Simple graph for test
+ *
+ * BEFORE
+ *
+ * [IFM1] [IFM2] [BIAS]
+ * \ | /
+ * [FC]
+ * |
+ * [Res]
+ *
+ * AFTER
+ * [IFM1] [IFM2]
+ * \ |
+ * [BatchMatMul] [BIAS]
+ * \ /
+ * [Add]
+ * |
+ * [Res]
+ *
+ */
+struct FCGraphlet
+{
+public:
+ FCGraphlet() = default;
+ virtual ~FCGraphlet() = default;
+
+ void init(loco::Graph *g, const ShapeU32 r_shape, const float bv)
+ {
+ _tr_y = g->nodes()->create<luci::CircleTranspose>();
+ _tr_y->a(_y);
+ std::vector<int32_t> tr_val = {1, 0};
+ _tr_y->perm(create_const_node(g, loco::DataType::S32, {2}, tr_val));
+
+ _fc = g->nodes()->create<luci::CircleFullyConnected>();
+ _fc->input(_x);
+ _fc->weights(_tr_y);
+ _fc->fusedActivationFunction(luci::FusedActFunc::NONE);
+ _fc->dtype(loco::DataType::FLOAT32);
+ _fc->shape(r_shape);
+ auto l = _fc->dim(_fc->rank() - 1).value();
+ std::vector<float> bias_val(l, bv);
+ _fc->bias(create_const_node(g, loco::DataType::FLOAT32, {l}, bias_val));
+ _fc->name("fc");
+ }
+
+public:
+ luci::CircleFullyConnected *fc() { return _fc; }
+
+protected:
+ luci::CircleFullyConnected *_fc = nullptr;
+ luci::CircleTranspose *_tr_y = nullptr;
+ luci::CircleInput *_x = nullptr;
+ luci::CircleInput *_y = nullptr;
+};
+
+struct FCGraph : public TestIsGraphlet<2>, public TestOGraphlet, public FCGraphlet
+{
+ FCGraph() = default;
+ virtual ~FCGraph() = default;
+ void init(const ShapeU32 x_shape, const ShapeU32 y_shape, const ShapeU32 r_shape, const float bv)
+ {
+ TestIsGraphlet<2>::init(g(), {x_shape, y_shape});
+ TestOGraphlet::init(g(), r_shape);
+ _x = input(0);
+ _y = input(1);
+ FCGraphlet::init(g(), r_shape, bv);
+ output()->from(_fc);
+ }
+};
+
+class ReplaceNonConstFCWithBatchMatMulPassTest : public ::testing::Test
+{
+public:
+ FCGraph g;
+ luci::ReplaceNonConstFCWithBatchMatMulPass pass;
+};
+
+} // namespace
+
+TEST_F(ReplaceNonConstFCWithBatchMatMulPassTest, simple_test)
+{
+ g.init({2, 3}, {2, 3}, {2, 2}, 0.0f);
+
+ auto ret = pass.run(g.g());
+ EXPECT_EQ(true, ret);
+
+ auto mm = dynamic_cast<luci::CircleBatchMatMul *>(g.output()->from());
+ EXPECT_NE(nullptr, mm);
+}
+
+TEST_F(ReplaceNonConstFCWithBatchMatMulPassTest, nonzero_bias_test)
+{
+ g.init({2, 3}, {2, 3}, {2, 2}, 1.0f);
+
+ auto ret = pass.run(g.g());
+ EXPECT_EQ(true, ret);
+
+ auto mm = dynamic_cast<luci::CircleAdd *>(g.output()->from());
+ EXPECT_NE(nullptr, mm);
+}
+
+TEST_F(ReplaceNonConstFCWithBatchMatMulPassTest, wrong_op_NEG)
+{
+ loco::Graph g;
+
+ auto inp = g.nodes()->create<luci::CircleInput>();
+ auto relu = g.nodes()->create<luci::CircleRelu>();
+ relu->features(inp);
+
+ luci::ReplaceNonConstFCWithBatchMatMulPass pass;
+ auto changed = pass.run(&g);
+
+ EXPECT_EQ(false, changed);
+}
require("hermes")
require("hermes-std")
require("tflchef")
+require("circlechef")
require("tflite2circle")
return loco::NodeShape{shape};
}
-loco::NodeShape infer_arg_max(const luci::CircleArgMax *node)
+template <class CIRCLENODE> loco::NodeShape infer_arg_maxmin(const CIRCLENODE *node)
{
- auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
- auto dimension_shape = luci::shape_get(node->dimension()).as<loco::TensorShape>();
-
- int64_t select_axis = 0;
- {
- LUCI_ASSERT(node->dimension(), "2nd input dimension() should not be nullptr");
-
- // Only support node's shape() is CircleConst with S32/S64
- // Support S32 for now.
- auto const_shape_node = loco::must_cast<luci::CircleConst *>(node->dimension());
- LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
- "Only support int32 CircleConst for CircleArgMax");
-
- if (const_shape_node->rank() > 1)
- INTERNAL_EXN_V("Only support rank 0/1 CircleConst",
- oops::to_uint32(const_shape_node->rank()));
-
- select_axis = const_shape_node->scalar<loco::DataType::S32>();
- }
- assert(select_axis < input_shape.rank());
- assert(select_axis >= 0); // TODO support minus of this breaks
-
- // NOTE select_axis is removed
- loco::TensorShape shape_output;
- uint32_t rank = input_shape.rank();
- uint32_t shrink = static_cast<uint32_t>(select_axis);
- assert(rank > 0);
- shape_output.rank(rank - 1);
- for (uint32_t r = 0, d = 0; r < rank; ++r)
- {
- if (r == shrink)
- continue;
- shape_output.dim(d++) = input_shape.dim(r);
- }
- return loco::NodeShape{shape_output};
-}
-
-loco::NodeShape infer_arg_min(const luci::CircleArgMin *node)
-{
- auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
- auto dimension_shape = luci::shape_get(node->dimension()).as<loco::TensorShape>();
+ auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
+ auto dimension_shape = luci::shape_get(node->dimension()).template as<loco::TensorShape>();
int64_t select_axis = 0;
{
// Support S32 for now.
auto const_shape_node = loco::must_cast<luci::CircleConst *>(node->dimension());
LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
- "Only support int32 CircleConst for CircleArgMin");
+ "Only support int32 CircleConst for CircleArgMax/CircleArgMin");
if (const_shape_node->rank() > 1)
INTERNAL_EXN_V("Only support rank 0/1 CircleConst",
oops::to_uint32(const_shape_node->rank()));
- select_axis = const_shape_node->scalar<loco::DataType::S32>();
+ select_axis = const_shape_node->template scalar<loco::DataType::S32>();
}
assert(select_axis < input_shape.rank());
assert(select_axis >= 0); // TODO support minus of this breaks
return loco::NodeShape{output_shape};
}
-loco::NodeShape infer_resize_bilinear(const luci::CircleResizeBilinear *node)
+template <class CIRCLENODE> loco::NodeShape infer_resize_type(const CIRCLENODE *node)
{
- auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
-
- if (input_shape.rank() != 4)
- INTERNAL_EXN("Expected ResizeBilinear input to have rank 4");
-
- auto *const_node = loco::must_cast<luci::CircleConst *>(node->size());
-
- if (const_node->dtype() != loco::DataType::S32)
- INTERNAL_EXN("Only S32 datatype is supported for ResizeBilinear size");
-
- if (const_node->rank() != 1)
- INTERNAL_EXN("Expected size tensor of rank 1");
-
- if (const_node->dim(0).value() != 2)
- INTERNAL_EXN("Expected size tensor with shape [2]");
-
- loco::TensorShape output_shape;
- output_shape.rank(4);
- output_shape.dim(0) = input_shape.dim(0);
- output_shape.dim(1) = const_node->at<loco::DataType::S32>(0);
- output_shape.dim(2) = const_node->at<loco::DataType::S32>(1);
- output_shape.dim(3) = input_shape.dim(3);
-
- return loco::NodeShape{output_shape};
-}
-
-loco::NodeShape infer_resize_nearest_neighbor(const luci::CircleResizeNearestNeighbor *node)
-{
- auto input_shape = luci::shape_get(node->input()).as<loco::TensorShape>();
+ auto input_shape = luci::shape_get(node->input()).template as<loco::TensorShape>();
if (input_shape.rank() != 4)
- INTERNAL_EXN("Expected ResizeNearesNeighbor input to have rank 4");
+ INTERNAL_EXN("Expected input to have rank 4");
auto *const_node = loco::must_cast<luci::CircleConst *>(node->size());
if (const_node->dtype() != loco::DataType::S32)
- INTERNAL_EXN("Only S32 datatype is supported for ResizeNearesNeighbor size");
+ INTERNAL_EXN("Only S32 datatype is supported for size");
if (const_node->rank() != 1)
INTERNAL_EXN("Expected size tensor of rank 1");
loco::TensorShape output_shape;
output_shape.rank(4);
output_shape.dim(0) = input_shape.dim(0);
- output_shape.dim(1) = const_node->at<loco::DataType::S32>(0);
- output_shape.dim(2) = const_node->at<loco::DataType::S32>(1);
+ output_shape.dim(1) = const_node->template at<loco::DataType::S32>(0);
+ output_shape.dim(2) = const_node->template at<loco::DataType::S32>(1);
output_shape.dim(3) = input_shape.dim(3);
return loco::NodeShape{output_shape};
loco::NodeShape visit(const luci::CircleAddN *node) final { return infer_add_n(node); }
- loco::NodeShape visit(const luci::CircleArgMax *node) final { return infer_arg_max(node); }
+ loco::NodeShape visit(const luci::CircleArgMax *node) final { return infer_arg_maxmin(node); }
- loco::NodeShape visit(const luci::CircleArgMin *node) final { return infer_arg_min(node); }
+ loco::NodeShape visit(const luci::CircleArgMin *node) final { return infer_arg_maxmin(node); }
loco::NodeShape visit(const luci::CircleAveragePool2D *node) final
{
loco::NodeShape visit(const luci::CircleResizeBilinear *node) final
{
- return infer_resize_bilinear(node);
+ return infer_resize_type(node);
}
loco::NodeShape visit(const luci::CircleResizeNearestNeighbor *node) final
{
- return infer_resize_nearest_neighbor(node);
+ return infer_resize_type(node);
}
loco::NodeShape visit(const luci::CircleReverseSequence *node) final
#include <mio/circle/schema_generated.h>
+#include <vector>
+
namespace mio
{
namespace circle
const char *tensor_type(const ::circle::Tensor *tensor);
const char *tensor_name(const ::circle::Tensor *tensor);
+template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+{
+ if (flat_array == nullptr)
+ {
+ throw std::runtime_error("flat array is nullptr");
+ }
+
+ std::vector<T> ret(flat_array->Length());
+ for (uint32_t i = 0; i < flat_array->Length(); i++)
+ {
+ ret[i] = flat_array->Get(i);
+ }
+ return ret;
+}
+
} // namespace circle
} // namespace mio
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef __CIRCLEREAD_READ_H__
-#define __CIRCLEREAD_READ_H__
+#ifndef __MIO_CIRCLE04_READER_H__
+#define __MIO_CIRCLE04_READER_H__
#include <mio/circle/schema_generated.h>
#include <string>
#include <vector>
-namespace circleread
-{
+// NOTE Reader class originated from circledump and for circle-tensordump
+// where this class has more work to be done for stability
+// as the tools are for developers not customores.
-template <typename T> std::vector<T> as_index_vector(const flatbuffers::Vector<T> *flat_array)
+namespace mio
+{
+namespace circle
{
- if (flat_array == nullptr)
- {
- throw std::runtime_error("flat array is nullptr");
- }
-
- std::vector<T> ret(flat_array->Length());
- for (uint32_t i = 0; i < flat_array->Length(); i++)
- {
- ret[i] = flat_array->Get(i);
- }
- return ret;
-}
/**
* @brief Loads Circle file and provides helpers to access attributes
class Reader
{
private:
- using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<circle::SubGraph>>;
- using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<circle::Buffer>>;
- using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<circle::Tensor>>;
- using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<circle::Operator>>;
- using CircleMetadata_t = flatbuffers::Vector<flatbuffers::Offset<circle::Metadata>>;
- using CircleSignatureDef_t = flatbuffers::Vector<flatbuffers::Offset<circle::SignatureDef>>;
+ using CircleSubGraphs_t = flatbuffers::Vector<flatbuffers::Offset<::circle::SubGraph>>;
+ using CircleBuffers_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Buffer>>;
+ using CircleTensors_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Tensor>>;
+ using CircleOperators_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Operator>>;
+ using CircleMetadata_t = flatbuffers::Vector<flatbuffers::Offset<::circle::Metadata>>;
+ using CircleSignatureDef_t = flatbuffers::Vector<flatbuffers::Offset<::circle::SignatureDef>>;
public:
- Reader(const circle::Model *model);
+ Reader(const ::circle::Model *model);
Reader() = delete;
public:
uint32_t version() const { return _version; }
- const std::vector<const circle::OperatorCode *> &opcodes() { return _op_codes; }
+ const std::vector<const ::circle::OperatorCode *> &opcodes() { return _op_codes; }
const CircleBuffers_t *buffers() { return _buffers; }
const CircleTensors_t *tensors() { return _tensors; }
const CircleOperators_t *operators() { return _operators; }
const std::vector<int32_t> &inputs() const { return _inputs; }
const std::vector<int32_t> &outputs() const { return _outputs; }
- const circle::DataFormat &data_format() const { return _data_format; }
+ const ::circle::DataFormat &data_format() const { return _data_format; }
const CircleMetadata_t *metadata() const { return _metadata; }
const CircleSignatureDef_t *signature_defs() const { return _signature_defs; }
uint32_t num_subgraph() const { return _subgraphs->Length(); }
size_t buffer_info(uint32_t buf_idx, const uint8_t **buff_data);
- circle::BuiltinOperator builtin_code(const circle::Operator *op) const;
- std::string opcode_name(const circle::Operator *op) const;
+ ::circle::BuiltinOperator builtin_code(const ::circle::Operator *op) const;
+ std::string opcode_name(const ::circle::Operator *op) const;
+ std::string tensor_name(const ::circle::Tensor *tensor) const;
+ std::string tensor_dtype(const ::circle::Tensor *tensor) const;
public:
bool select_subgraph(uint32_t subgraph);
uint32_t _subgraph_index = 0;
std::string _subgraph_name;
- std::vector<const circle::OperatorCode *> _op_codes;
+ std::vector<const ::circle::OperatorCode *> _op_codes;
std::vector<int32_t> _inputs;
std::vector<int32_t> _outputs;
- circle::DataFormat _data_format = circle::DataFormat::DataFormat_CHANNELS_FIRST;
+ ::circle::DataFormat _data_format = ::circle::DataFormat::DataFormat_CHANNELS_FIRST;
};
-} // namespace circleread
+} // namespace circle
+} // namespace mio
-#endif // __CIRCLEREAD_READ_H__
+#endif // __MIO_CIRCLE04_READER_H__
/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#include "Reader.h"
-
-#include <mio_circle/Helper.h>
+#include "mio_circle/Reader.h"
+#include "mio_circle/Helper.h"
#include <sstream>
#include <string>
-namespace circleinspect
+namespace mio
+{
+namespace circle
{
-Reader::Reader(const circle::Model *model)
+Reader::Reader(const ::circle::Model *model)
{
+ if (model == nullptr)
+ {
+ throw std::runtime_error("Invalid model");
+ }
+
+ _version = model->version();
_subgraphs = model->subgraphs();
_buffers = model->buffers();
+ _metadata = model->metadata();
+ _signature_defs = model->signature_defs();
auto opcodes = model->operator_codes();
for (const ::circle::OperatorCode *opcode : *opcodes)
return 0;
}
-circle::BuiltinOperator Reader::builtin_code(const circle::Operator *op) const
+::circle::BuiltinOperator Reader::builtin_code(const ::circle::Operator *op) const
{
uint32_t index = op->opcode_index();
assert(index < _op_codes.size());
- const circle::OperatorCode *opcode = _op_codes.at(index);
+ const ::circle::OperatorCode *opcode = _op_codes.at(index);
return mio::circle::builtin_code_neutral(opcode);
}
-std::string Reader::opcode_name(const circle::Operator *op) const
+std::string Reader::opcode_name(const ::circle::Operator *op) const
{
uint32_t index = op->opcode_index();
assert(index < _op_codes.size());
- const circle::OperatorCode *opcode = _op_codes.at(index);
+ const ::circle::OperatorCode *opcode = _op_codes.at(index);
if (!mio::circle::is_valid(opcode))
{
return mio::circle::opcode_name(opcode);
}
-std::string Reader::tensor_name(const circle::Tensor *tensor) const
+std::string Reader::tensor_name(const ::circle::Tensor *tensor) const
{
return mio::circle::tensor_name(tensor);
}
-std::string Reader::tensor_dtype(const circle::Tensor *tensor) const
+std::string Reader::tensor_dtype(const ::circle::Tensor *tensor) const
{
return mio::circle::tensor_type(tensor);
}
bool Reader::select_subgraph(uint32_t sgindex)
{
+ _subgraph_index = sgindex;
_tensors = nullptr;
_operators = nullptr;
return false;
}
- const circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
+ const ::circle::SubGraph *subgraph = (*_subgraphs)[sgindex];
+
+ auto name = subgraph->name();
+ _subgraph_name = name ? name->c_str() : "(noname)";
_tensors = subgraph->tensors();
_operators = subgraph->operators();
+ _data_format = subgraph->data_format();
_inputs = as_index_vector(subgraph->inputs());
_outputs = as_index_vector(subgraph->outputs());
return true;
}
-} // namespace circleinspect
+} // namespace circle
+} // namespace mio
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mio_circle/Reader.h"
+
+#include <flatbuffers/flatbuffers.h>
+#include <gtest/gtest.h>
+
+class mio_circle04_reader_test : public ::testing::Test
+{
+protected:
+ void initialization_emty(void)
+ {
+ _model = circle::CreateModelDirect(_fbb, 0, &_opcodes_vec);
+ circle::FinishModelBuffer(_fbb, _model);
+ }
+
+ const circle::Model *circleModel(void)
+ {
+ auto ptr = _fbb.GetBufferPointer();
+ return circle::GetModel(ptr);
+ }
+
+private:
+ flatbuffers::FlatBufferBuilder _fbb;
+ flatbuffers::Offset<circle::Model> _model;
+ std::vector<flatbuffers::Offset<circle::OperatorCode>> _opcodes_vec;
+};
+
+TEST_F(mio_circle04_reader_test, null_Model_NEG)
+{
+ EXPECT_THROW(mio::circle::Reader reader(nullptr), std::runtime_error);
+}
+
+TEST_F(mio_circle04_reader_test, empty_Model)
+{
+ initialization_emty();
+
+ const circle::Model *model = circleModel();
+ EXPECT_NE(nullptr, model);
+
+ mio::circle::Reader reader(model);
+
+ SUCCEED();
+}
+
+// TODO add more tests
'--input_type',
type=str,
help=
- 'data type of inputs of quantized model (supported: uint8, int16, default=quantized_dtype). QUANTIZE Op will be inserted at the beginning of the quantized model if input_type is different from quantized_dtype.'
+ 'data type of inputs of quantized model (supported: uint8, int16, float32, default=quantized_dtype). QUANTIZE Op will be inserted at the beginning of the quantized model if input_type is different from quantized_dtype.'
)
quantization_group.add_argument(
'--output_type',
type=str,
help=
- 'data type of outputs of quantized model (supported: uint8, int16, default=quantized_dtype). QUANTIZE Op will be inserted at the end of the quantized model if output_type is different from quantized_dtype.'
+ 'data type of outputs of quantized model (supported: uint8, int16, float32, default=quantized_dtype). QUANTIZE Op will be inserted at the end of the quantized model if output_type is different from quantized_dtype.'
)
quantization_group.add_argument(
'--min_percentile',
--input_dtype float32 \
--quantized_dtype int16 \
--granularity channel \
---input_type float32 \
+--input_type float64 \
--input_path ${inputfile} \
--output_path ${outputfile} > ${filename}.log 2>&1
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
unset(QUANTIZATION_VALUE_TEST)
unset(QUANTIZATION_VALUE_TEST_WITH_PARAM)
unset(QUANTIZATION_CONFIG_VALUE_TEST)
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
unset(RECORD_MINMAX_CONVERSION_TEST)
macro(addTest NAME)
arser::Arser arser(
"Embedding min/max values of activations to the circle model for post-training quantization");
- arser.add_argument("--version")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("Show version information and exit")
- .exit_with(print_version);
-
- arser.add_argument("-V", "--verbose")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("output additional information to stdout or stderr");
+ arser::Helper::add_version(arser, print_version);
+ arser::Helper::add_verbose(arser);
- arser.add_argument("--input_model")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(true)
- .help("Input model filepath");
+ arser.add_argument("--input_model").required(true).help("Input model filepath");
arser.add_argument("--input_data")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(false)
.help("Input data filepath. If not given, record-minmax will run with randomly generated data. "
"Note that the random dataset does not represent inference workload, leading to poor "
"model accuracy.");
- arser.add_argument("--output_model")
- .nargs(1)
- .type(arser::DataType::STR)
- .required(true)
- .help("Output model filepath");
+ arser.add_argument("--output_model").required(true).help("Output model filepath");
arser.add_argument("--min_percentile")
- .nargs(1)
.type(arser::DataType::FLOAT)
.help("Record n'th percentile of min");
arser.add_argument("--max_percentile")
- .nargs(1)
.type(arser::DataType::FLOAT)
.help("Record n'th percentile of max");
- arser.add_argument("--mode")
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Record mode. percentile (default) or moving_average");
+ arser.add_argument("--mode").help("Record mode. percentile (default) or moving_average");
arser.add_argument("--input_data_format")
- .nargs(1)
- .type(arser::DataType::STR)
.help("Input data format. h5/hdf5 (default) or list/filelist");
arser.add_argument("--generate_profile_data")
.nargs(0)
- .required(false)
.default_value(false)
.help("This will turn on profiling data generation.");
throw std::runtime_error("Failed to verify circle '" + input_model_path + "'");
}
- _module = luci::Importer().importModule(circle::GetModel(model_data.data()));
+ const circle::Model *circle_model = circle::GetModel(model_data.data());
+ if (circle_model == nullptr)
+ {
+ throw std::runtime_error("Failed to load '" + input_model_path + "'");
+ }
+
+ _module = luci::Importer().importModule(circle_model);
if (_module == nullptr)
{
# This "tf2circle_conversion_test_deps" target enforces CMake to generate all the dependencies during "build" phase
add_custom_target(tf2circle_conversion_test_deps ALL DEPENDS ${TEST_DEPS})
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
# Run tests
add_test(
NAME tf2circle_conversion_test
# Generate dependencies
add_custom_target(tf2circle_dredd_pb_deps ALL DEPENDS ${DEPS})
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
add_test(
NAME tf2circle_dredd_pb_test
COMMAND
# Generate dependencies
add_custom_target(tf2circle_dredd_pbtxt_deps ALL DEPENDS ${DEPS})
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
add_test(
NAME tf2circle_dredd_pbtxt_test
COMMAND
### Generate dependencies
add_custom_target(tf2circle_model_test_deps ALL DEPENDS ${DEPS})
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
# NOTE This target is not built by default
add_test(
NAME tf2circle_model_test
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
nnas_include(TargetRequire)
unset(REQUIRED_TARGETS)
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
nnas_include(TargetRequire)
unset(REQUIRED_TARGETS)
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
nnas_include(TargetRequire)
unset(REQUIRED_TARGETS)
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
nnas_include(TargetRequire)
unset(REQUIRED_TARGETS)
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
nncc_find_resource(TensorFlowTests)
#
-if(NOT TARGET mio_tflite)
+if(NOT TARGET mio_tflite280)
return()
-endif(NOT TARGET mio_tflite)
+endif(NOT TARGET mio_tflite280)
set(DRIVER "driver/Driver.cpp")
.nargs(0)
.help("Dump Conv2D series weight operators in tflite file");
arser.add_argument("--op_version").nargs(0).help("Dump versions of the operators in tflite file");
- arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file to inspect");
+ arser.add_argument("tflite").help("TFLite file to inspect");
try
{
-if(NOT TARGET mio_tflite)
+if(NOT TARGET mio_tflite280)
return()
-endif(NOT TARGET mio_tflite)
+endif(NOT TARGET mio_tflite280)
file(GLOB_RECURSE SOURCES "src/*.cpp")
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file path to verify");
+ arser.add_argument("tflite").help("TFLite file path to verify");
try
{
add_subdirectory(tflite)
# Tools
add_subdirectory(tools)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
add_subdirectory(tests)
add_library(tflchef_tflite STATIC ${SOURCES})
target_include_directories(tflchef_tflite PUBLIC include)
target_include_directories(tflchef_tflite PRIVATE src)
+target_include_directories(tflchef_tflite PRIVATE src/Op/include)
target_link_libraries(tflchef_tflite tflchef_proto)
target_link_libraries(tflchef_tflite mio_tflite280)
target_link_libraries(tflchef_tflite mio_tflite280_helper)
}
} // namespace tflchef
+
+// helpers of common codes for filling inputs
+namespace tflchef
+{
+
+void fill_two_inputs(const tflite::Operator *op, TFliteImport *import)
+{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+ assert(inputs.size() == 2);
+
+ fill_tensor_to_import(inputs[0], import);
+ fill_tensor_to_import(inputs[1], import);
+}
+
+} // namespace tflchef
} // namespace tflchef
+// helpers of common codes for filling inputs
+namespace tflchef
+{
+
+void fill_two_inputs(const tflite::Operator *op, TFliteImport *import);
+
+} // namespace tflchef
+
#endif // __FILLER_HELPER_H__
{
// Add may have constant input
- const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
- assert(inputs.size() == 2);
-
- fill_tensor_to_import(inputs[0], import);
- fill_tensor_to_import(inputs[1], import);
+ fill_two_inputs(op, import);
}
tflchef::Operation *TFliteOpAdd::build(const tflite::Operator *op, TFliteImport *import,
void TFliteOpMaximum::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
- const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
- assert(inputs.size() == 2);
-
- fill_tensor_to_import(inputs[0], import);
- fill_tensor_to_import(inputs[1], import);
+ fill_two_inputs(op, import);
}
tflchef::Operation *TFliteOpMaximum::build(const tflite::Operator *op, TFliteImport *import,
void TFliteOpMinimum::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
- const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
- assert(inputs.size() == 2);
-
- fill_tensor_to_import(inputs[0], import);
- fill_tensor_to_import(inputs[1], import);
+ fill_two_inputs(op, import);
}
tflchef::Operation *TFliteOpMinimum::build(const tflite::Operator *op, TFliteImport *import,
{
// Mul may have constant input
- const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
- assert(inputs.size() == 2);
-
- fill_tensor_to_import(inputs[0], import);
- fill_tensor_to_import(inputs[1], import);
+ fill_two_inputs(op, import);
}
tflchef::Operation *TFliteOpMul::build(const tflite::Operator *op, TFliteImport *import,
for (int32_t index = 2; index < 5; ++index)
{
- fill_tensor_to_import(index, import);
+ fill_tensor_to_import(inputs[index], import);
}
}
for (int32_t index = 2; index < 6; ++index)
{
- fill_tensor_to_import(index, import);
+ fill_tensor_to_import(inputs[index], import);
}
}
#include "PadV2.h"
+#include "Convert.h"
#include "FillerHelper.h"
namespace tflchef
void TFliteOpPadV2::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
// Filler for paddings and constant_values
- fill_tensor_to_import(1, import);
- fill_tensor_to_import(2, import);
+ fill_tensor_to_import(inputs[1], import);
+ fill_tensor_to_import(inputs[2], import);
}
tflchef::Operation *TFliteOpPadV2::build(const tflite::Operator *op, TFliteImport *import,
void TFliteOpScatterNd::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
// Filler for indices and shape
- fill_tensor_to_import(0, import);
- fill_tensor_to_import(2, import);
+ fill_tensor_to_import(inputs[0], import);
+ fill_tensor_to_import(inputs[2], import);
}
tflchef::Operation *TFliteOpScatterNd::build(const tflite::Operator *, TFliteImport *,
#include "SegmentSum.h"
+#include "Convert.h"
#include "FillerHelper.h"
namespace tflchef
void TFliteOpSegmentSum::filler(const tflite::Operator *op, TFliteImport *import,
tflchef::ModelRecipe *model_recipe) const
{
- // Filler for indices and shape
- fill_tensor_to_import(1, import);
+ const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
+
+ // Filler for segment_ids
+ fill_tensor_to_import(inputs[1], import);
}
tflchef::Operation *TFliteOpSegmentSum::build(const tflite::Operator *op, TFliteImport *import,
{
// Sub may have constant input
- const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
- assert(inputs.size() == 2);
-
- fill_tensor_to_import(inputs[0], import);
- fill_tensor_to_import(inputs[1], import);
+ fill_two_inputs(op, import);
}
tflchef::Operation *TFliteOpSub::build(const tflite::Operator *op, TFliteImport *import,
#define __TFLITE_OP_CHEFS_H__
// In alphabet order
-#include "Op/Abs.h"
-#include "Op/Add.h"
-#include "Op/AddN.h"
-#include "Op/ArgMax.h"
-#include "Op/ArgMin.h"
-#include "Op/AveragePool2D.h"
-#include "Op/BatchMatMul.h"
-#include "Op/BatchToSpaceND.h"
-#include "Op/BidirectionalSequenceLSTM.h"
-#include "Op/Cast.h"
-#include "Op/Ceil.h"
-#include "Op/Concatenation.h"
-#include "Op/Conv2D.h"
-#include "Op/Cos.h"
-#include "Op/DepthToSpace.h"
-#include "Op/DepthwiseConv2D.h"
-#include "Op/Dequantize.h"
-#include "Op/Div.h"
-#include "Op/ELU.h"
-#include "Op/Equal.h"
-#include "Op/Exp.h"
-#include "Op/ExpandDims.h"
-#include "Op/FakeQuant.h"
-#include "Op/Fill.h"
-#include "Op/Floor.h"
-#include "Op/FloorDiv.h"
-#include "Op/FloorMod.h"
-#include "Op/FullyConnected.h"
-#include "Op/Gather.h"
-#include "Op/GatherNd.h"
-#include "Op/Greater.h"
-#include "Op/GreaterEqual.h"
-#include "Op/L2Normalize.h"
-#include "Op/L2Pool2D.h"
-#include "Op/LeakyRelu.h"
-#include "Op/Less.h"
-#include "Op/LessEqual.h"
-#include "Op/LocalResponseNormalization.h"
-#include "Op/Log.h"
-#include "Op/LogicalAnd.h"
-#include "Op/LogicalNot.h"
-#include "Op/LogicalOr.h"
-#include "Op/Logistic.h"
-#include "Op/LogSoftmax.h"
-#include "Op/MatrixDiag.h"
-#include "Op/MatrixSetDiag.h"
-#include "Op/Maximum.h"
-#include "Op/MaxPool2D.h"
-#include "Op/Mean.h"
-#include "Op/Minimum.h"
-#include "Op/MirrorPad.h"
-#include "Op/Mul.h"
-#include "Op/Neg.h"
-#include "Op/NonMaxSuppressionV4.h"
-#include "Op/NonMaxSuppressionV5.h"
-#include "Op/NotEqual.h"
-#include "Op/OneHot.h"
-#include "Op/Pack.h"
-#include "Op/Pad.h"
-#include "Op/PadV2.h"
-#include "Op/Pow.h"
-#include "Op/PRelu.h"
-#include "Op/Quantize.h"
-#include "Op/Range.h"
-#include "Op/Rank.h"
-#include "Op/ReduceAny.h"
-#include "Op/ReduceMax.h"
-#include "Op/ReduceMin.h"
-#include "Op/ReduceProd.h"
-#include "Op/ReLU.h"
-#include "Op/ReLU6.h"
-#include "Op/ReLUN1To1.h"
-#include "Op/Reshape.h"
-#include "Op/ResizeBilinear.h"
-#include "Op/ResizeNearestNeighbor.h"
-#include "Op/ReverseSequence.h"
-#include "Op/ReverseV2.h"
-#include "Op/Round.h"
-#include "Op/Rsqrt.h"
-#include "Op/ScatterNd.h"
-#include "Op/SegmentSum.h"
-#include "Op/Select.h"
-#include "Op/SelectV2.h"
-#include "Op/Shape.h"
-#include "Op/Sin.h"
-#include "Op/Slice.h"
-#include "Op/Softmax.h"
-#include "Op/SpaceToBatchND.h"
-#include "Op/SpaceToDepth.h"
-#include "Op/SparseToDense.h"
-#include "Op/Split.h"
-#include "Op/SplitV.h"
-#include "Op/Sqrt.h"
-#include "Op/Square.h"
-#include "Op/SquaredDifference.h"
-#include "Op/Squeeze.h"
-#include "Op/StridedSlice.h"
-#include "Op/Sub.h"
-#include "Op/Sum.h"
-#include "Op/SVDF.h"
-#include "Op/Tanh.h"
-#include "Op/Tile.h"
-#include "Op/TopKV2.h"
-#include "Op/Transpose.h"
-#include "Op/TransposeConv.h"
-#include "Op/UnidirectionalSequenceLSTM.h"
-#include "Op/Unique.h"
-#include "Op/Unpack.h"
-#include "Op/Where.h"
-#include "Op/ZerosLike.h"
+#include "Op/include/Abs.h"
+#include "Op/include/Add.h"
+#include "Op/include/AddN.h"
+#include "Op/include/ArgMax.h"
+#include "Op/include/ArgMin.h"
+#include "Op/include/AveragePool2D.h"
+#include "Op/include/BatchMatMul.h"
+#include "Op/include/BatchToSpaceND.h"
+#include "Op/include/BidirectionalSequenceLSTM.h"
+#include "Op/include/Cast.h"
+#include "Op/include/Ceil.h"
+#include "Op/include/Concatenation.h"
+#include "Op/include/Conv2D.h"
+#include "Op/include/Cos.h"
+#include "Op/include/DepthToSpace.h"
+#include "Op/include/DepthwiseConv2D.h"
+#include "Op/include/Dequantize.h"
+#include "Op/include/Div.h"
+#include "Op/include/ELU.h"
+#include "Op/include/Equal.h"
+#include "Op/include/Exp.h"
+#include "Op/include/ExpandDims.h"
+#include "Op/include/FakeQuant.h"
+#include "Op/include/Fill.h"
+#include "Op/include/Floor.h"
+#include "Op/include/FloorDiv.h"
+#include "Op/include/FloorMod.h"
+#include "Op/include/FullyConnected.h"
+#include "Op/include/Gather.h"
+#include "Op/include/GatherNd.h"
+#include "Op/include/Greater.h"
+#include "Op/include/GreaterEqual.h"
+#include "Op/include/L2Normalize.h"
+#include "Op/include/L2Pool2D.h"
+#include "Op/include/LeakyRelu.h"
+#include "Op/include/Less.h"
+#include "Op/include/LessEqual.h"
+#include "Op/include/LocalResponseNormalization.h"
+#include "Op/include/Log.h"
+#include "Op/include/LogicalAnd.h"
+#include "Op/include/LogicalNot.h"
+#include "Op/include/LogicalOr.h"
+#include "Op/include/Logistic.h"
+#include "Op/include/LogSoftmax.h"
+#include "Op/include/MatrixDiag.h"
+#include "Op/include/MatrixSetDiag.h"
+#include "Op/include/Maximum.h"
+#include "Op/include/MaxPool2D.h"
+#include "Op/include/Mean.h"
+#include "Op/include/Minimum.h"
+#include "Op/include/MirrorPad.h"
+#include "Op/include/Mul.h"
+#include "Op/include/Neg.h"
+#include "Op/include/NonMaxSuppressionV4.h"
+#include "Op/include/NonMaxSuppressionV5.h"
+#include "Op/include/NotEqual.h"
+#include "Op/include/OneHot.h"
+#include "Op/include/Pack.h"
+#include "Op/include/Pad.h"
+#include "Op/include/PadV2.h"
+#include "Op/include/Pow.h"
+#include "Op/include/PRelu.h"
+#include "Op/include/Quantize.h"
+#include "Op/include/Range.h"
+#include "Op/include/Rank.h"
+#include "Op/include/ReduceAny.h"
+#include "Op/include/ReduceMax.h"
+#include "Op/include/ReduceMin.h"
+#include "Op/include/ReduceProd.h"
+#include "Op/include/ReLU.h"
+#include "Op/include/ReLU6.h"
+#include "Op/include/ReLUN1To1.h"
+#include "Op/include/Reshape.h"
+#include "Op/include/ResizeBilinear.h"
+#include "Op/include/ResizeNearestNeighbor.h"
+#include "Op/include/ReverseSequence.h"
+#include "Op/include/ReverseV2.h"
+#include "Op/include/Round.h"
+#include "Op/include/Rsqrt.h"
+#include "Op/include/ScatterNd.h"
+#include "Op/include/SegmentSum.h"
+#include "Op/include/Select.h"
+#include "Op/include/SelectV2.h"
+#include "Op/include/Shape.h"
+#include "Op/include/Sin.h"
+#include "Op/include/Slice.h"
+#include "Op/include/Softmax.h"
+#include "Op/include/SpaceToBatchND.h"
+#include "Op/include/SpaceToDepth.h"
+#include "Op/include/SparseToDense.h"
+#include "Op/include/Split.h"
+#include "Op/include/SplitV.h"
+#include "Op/include/Sqrt.h"
+#include "Op/include/Square.h"
+#include "Op/include/SquaredDifference.h"
+#include "Op/include/Squeeze.h"
+#include "Op/include/StridedSlice.h"
+#include "Op/include/Sub.h"
+#include "Op/include/Sum.h"
+#include "Op/include/SVDF.h"
+#include "Op/include/Tanh.h"
+#include "Op/include/Tile.h"
+#include "Op/include/TopKV2.h"
+#include "Op/include/Transpose.h"
+#include "Op/include/TransposeConv.h"
+#include "Op/include/UnidirectionalSequenceLSTM.h"
+#include "Op/include/Unique.h"
+#include "Op/include/Unpack.h"
+#include "Op/include/Where.h"
+#include "Op/include/ZerosLike.h"
#endif // __TFLITE_OP_CHEFS_H__
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("recipe")
- .type(arser::DataType::STR)
- .help("Source recipe file path to convert");
- arser.add_argument("tflite").type(arser::DataType::STR).help("Target tflite file path");
+ arser.add_argument("recipe").help("Source recipe file path to convert");
+ arser.add_argument("tflite").help("Target tflite file path");
try
{
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("tflite")
- .type(arser::DataType::STR)
- .help("Source tflite file path to convert");
- arser.add_argument("recipe").type(arser::DataType::STR).help("Target recipe file path");
+ arser.add_argument("tflite").help("Source tflite file path to convert");
+ arser.add_argument("recipe").help("Target recipe file path");
try
{
add_executable(tfldump ${DRIVER} ${SOURCES})
target_include_directories(tfldump PRIVATE include)
target_link_libraries(tfldump arser)
+target_link_libraries(tfldump foder)
target_link_libraries(tfldump mio_tflite280)
target_link_libraries(tfldump mio_tflite280_helper)
target_link_libraries(tfldump safemain)
*/
#include <arser/arser.h>
-#include <tflread/Model.h>
+#include <foder/FileLoader.h>
#include <tfldump/Dump.h>
#include <iostream>
int entry(int argc, char **argv)
{
arser::Arser arser;
- arser.add_argument("tflite").type(arser::DataType::STR).help("TFLite file to dump");
+ arser.add_argument("tflite").help("TFLite file to dump");
try
{
std::string tflite_path = arser.get<std::string>("tflite");
// Load TF lite model from a tflite file
- std::unique_ptr<tflread::Model> model = tflread::load_tflite(tflite_path);
- if (model == nullptr)
- {
- std::cerr << "ERROR: Failed to load tflite '" << tflite_path << "'" << std::endl;
- return 255;
- }
-
- const tflite::Model *tflmodel = model->model();
+ foder::FileLoader fileLoader{tflite_path};
+ std::vector<char> modelData = fileLoader.load();
+ const tflite::Model *tflmodel = tflite::GetModel(modelData.data());
if (tflmodel == nullptr)
{
std::cerr << "ERROR: Failed to load tflite '" << tflite_path << "'" << std::endl;
require("arser")
+require("foder")
require("mio-tflite280")
require("safemain")
+++ /dev/null
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <tflread/Model.h>
-
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-namespace
-{
-
-class MemoryMappedModel final : public tflread::Model
-{
-public:
- /**
- * @require fd and data SHOULD be valid
- */
- explicit MemoryMappedModel(int fd, void *data, size_t size) : _fd{fd}, _data{data}, _size{size}
- {
- // DO NOTHING
- }
-
-public:
- ~MemoryMappedModel()
- {
- munmap(_data, _size);
- close(_fd);
- }
-
-public:
- MemoryMappedModel(const MemoryMappedModel &) = delete;
- MemoryMappedModel(MemoryMappedModel &&) = delete;
-
-public:
- const ::tflite::Model *model(void) const override { return ::tflite::GetModel(_data); }
-
-private:
- int _fd = -1;
- void *_data = nullptr;
- size_t _size = 0;
-};
-
-class FileDescriptor final
-{
-public:
- FileDescriptor(int value) : _value{value}
- {
- // DO NOTHING
- }
-
-public:
- // NOTE Copy is not allowed
- FileDescriptor(const FileDescriptor &) = delete;
-
-public:
- // NOTE Move is allowed
- FileDescriptor(FileDescriptor &&fd) { _value = fd.release(); }
-
-public:
- ~FileDescriptor()
- {
- if (_value != -1)
- {
- // Close on destructor
- close(_value);
- }
- }
-
-public:
- int value(void) const { return _value; }
-
-public:
- int release(void)
- {
- auto res = _value;
- _value = -1;
- return res;
- }
-
-private:
- int _value = -1;
-};
-
-} // namespace
-
-namespace tflread
-{
-
-std::unique_ptr<Model> load_tflite(const std::string &path)
-{
- FileDescriptor fd = open(path.c_str(), O_RDONLY);
-
- if (fd.value() == -1)
- {
- // Return nullptr on open failure
- return nullptr;
- }
-
- struct stat st;
- if (fstat(fd.value(), &st) == -1)
- {
- // Return nullptr on fstat failure
- return nullptr;
- }
-
- auto size = st.st_size;
- auto data = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd.value(), 0);
-
- if (data == MAP_FAILED)
- {
- // Return nullptr on mmap failure
- return nullptr;
- }
-
- return std::unique_ptr<tflread::Model>{new MemoryMappedModel(fd.release(), data, size)};
-}
-
-} // namespace tflread
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
nnas_include(TargetRequire)
unset(REQUIRED_TARGETS)
{
arser::Arser arser{"tflite2circle is a Tensorflow lite to circle model converter"};
- arser.add_argument("--version")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("Show version information and exit")
- .exit_with(print_version);
-
- arser.add_argument("-V", "--verbose")
- .nargs(0)
- .required(false)
- .default_value(false)
- .help("output additional information to stdout or stderr");
-
- arser.add_argument("tflite")
- .nargs(1)
- .type(arser::DataType::STR)
- .help("Source tflite file path to convert");
- arser.add_argument("circle").nargs(1).type(arser::DataType::STR).help("Target circle file path");
+ arser::Helper::add_version(arser, print_version);
+ arser::Helper::add_verbose(arser);
+
+ arser.add_argument("tflite").help("Source tflite file path to convert");
+ arser.add_argument("circle").help("Target circle file path");
try
{
--- /dev/null
+.. ONE documentation master file, created by
+ sphinx-quickstart on Tue Apr 26 10:18:12 2022.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+1.20
+====
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ ./release-note-1.20.0.md
--- /dev/null
+# Release Note 1.20.0
+
+## ONE Compiler
+
+### Compiler Frontend
+
+- luci-interpreter supports multiple kernels with PAL layer including Cortext-M
+- luci-interpreter supports integer tensor for partly kernels
+- luci import support constant without coping to reduce memory for luci-interpreter
+- Reduce duplicate codes to package released modules
+- Limited support for ONNX LSTM/RNN unrolling while importing
+- Limited support for ARM32 cross build
+- Support new operator: SVDF
+- New virtual CircleVariable to support tensor with variable
+- Support quantization of BatchMatMul Op
+- Support mixed(UINT8 + INT16) quantization
+- Support backward propagation of quantization parameters
+- Upgrade default python to version 3.8
+- Support TensorFlow 2.8.0, ONNX-TF 1.10.0, ONNX 1.11.0
+- Upgrade circle schema to follow tflite schema v3b
+- Refactor to mio-tflite280, mio-circle04 with version and helpers methods
+- Use one flatbuffers 2.0 version
+- Drop support for TensorFlow 1.x
+- Fix for several bugs, performance enhancements, and typos
+
+## ONE Runtime
+
+### Introduce TRIX backend
+- TRIX backend supports trix binary with NHWC layout
+- TRIX backend supports trix binary with input/output of Q8 and Q16 type
+
+### API supports new data type
+- Symmetric Quantized int16 type named "NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED"
+
# Exclude *.test.cpp files from coverage report
# Exclude flatbuffer generated files from coverage report
"${LCOV_PATH}" -r "${EXTRACTED_COVERAGE_INFO_PATH}" -o "${EXCLUDED_COVERAGE_INFO_PATH}" \
- '*.test.cpp' '*_schema_generated.h' "${opencl_files[@]}"
+ '*.test.cpp' '*/test/*' '*_schema_generated.h' "${opencl_files[@]}"
# Final coverage data
cp -v ${EXCLUDED_COVERAGE_INFO_PATH} ${COVERAGE_INFO_PATH}
+one (1.20.0) bionic; urgency=medium
+
+ * luci-interpreter supports multiple kernels with PAL layer including Cortext-M
+ * luci-interpreter supports integer tensor for partly kernels
+ * luci import support constant without coping to reduce memory for luci-interpreter
+ * Reduce duplicate codes to package released modules
+ * Limited support for ONNX LSTM/RNN unrolling while importing
+ * Limited support for ARM32 cross build
+ * Support new operator: SVDF
+ * New virtual CircleVariable to support tensor with variable
+ * Support quantization of BatchMatMul Op
+ * Support mixed(UINT8 + INT16) quantization
+ * Support backward propagation of quantization parameters
+ * Upgrade default python to version 3.8
+ * Support TensorFlow 2.8.0, ONNX-TF 1.10.0, ONNX 1.11.0
+ * Upgrade circle schema to follow tflite schema v3b
+ * Refactor to mio-tflite280, mio-circle04 with version and helpers methods
+ * Use one flatbuffers 2.0 version
+ * Drop support for TensorFlow 1.x
+ * Fix for several bugs, performance enhancements, and typos
+
+ -- seongwoo <mhs4670go@naver.com> Tue, 26 Apr 2022 12:00:00 +0900
+
one (1.19.0) bionic; urgency=medium
* `circle-quantizer` supports input/output type option
+one (1.20.0) bionic; urgency=low
+
+ * Introduce TRIX backend
+ * API supports new data type NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED
+
+ -- Chunseok Lee <chunseok.lee@samsung.com> Wed, 26 Apr 2022 12:00:00 +0900
+
one (1.19.0) bionic; urgency=low
* Synch up version with ONE Compiler
# loco IR and related utilities
REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo")
# Flatbuffer I/O
- REQUIRED_UNITS+=("mio-tflite" "mio-tflite260" "mio-tflite280" "mio-circle04")
+ REQUIRED_UNITS+=("mio-tflite280" "mio-circle04")
# Data I/O
REQUIRED_UNITS+=("dio-hdf5")
# Circle compiler library (.circle -> .circle)
REQUIRED_UNITS+=("luci")
# Tools
- REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef" "circlechef")
+ REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef")
REQUIRED_UNITS+=("circle-tensordump" "circledump")
- REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter" "circle-verify")
+ REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter")
REQUIRED_UNITS+=("luci-eval-driver")
REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5")
REQUIRED_UNITS+=("circle-partitioner")
REQUIRED_UNITS+=("one-cmds")
REQUIRED_UNITS+=("bcq-tools")
+ # Dependent modules needed for build
+ REQUIRED_UNITS+=("circlechef")
+ REQUIRED_UNITS+=("circle-verify")
+
NPROC=${NPROC:-$(cat /proc/cpuinfo | grep -c processor)}
# TODO Use "nncc configure" and "nncc build"
# loco IR and related utilities
REQUIRED_UNITS+=("loco" "locop" "locomotiv" "logo-core" "logo")
# Flatbuffer I/O
- REQUIRED_UNITS+=("mio-tflite" "mio-tflite260" "mio-tflite280" "mio-circle04")
+ REQUIRED_UNITS+=("mio-tflite280" "mio-circle04")
# Data I/O
REQUIRED_UNITS+=("dio-hdf5")
# Circle compiler library (.circle -> .circle)
REQUIRED_UNITS+=("luci")
# Tools
- REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef" "circlechef")
- REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter" "circle-verify")
+ REQUIRED_UNITS+=("tflite2circle" "circle2circle" "tflchef")
+ REQUIRED_UNITS+=("circle-tensordump" "circledump")
+ REQUIRED_UNITS+=("tf2tfliteV2" "luci-interpreter")
REQUIRED_UNITS+=("luci-eval-driver")
REQUIRED_UNITS+=("record-minmax" "circle-quantizer" "rawdata2hdf5")
REQUIRED_UNITS+=("circle-partitioner")
REQUIRED_UNITS+=("one-cmds")
REQUIRED_UNITS+=("bcq-tools")
+ # Dependent modules needed for build
+ REQUIRED_UNITS+=("circlechef")
+ REQUIRED_UNITS+=("circle-verify")
+
NPROC=$(cat /proc/cpuinfo | grep -c processor)
# TODO Use "nncc configure" and "nncc build"
DEBUG_BUILD_ITEMS+=";hermes;hermes-std"
DEBUG_BUILD_ITEMS+=";loco;locop;locomotiv;logo-core;logo"
DEBUG_BUILD_ITEMS+=";foder;crew;souschef;arser;vconone"
-DEBUG_BUILD_ITEMS+=";safemain;mio-circle04;mio-tflite;mio-tflite260;mio-tflite280"
+DEBUG_BUILD_ITEMS+=";safemain;mio-circle04;mio-tflite280;dio-hdf5"
DEBUG_BUILD_ITEMS+=";tflite2circle"
DEBUG_BUILD_ITEMS+=";luci"
DEBUG_BUILD_ITEMS+=";luci-interpreter"
DEBUG_BUILD_ITEMS+=";tflite2circle-conversion-test"
DEBUG_BUILD_ITEMS+=";pota-quantization-value-test"
DEBUG_BUILD_ITEMS+=";circle-part-value-test"
+DEBUG_BUILD_ITEMS+=";circle-quantizer-dredd-recipe-test"
export DOCKER_ENV_VARS
export DOCKER_VOLUMES
export BUILD_OPTIONS
-# Disable nnpackage_run build: mismatch between buildtool for CI and installed hdf5
-CMD="export OPTIONS='-DBUILD_NNPACKAGE_RUN=OFF $BUILD_OPTIONS' && \
- export BUILD_TYPE=Release && \
+
+CMD="export BUILD_TYPE=Release && \
cp -nv Makefile.template Makefile && \
make all install build_test_suite"
./nnfw docker-run bash -c "$CMD"
# Run the test
export OP_BACKEND_Conv2D="cpu"
-export OP_BACKEND_MaxPool2D="acl_cl"
-export OP_BACKEND_AvgPool2D="acl_neon"
+export OP_BACKEND_Pool2D="acl_cl"
+export OP_BACKEND_FullyConnected="acl_neon"
export ACL_LAYOUT="NCHW"
export RUY_THREADS=4
NNAPIGTest "acl_cl;acl_neon;cpu" "Product/out/unittest/nnapi_gtest.skip.${TEST_ARCH}-${TEST_OS}.union" "report/mixed"
--- /dev/null
+operand {
+ name: "x"
+ type: FLOAT32
+ shape { dim: 2 dim: 4 }
+}
+operand {
+ name: "y"
+ type: FLOAT32
+ shape { dim: 2 dim: 4 }
+}
+operand {
+ name: "out"
+ type: FLOAT32
+ shape { dim: 2 dim: 2 }
+}
+operation {
+ type: "FullyConnected"
+ fullyconnected_options {
+ activation: NONE
+ keep_num_dims: true
+ }
+ input: "x"
+ input: "y"
+ input: ""
+ output: "out"
+}
+input: "x"
+input: "y"
+output: "out"
--- /dev/null
+# To check if FullyConnected with non-const weight is replaced by MatMul
+# with replace_non_const_fc_with_batch_matmul pass
+
+RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1
+
+RULE "BATCH_MATMUL_EXIST" $(op_count BATCH_MATMUL) '=' 1
+RULE "NO_FULLY_CONNECTED" $(op_count FULLY_CONNECTED) '=' 0
--- /dev/null
+operand {
+ name: "ifm"
+ type: FLOAT32
+ shape { dim: 1 dim: 64 dim: 64 dim: 32 }
+}
+operand {
+ name: "filter"
+ type: FLOAT32
+ shape { dim: 64 dim: 1 dim: 1 dim: 32 }
+ filler {
+ tag: "gaussian"
+ arg: "0.0"
+ arg: "1.0"
+ }
+}
+operand {
+ name: "bias"
+ type: FLOAT32
+ shape { dim: 64 }
+ filler {
+ tag: "gaussian"
+ arg: "0.0"
+ arg: "1.0"
+ }
+}
+operand {
+ name: "ofm"
+ type: FLOAT32
+ shape { dim: 1 dim: 32 dim: 32 dim: 64 }
+}
+operation {
+ type: "Conv2D"
+ conv2d_options {
+ padding: VALID
+ stride_w: 2
+ stride_h: 2
+ }
+ input: "ifm"
+ input: "filter"
+ input: "bias"
+ output: "ofm"
+}
+input: "ifm"
+output: "ofm"
--- /dev/null
+# To check float32 input.
+# Input is float32, Conv is uint8. Quantize Op is inserted at the beginning.
+
+RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1
+
+RULE "INPUT_FLOAT32" $(tensor_dtype ifm) '=' FLOAT32
+RULE "CONV_UINT8" $(tensor_dtype ofm) '=' UINT8
+RULE "WEIGHTS_UINT8" $(tensor_dtype filter) '=' UINT8
+RULE "BIAS_INT32" $(tensor_dtype bias) '=' INT32
+RULE "QUANTIZE_OP" $(op_count QUANTIZE) '=' 1
--- /dev/null
+operand {
+ name: "ifm"
+ type: FLOAT32
+ shape { dim: 1 dim: 64 dim: 64 dim: 32 }
+}
+operand {
+ name: "filter"
+ type: FLOAT32
+ shape { dim: 64 dim: 1 dim: 1 dim: 32 }
+ filler {
+ tag: "gaussian"
+ arg: "0.0"
+ arg: "1.0"
+ }
+}
+operand {
+ name: "bias"
+ type: FLOAT32
+ shape { dim: 64 }
+ filler {
+ tag: "gaussian"
+ arg: "0.0"
+ arg: "1.0"
+ }
+}
+operand {
+ name: "ofm"
+ type: FLOAT32
+ shape { dim: 1 dim: 32 dim: 32 dim: 64 }
+}
+operation {
+ type: "Conv2D"
+ conv2d_options {
+ padding: VALID
+ stride_w: 2
+ stride_h: 2
+ }
+ input: "ifm"
+ input: "filter"
+ input: "bias"
+ output: "ofm"
+}
+input: "ifm"
+output: "ofm"
--- /dev/null
+# To check float32 output.
+# Output is float32, Conv is uint8. Dequantize Op is inserted at the end.
+
+RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1
+
+# Update tensor name (ofm_Dequantize) if 'create_dequantize' function is changed.
+RULE "OUTPUT_FLOAT32" $(tensor_dtype ofm_Dequantize) '=' FLOAT32
+RULE "CONV_UINT8" $(tensor_dtype ofm) '=' UINT8
+RULE "WEIGHTS_UINT8" $(tensor_dtype filter) '=' UINT8
+RULE "BIAS_INT32" $(tensor_dtype bias) '=' INT32
+RULE "DEQUANTIZE_OP" $(op_count DEQUANTIZE) '=' 1
--- /dev/null
+operand {
+ name: "ifm"
+ type: FLOAT32
+ shape { dim: 1 dim: 64 dim: 64 dim: 32 }
+}
+operand {
+ name: "filter"
+ type: FLOAT32
+ shape { dim: 64 dim: 1 dim: 1 dim: 32 }
+ filler {
+ tag: "gaussian"
+ arg: "0.0"
+ arg: "1.0"
+ }
+}
+operand {
+ name: "bias"
+ type: FLOAT32
+ shape { dim: 64 }
+ filler {
+ tag: "gaussian"
+ arg: "0.0"
+ arg: "1.0"
+ }
+}
+operand {
+ name: "ofm"
+ type: FLOAT32
+ shape { dim: 1 dim: 32 dim: 32 dim: 64 }
+}
+operation {
+ type: "Conv2D"
+ conv2d_options {
+ padding: VALID
+ stride_w: 2
+ stride_h: 2
+ }
+ input: "ifm"
+ input: "filter"
+ input: "bias"
+ output: "ofm"
+}
+input: "ifm"
+output: "ofm"
--- /dev/null
+# To check float32 input/output.
+# Input/Output is float32, Conv is uint8.
+# Quantize Op is inserted at the beginning, Dequantize Op is inserted at the end.
+
+RULE "VERIFY_FILE_FORMAT" $(verify_file_format) '=' 1
+
+RULE "INPUT_FLOAT32" $(tensor_dtype ifm) '=' FLOAT32
+RULE "OUTPUT_FLOAT32" $(tensor_dtype ofm_Dequantize) '=' FLOAT32
+RULE "CONV_UINT8" $(tensor_dtype ofm) '=' UINT8
+RULE "WEIGHTS_UINT8" $(tensor_dtype filter) '=' UINT8
+RULE "BIAS_INT32" $(tensor_dtype bias) '=' INT32
+RULE "QUANTIZE_OP" $(op_count QUANTIZE) '=' 1
+RULE "DEQUANTIZE_OP" $(op_count DEQUANTIZE) '=' 1
target_link_libraries(nnfw_lib_misc PRIVATE nnfw_coverage)
add_executable(nnfw_tensor_index_iterator "examples/tensor_index_iterator.cpp")
-target_link_libraries(nnfw_tensor_index_iterator nnfw_lib_misc)
+target_link_libraries(nnfw_tensor_index_iterator PRIVATE nnfw_lib_misc)
+
+add_subdirectory(test)
--- /dev/null
+if(NOT TARGET nnfw_lib_misc)
+ return()
+endif()
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+add_executable(nnfw_index_enumerator_test IndexEnumerator.test.cpp)
+target_link_libraries(nnfw_index_enumerator_test PRIVATE nnfw_lib_misc)
+target_link_libraries(nnfw_index_enumerator_test PRIVATE nnfw_coverage)
+target_link_libraries(nnfw_index_enumerator_test PUBLIC gtest gtest_main ${LIB_PTHREAD})
+
+add_test(nnfw_index_enumerator_test nnfw_index_enumerator_test)
+install(TARGETS nnfw_index_enumerator_test DESTINATION unittest_standalone)
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "misc/tensor/IndexEnumerator.h"
+
+#include <vector>
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+using nnfw::misc::tensor::Shape;
+using nnfw::misc::tensor::Index;
+using nnfw::misc::tensor::IndexEnumerator;
+
+TEST(MiscIndexEnumeratorTest, iterate_full_range)
+{
+ const uint32_t H = 3;
+ const uint32_t W = 4;
+
+ const Shape shape{H, W};
+
+ std::vector<uint32_t> count;
+
+ count.resize(H * W, 0);
+
+ for (IndexEnumerator e{shape}; e.valid(); e.advance())
+ {
+ const auto &ind = e.curr();
+
+ ASSERT_EQ(2, ind.rank());
+ count.at(ind.at(0) * W + ind.at(1)) += 1;
+ }
+
+ ASSERT_TRUE(std::all_of(count.begin(), count.end(), [](uint32_t n) { return n == 1; }));
+}
+
+TEST(MiscIndexEnumeratorTest, neg_zero_rank_shape)
+{
+ // Test abnormal case of empty shape
+ // It is expected not to throw any exception, do nothing
+ const Shape shape{};
+ IndexEnumerator e{shape};
+ ASSERT_NO_THROW(e.valid());
+ ASSERT_NO_THROW(e.advance());
+ SUCCEED();
+}
target_link_libraries(ndarray_test PRIVATE ndarray)
+# Requires linking nnfw_coverage: check header coverage
+target_link_libraries(ndarray_test PRIVATE nnfw_coverage)
+
nnfw_find_package(GTest)
if(NOT GTest_FOUND)
message(STATUS "GTest not avaialble. Skipping NDArray test build")
{
float raw_data[] = {1, 2, 3, 4};
+ int32_t raw_data_int[] = {1, 2, 3, 4};
+ uint32_t raw_data_uint[] = {1, 2, 3, 4};
+ int8_t raw_data_int8[] = {1, 2, 3, 4};
Array<float> data22{raw_data, {2, 2}};
+ Array<int32_t> data22_int{raw_data_int, {2, 2}};
+ Array<uint32_t> data22_uint{raw_data_uint, {2, 2}};
+ Array<int8_t> data22_int8{raw_data_int8, {2, 2}};
ASSERT_FLOAT_EQ(data22.at(0, 0), 1);
ASSERT_FLOAT_EQ(data22.at(0, 1), 2);
ASSERT_EQ(data14.shape().dim(0), 1);
ASSERT_EQ(data14.shape().dim(1), 4);
- ContiguousSpan<float> cs = data22.flat();
- ASSERT_EQ(cs.size(), 4);
- ASSERT_FLOAT_EQ(cs.at(3), 4);
+ // <float, false>
+ {
+ ContiguousSpan<float> cs = data22.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<float> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+ float sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_EQ(sum, 10);
+
+ std::vector<float> array_data{1, 2, 3, 4};
+ auto cs3 = std::make_unique<ContiguousSpan<float>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_FLOAT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ ASSERT_FLOAT_EQ(cs4->at(3), 4);
+ }
+
+ // <float, true>
+ {
+ ContiguousSpan<float, true> cs = data22.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<float, true> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+ float sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_FLOAT_EQ(sum, 10);
+
+ std::vector<float> array_data{1, 2, 3, 4};
+ auto cs3 = std::make_unique<ContiguousSpan<float, true>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_FLOAT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ ASSERT_FLOAT_EQ(cs4->at(3), 4);
+ }
+
+ // <int32_t, false>
+ {
+ ContiguousSpan<int32_t> cs = data22_int.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<int32_t> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_EQ(cs2.at(3), 4);
+
+ int32_t sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_EQ(sum, 10);
+
+ std::vector<int32_t> array_data{1, 2, 3, 4};
+ auto cs3 = std::make_unique<ContiguousSpan<int32_t>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ ASSERT_EQ(cs4->at(3), 4);
+ }
+
+ // <int32_t, true>
+ {
+ ContiguousSpan<int32_t, true> cs = data22_int.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<int32_t, true> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_EQ(cs2.at(3), 4);
+
+ int32_t sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_EQ(sum, 10);
+
+ std::vector<int32_t> array_data{1, 2, 3, 4};
+ auto cs3 =
+ std::make_unique<ContiguousSpan<int32_t, true>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ ASSERT_EQ(cs4->at(3), 4);
+ }
+
+ // <uint32_t, false>
+ {
+ ContiguousSpan<uint32_t> cs = data22_uint.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<uint32_t> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_EQ(cs2.at(3), 4);
+
+ uint32_t sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_EQ(sum, 10);
+
+ std::vector<uint32_t> array_data{1, 2, 3, 4};
+ auto cs3 = std::make_unique<ContiguousSpan<uint32_t>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ }
+
+ // <uint32_t, true>
+ {
+ ContiguousSpan<uint32_t, true> cs = data22_uint.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<uint32_t, true> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_EQ(cs2.at(3), 4);
+
+ uint32_t sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_EQ(sum, 10);
+
+ std::vector<uint32_t> array_data{1, 2, 3, 4};
+ auto cs3 =
+ std::make_unique<ContiguousSpan<uint32_t, true>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ ASSERT_EQ(cs4->at(3), 4);
+ }
+
+ // <int8_t, false>
+ {
+ ContiguousSpan<int8_t> cs = data22_int8.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<int8_t> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+ int8_t sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_EQ(sum, 10);
+
+ std::vector<int8_t> array_data{1, 2, 3, 4};
+ auto cs3 = std::make_unique<ContiguousSpan<int8_t>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ ASSERT_EQ(cs4->at(3), 4);
+
+ auto cs5 = ContiguousSpan<int8_t>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs5.size(), 4);
+ ASSERT_EQ(cs5.at(3), 4);
+ }
+
+ // <int8_t, true>
+ {
+ ContiguousSpan<int8_t, true> cs = data22_int8.flat();
+ ASSERT_EQ(cs.size(), 4);
+ ASSERT_FLOAT_EQ(cs.at(3), 4);
+
+ ContiguousSpan<int8_t, true> cs2 = std::move(cs);
+ ASSERT_EQ(cs2.size(), 4);
+ ASSERT_FLOAT_EQ(cs2.at(3), 4);
+
+ int8_t sum = 0;
+ for (auto it = cs2.begin(); it < cs2.end(); it++)
+ {
+ sum += *it;
+ }
+ ASSERT_EQ(sum, 10);
+
+ std::vector<int8_t> array_data{1, 2, 3, 4};
+ auto cs3 = std::make_unique<ContiguousSpan<int8_t, true>>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs3->size(), 4);
+ ASSERT_EQ(cs3->at(3), 4);
+
+ auto cs4 = std::move(cs3);
+ ASSERT_EQ(cs3, nullptr);
+ ASSERT_EQ(cs4->size(), 4);
+ ASSERT_EQ(cs4->at(3), 4);
+
+ auto cs5 = ContiguousSpan<int8_t, true>(array_data.begin(), array_data.end());
+ ASSERT_EQ(cs5.size(), 4);
+ ASSERT_EQ(cs5.at(3), 4);
+ }
Array<float> lv = std::move(data14);
ASSERT_FLOAT_EQ(lv.at(0, 0), 1);
TEST(NDArray_tests, slice_write_test)
{
- float raw_data[4] = {0};
+ // float
+ {
+ float raw_data[4] = {0};
- Array<float> data22{raw_data, {2, 2}};
+ Array<float> data22{raw_data, {2, 2}};
+
+ data22.slice(1) = {1, 2};
+
+ ASSERT_FLOAT_EQ(data22.at(0, 0), 0);
+ ASSERT_FLOAT_EQ(data22.at(0, 1), 0);
+ ASSERT_FLOAT_EQ(data22.at(1, 0), 1);
+ ASSERT_FLOAT_EQ(data22.at(1, 1), 2);
+ }
+
+ // int32_t
+ {
+ int32_t raw_data[4] = {0};
+ Array<int32_t> data22{raw_data, {2, 2}};
+
+ data22.slice(1) = {1, 2};
- data22.slice(1) = {1, 2};
+ ASSERT_EQ(data22.at(0, 0), 0);
+ ASSERT_EQ(data22.at(0, 1), 0);
+ ASSERT_EQ(data22.at(1, 0), 1);
+ ASSERT_EQ(data22.at(1, 1), 2);
+ }
- ASSERT_FLOAT_EQ(data22.at(0, 0), 0);
- ASSERT_FLOAT_EQ(data22.at(0, 1), 0);
- ASSERT_FLOAT_EQ(data22.at(1, 0), 1);
- ASSERT_FLOAT_EQ(data22.at(1, 1), 2);
+ // uint32_t
+ {
+ uint32_t raw_data[4] = {0};
+ Array<uint32_t> data22{raw_data, {2, 2}};
+
+ data22.slice(1) = {1, 2};
+
+ ASSERT_EQ(data22.at(0, 0), 0);
+ ASSERT_EQ(data22.at(0, 1), 0);
+ ASSERT_EQ(data22.at(1, 0), 1);
+ ASSERT_EQ(data22.at(1, 1), 2);
+ }
+
+ // int8_t
+ {
+ int8_t raw_data[4] = {0};
+ Array<int8_t> data22{raw_data, {2, 2}};
+
+ data22.slice(1) = {1, 2};
+
+ ASSERT_EQ(data22.at(0, 0), 0);
+ ASSERT_EQ(data22.at(0, 1), 0);
+ ASSERT_EQ(data22.at(1, 0), 1);
+ ASSERT_EQ(data22.at(1, 1), 2);
+ }
}
TEST(NDArray_tests, slice_read_test)
{
- float raw_data[4] = {1, 2, 3, 4};
+ // float
+ {
+ float raw_data[4] = {1, 2, 3, 4};
- Array<float> data22{raw_data, {2, 2}};
+ Array<float> data22{raw_data, {2, 2}};
- auto slice = data22.slice(1);
+ auto slice = data22.slice(1);
- ASSERT_FLOAT_EQ(slice[0], 3);
- ASSERT_FLOAT_EQ(slice[1], 4);
+ ASSERT_FLOAT_EQ(slice[0], 3);
+ ASSERT_FLOAT_EQ(slice[1], 4);
+ }
+
+ // int32_t
+ {
+ int32_t raw_data[4] = {1, 2, 3, 4};
+
+ Array<int32_t> data22{raw_data, {2, 2}};
+
+ auto slice = data22.slice(1);
+
+ ASSERT_EQ(slice[0], 3);
+ ASSERT_EQ(slice[1], 4);
+ }
+
+ // uint32_t
+ {
+ uint32_t raw_data[4] = {1, 2, 3, 4};
+
+ Array<uint32_t> data22{raw_data, {2, 2}};
+
+ auto slice = data22.slice(1);
+
+ ASSERT_EQ(slice[0], 3);
+ ASSERT_EQ(slice[1], 4);
+ }
+
+ // int8_t
+ {
+ int8_t raw_data[4] = {1, 2, 3, 4};
+
+ Array<int8_t> data22{raw_data, {2, 2}};
+
+ auto slice = data22.slice(1);
+
+ ASSERT_EQ(slice[0], 3);
+ ASSERT_EQ(slice[1], 4);
+ }
}
TEST(NDArray_tests, multidim_test)
{
- float raw_data[5] = {0, 1, 2, 3, 4};
+ // float
+ {
+ float raw_data[5] = {0, 1, 2, 3, 4};
+
+ Array<float> data22{raw_data, {1, 1, 1, 1, 5}};
- Array<float> data22{raw_data, {1, 1, 1, 1, 5}};
+ ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+ ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+ ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+ ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+ ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+ }
- ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 0), 0);
- ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 1), 1);
- ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 2), 2);
- ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 3), 3);
- ASSERT_FLOAT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+ // int32_t
+ {
+ int32_t raw_data[5] = {0, 1, 2, 3, 4};
+
+ Array<int32_t> data22{raw_data, {1, 1, 1, 1, 5}};
+
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+ }
+
+ // uint32_t
+ {
+ uint32_t raw_data[5] = {0, 1, 2, 3, 4};
+
+ Array<uint32_t> data22{raw_data, {1, 1, 1, 1, 5}};
+
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+ }
+
+ // int8_t
+ {
+ int8_t raw_data[5] = {0, 1, 2, 3, 4};
+
+ Array<int8_t> data22{raw_data, {1, 1, 1, 1, 5}};
+
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 0), 0);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 1), 1);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 2), 2);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 3), 3);
+ ASSERT_EQ(data22.at(0, 0, 0, 0, 4), 4);
+ }
}
TEST(NDArray_tests, slice_assign_test)
{
- std::vector<float> v1{1, 2, 3, 4, 5};
- std::vector<float> v2(5);
+ // float
+ {
+ std::vector<float> v1{1, 2, 3, 4, 5};
+ std::vector<float> v2(5);
+
+ ContiguousSpan<float> span1(v1.begin(), v1.end());
+ ContiguousSpan<float> span2(v2.begin(), v2.end());
+
+ span2.assign(span1);
+
+ ASSERT_EQ(v1, v2);
+ ASSERT_EQ(span1.size(), 5);
+ ASSERT_EQ(span2.size(), 5);
+
+ ASSERT_EQ(span2.at(2), 3);
+ ASSERT_EQ(span2.at(4), 5);
+
+ ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+
+ ContiguousSpan<float> span3(span2.offset(1));
+ ASSERT_EQ(span3.size(), 4);
+ ASSERT_EQ(span3.at(0), 2);
+ ASSERT_EQ(span3.at(1), 3);
+ ASSERT_EQ(span3[2], 4);
+ ASSERT_EQ(span3[3], 5);
+
+ // const
+ ContiguousSpan<float, true> span4(v1.begin(), v1.end());
+ ASSERT_EQ(span4.size(), 5);
+ ASSERT_EQ(span4.at(0), 1);
+ ASSERT_EQ(span4.at(1), 2);
+ ASSERT_EQ(span4.at(2), 3);
+ ASSERT_EQ(span4[3], 4);
+ ASSERT_EQ(span4[4], 5);
+
+ ContiguousSpan<float, true> span5(span4.offset(1));
+ ASSERT_EQ(span5.size(), 4);
+ ASSERT_EQ(span5.at(0), 2);
+ ASSERT_EQ(span5.at(1), 3);
+ ASSERT_EQ(span5[2], 4);
+ ASSERT_EQ(span5[3], 5);
+
+ ASSERT_EQ(*(span5.data() + 2), *(span5.data() + 2));
+ }
+
+ // int32_t
+ {
+ std::vector<int32_t> v1{1, 2, 3, 4, 5};
+ std::vector<int32_t> v2(5);
+
+ ContiguousSpan<int32_t> span1(v1.begin(), v1.end());
+ ContiguousSpan<int32_t> span2(v2.begin(), v2.end());
+
+ span2.assign(span1);
+
+ ASSERT_EQ(v1, v2);
+ ASSERT_EQ(span1.size(), 5);
+ ASSERT_EQ(span2.size(), 5);
+
+ ASSERT_EQ(span2.at(2), 3);
+ ASSERT_EQ(span2.at(4), 5);
+
+ ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+
+ ContiguousSpan<int32_t> span3(span2.offset(1));
+ ASSERT_EQ(span3.size(), 4);
+ ASSERT_EQ(span3.at(0), 2);
+ ASSERT_EQ(span3.at(1), 3);
+ ASSERT_EQ(span3[2], 4);
+ ASSERT_EQ(span3[3], 5);
+
+ // const
+ ContiguousSpan<int32_t, true> span4(v1.begin(), v1.end());
+ ASSERT_EQ(span4.size(), 5);
+ ASSERT_EQ(span4.at(0), 1);
+ ASSERT_EQ(span4.at(1), 2);
+ ASSERT_EQ(span4.at(2), 3);
+ ASSERT_EQ(span4[3], 4);
+ ASSERT_EQ(span4[4], 5);
+
+ ContiguousSpan<int32_t, true> span5(span4.offset(1));
+ ASSERT_EQ(span5.size(), 4);
+ ASSERT_EQ(span5.at(0), 2);
+ ASSERT_EQ(span5.at(1), 3);
+ ASSERT_EQ(span5[2], 4);
+ ASSERT_EQ(span5[3], 5);
+ }
+
+ // uint32_t
+ {
+ std::vector<uint32_t> v1{1, 2, 3, 4, 5};
+ std::vector<uint32_t> v2(5);
+
+ ContiguousSpan<uint32_t> span1(v1.begin(), v1.end());
+ ContiguousSpan<uint32_t> span2(v2.begin(), v2.end());
+
+ span2.assign(span1);
+
+ ASSERT_EQ(v1, v2);
+ ASSERT_EQ(span1.size(), 5);
+ ASSERT_EQ(span2.size(), 5);
+
+ ASSERT_EQ(span2.at(2), 3);
+ ASSERT_EQ(span2.at(4), 5);
+
+ ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+
+ ContiguousSpan<uint32_t> span3(span2.offset(1));
+ ASSERT_EQ(span3.size(), 4);
+ ASSERT_EQ(span3.at(0), 2);
+ ASSERT_EQ(span3.at(1), 3);
+ ASSERT_EQ(span3[2], 4);
+ ASSERT_EQ(span3[3], 5);
+
+ // const
+ ContiguousSpan<uint32_t, true> span4(v1.begin(), v1.end());
+ ASSERT_EQ(span4.size(), 5);
+ ASSERT_EQ(span4.at(0), 1);
+ ASSERT_EQ(span4.at(1), 2);
+ ASSERT_EQ(span4.at(2), 3);
+ ASSERT_EQ(span4[3], 4);
+ ASSERT_EQ(span4[4], 5);
+
+ ContiguousSpan<uint32_t, true> span5(span4.offset(1));
+ ASSERT_EQ(span5.size(), 4);
+ ASSERT_EQ(span5.at(0), 2);
+ ASSERT_EQ(span5.at(1), 3);
+ ASSERT_EQ(span5[2], 4);
+ ASSERT_EQ(span5[3], 5);
+ }
+
+ // int8_t
+ {
+ std::vector<int8_t> v1{1, 2, 3, 4, 5};
+ std::vector<int8_t> v2(5);
+
+ ContiguousSpan<int8_t> span1(v1.begin(), v1.end());
+ ContiguousSpan<int8_t> span2(v2.begin(), v2.end());
+
+ span2.assign(span1);
- ContiguousSpan<float> span1(v1.begin(), v1.end());
- ContiguousSpan<float> span2(v2.begin(), v2.end());
+ ASSERT_EQ(v1, v2);
+ ASSERT_EQ(span1.size(), 5);
+ ASSERT_EQ(span2.size(), 5);
- span2.assign(span1);
+ ASSERT_EQ(span2.at(2), 3);
+ ASSERT_EQ(span2.at(4), 5);
- ASSERT_EQ(v1, v2);
- ASSERT_EQ(span1.size(), 5);
- ASSERT_EQ(span2.size(), 5);
+ ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
- ASSERT_EQ(span2.at(2), 3);
- ASSERT_EQ(span2.at(4), 5);
+ ContiguousSpan<int8_t> span3(span2.offset(1));
+ ASSERT_EQ(span3.size(), 4);
+ ASSERT_EQ(span3.at(0), 2);
+ ASSERT_EQ(span3.at(1), 3);
+ ASSERT_EQ(span3[2], 4);
+ ASSERT_EQ(span3[3], 5);
- ASSERT_EQ(*(span1.data() + 2), *(span1.data() + 2));
+ // const
+ ContiguousSpan<int8_t, true> span4(v1.begin(), v1.end());
+ ASSERT_EQ(span4.size(), 5);
+ ASSERT_EQ(span4.at(0), 1);
+ ASSERT_EQ(span4.at(1), 2);
+ ASSERT_EQ(span4.at(2), 3);
+ ASSERT_EQ(span4[3], 4);
+ ASSERT_EQ(span4[4], 5);
- ContiguousSpan<float> span3(span2.offset(1));
- ASSERT_EQ(span3.size(), 4);
- ASSERT_EQ(span3.at(0), 2);
- ASSERT_EQ(span3.at(1), 3);
- ASSERT_EQ(span3.at(2), 4);
- ASSERT_EQ(span3.at(3), 5);
+ ContiguousSpan<int8_t, true> span5(span4.offset(1));
+ ASSERT_EQ(span5.size(), 4);
+ ASSERT_EQ(span5.at(0), 2);
+ ASSERT_EQ(span5.at(1), 3);
+ ASSERT_EQ(span5[2], 4);
+ ASSERT_EQ(span5[3], 5);
+ }
}
set(LIB_ONERT_BACKEND_TRIX onert_backend_trix)
-nnfw_find_package(TRIXEngine EXACT 2.5.0 QUIET)
+nnfw_find_package(TRIXEngine QUIET 2.5.0)
if(NOT TRIXEngine_FOUND)
return()
endif(NOT TRIXEngine_FOUND)
#ifndef __ONERT_EXEC_FEATURE_NHWC_VIEW_H__
#define __ONERT_EXEC_FEATURE_NHWC_VIEW_H__
-#include "../Reader.h"
+#include "Reader.h"
#include <cassert>
#include <cstddef>
case TensorType::TensorType_INT8:
return ir::DataType::QUANT_INT8_ASYMM;
// case TensorType::TensorType_FLOAT64
+ case TensorType::TensorType_UINT32:
+ return ir::DataType::UINT32;
default:
throw std::runtime_error(
std::string("Unsupported tensor type: ").append(EnumNameTensorType(type)));
return()
endif ()
-nnfw_find_package(TRIXEngine EXACT 2.5.0 QUIET)
+nnfw_find_package(TRIXEngine QUIET 2.5.0)
if(TRIXEngine_FOUND)
list(APPEND SOURCES src/trix_loader.cc)
else()
target_link_libraries(${TEST_ONERT} gtest)
target_link_libraries(${TEST_ONERT} gtest_main)
target_link_libraries(${TEST_ONERT} ${LIB_PTHREAD} dl)
+
+# Requires linking nnfw_coverage: check header coverage
+target_link_libraries(${TEST_ONERT} nnfw_coverage)
+
add_test(${TEST_ONERT} ${TEST_ONERT})
install(TARGETS ${TEST_ONERT} DESTINATION unittest_standalone)
--- /dev/null
+
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "backend/ITensor.h"
+
+template <typename T> class MockTensor : public onert::backend::ITensor
+{
+public:
+ MockTensor<T>(onert::ir::Shape &shape, T *buf, onert::ir::Layout layout)
+ : _buf(reinterpret_cast<uint8_t *>(buf)), _shape(shape), _layout(layout)
+ {
+ }
+
+public:
+ uint8_t *buffer() const override { return _buf; }
+
+ size_t calcOffset(const onert::ir::Coordinates &coords) const override
+ {
+ size_t rank = _shape.rank();
+ rank = rank == 0 ? 1 : rank;
+ size_t offset = 0;
+ for (size_t i = 0; i < rank; ++i)
+ {
+ auto dim = _shape.rank() == 0 ? 1 : _shape.dim(i);
+ offset = offset * dim + coords[i];
+ }
+ offset *= sizeof(T);
+
+ return offset;
+ }
+
+ onert::ir::Shape getShape() const override { return _shape; }
+
+public: // DUMMY methods
+ size_t total_size() const override { return 0; }
+ onert::ir::Layout layout() const override { return _layout; }
+ onert::ir::DataType data_type() const override { return onert::ir::DataType::UINT8; }
+ float data_scale() const override { return 0; }
+ int32_t data_zero_point() const override { return 0; }
+ const std::vector<float> &data_scales() const override { return _dummy_scales; }
+ const std::vector<int32_t> &data_zero_points() const override { return _dummy_zerops; }
+ bool has_padding() const override { return false; }
+ void access(const std::function<void(ITensor &tensor)> &fn) override {}
+ bool is_dynamic() const override { return false; }
+
+private:
+ uint8_t *_buf = nullptr;
+ onert::ir::Shape _shape;
+ onert::ir::Layout _layout = onert::ir::Layout::UNKNOWN;
+ std::vector<float> _dummy_scales;
+ std::vector<int32_t> _dummy_zerops;
+};
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../MockTensor.h"
+
+#include "exec/feature/nchw/Reader.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class Reader_nchw : public testing::Test
+{
+public:
+ void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+ void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ _shape = onert::ir::FeatureShape(batch, depth, height, width);
+ }
+
+ void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ auto elem_size = sizeof(T);
+ _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+ width * elem_size);
+ }
+
+ void createReader()
+ {
+ _reader =
+ std::make_shared<nchw::Reader<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+ }
+
+ void createUsingMockTensor()
+ {
+ onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+ _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NCHW);
+ _reader = std::make_shared<nchw::Reader<T>>(_tensor.get());
+ }
+
+ std::shared_ptr<Reader<T>> _reader = nullptr;
+
+private:
+ std::shared_ptr<std::vector<T>> _data = nullptr;
+ onert::ir::FeatureShape _shape;
+ onert::ir::FeatureShape _stride;
+ std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ReaderTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(Reader_nchw, ReaderTypes);
+
+TYPED_TEST(Reader_nchw, basic_reader)
+{
+ this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+ this->setShape(1, 2, 3, 2);
+ this->setStride(12, 6, 2, 1);
+ this->createReader();
+
+ // Data: NCHW
+ // Shape: NCHW
+ ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 8);
+ ASSERT_EQ(this->_reader->at(1, 1, 0), 8);
+
+ // Data: NCHW
+ // Shape: NCHW
+ this->createUsingMockTensor();
+
+ ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 6);
+ ASSERT_EQ(this->_reader->at(1, 1, 0), 6);
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../MockTensor.h"
+
+#include "exec/feature/nchw/View.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class View_nchw : public testing::Test
+{
+public:
+ void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+ void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ _shape = onert::ir::FeatureShape(batch, depth, height, width);
+ }
+
+ void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ auto elem_size = sizeof(T);
+ _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+ width * elem_size);
+ }
+
+ void createView()
+ {
+ _view =
+ std::make_shared<nchw::View<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+ }
+
+ void createUsingMockTensor()
+ {
+ onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+ _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NCHW);
+ _view = std::make_shared<nchw::View<T>>(_tensor.get());
+ }
+
+ std::shared_ptr<nchw::View<T>> _view = nullptr;
+
+private:
+ std::shared_ptr<std::vector<T>> _data = nullptr;
+ onert::ir::FeatureShape _shape;
+ onert::ir::FeatureShape _stride;
+ std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ViewTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(View_nchw, ViewTypes);
+
+TYPED_TEST(View_nchw, basic_view)
+{
+ this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+ this->setShape(1, 2, 3, 2);
+ this->setStride(12, 6, 2, 1);
+ this->createView();
+
+ // Data: NCHW
+ // Shape: NCHW
+ ASSERT_EQ(this->_view->at(0, 1, 1, 0), 8);
+ ASSERT_EQ(this->_view->at(1, 1, 0), 8);
+
+ // Data: NCHW
+ // Shape: NCHW
+ this->createUsingMockTensor();
+
+ ASSERT_EQ(this->_view->at(0, 1, 1, 0), 6);
+ ASSERT_EQ(this->_view->at(1, 1, 0), 6);
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../MockTensor.h"
+
+#include "exec/feature/nhwc/Reader.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class Reader_nhwc : public testing::Test
+{
+public:
+ void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+ void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ _shape = onert::ir::FeatureShape(batch, depth, height, width);
+ }
+
+ void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ auto elem_size = sizeof(T);
+ _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+ width * elem_size);
+ }
+
+ void createReader()
+ {
+ _reader =
+ std::make_shared<nhwc::Reader<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+ }
+
+ void createUsingMockTensor()
+ {
+ onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+ _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NHWC);
+ _reader = std::make_shared<nhwc::Reader<T>>(_tensor.get());
+ }
+
+ std::shared_ptr<nhwc::Reader<T>> _reader = nullptr;
+
+private:
+ std::shared_ptr<std::vector<T>> _data = nullptr;
+ onert::ir::FeatureShape _shape;
+ onert::ir::FeatureShape _stride;
+ std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ReaderTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(Reader_nhwc, ReaderTypes);
+TYPED_TEST_SUITE(MockTensorReader_nhwc, ReaderTypes);
+
+TYPED_TEST(Reader_nhwc, basic_reader)
+{
+ this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+ this->setShape(1, 2, 3, 2);
+ this->setStride(12, 1, 6, 2);
+ this->createReader();
+
+ // Data: NCHW
+ // Shape: NHWC
+ ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 8);
+ ASSERT_EQ(this->_reader->at(1, 1, 0), 8);
+
+ // Data: NHWC
+ // Shape: NHWC
+ this->createUsingMockTensor();
+
+ ASSERT_EQ(this->_reader->at(0, 1, 1, 0), 6);
+ ASSERT_EQ(this->_reader->at(1, 1, 0), 6);
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../MockTensor.h"
+
+#include "exec/feature/nhwc/View.h"
+
+#include <gtest/gtest.h>
+
+using namespace onert::exec::feature;
+
+template <typename T> class View_nhwc : public testing::Test
+{
+public:
+ void setData(std::initializer_list<T> list) { _data = std::make_shared<std::vector<T>>(list); }
+
+ void setShape(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ _shape = onert::ir::FeatureShape(batch, depth, height, width);
+ }
+
+ void setStride(int32_t batch, int32_t depth, int32_t height, int32_t width)
+ {
+ auto elem_size = sizeof(T);
+ _stride = onert::ir::FeatureShape(batch * elem_size, depth * elem_size, height * elem_size,
+ width * elem_size);
+ }
+
+ void createView()
+ {
+ _view =
+ std::make_shared<nhwc::View<T>>(_shape, _stride, _data->data(), _data->size() * sizeof(T));
+ }
+
+ void createUsingMockTensor()
+ {
+ onert::ir::Shape shape = {_shape.N, _shape.H, _shape.W, _shape.C};
+ _tensor = std::make_shared<MockTensor<T>>(shape, _data->data(), onert::ir::Layout::NHWC);
+ _view = std::make_shared<nhwc::View<T>>(_tensor.get());
+ }
+
+ std::shared_ptr<nhwc::View<T>> _view = nullptr;
+
+private:
+ std::shared_ptr<std::vector<T>> _data = nullptr;
+ onert::ir::FeatureShape _shape;
+ onert::ir::FeatureShape _stride;
+ std::shared_ptr<MockTensor<T>> _tensor = nullptr;
+};
+
+using ViewTypes = ::testing::Types<float, int32_t, uint8_t, int8_t, int16_t>;
+TYPED_TEST_SUITE(View_nhwc, ViewTypes);
+TYPED_TEST_SUITE(MockTensorView_nhwc, ViewTypes);
+
+TYPED_TEST(View_nhwc, basic_view)
+{
+ this->setData({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
+ this->setShape(1, 2, 3, 2);
+ this->setStride(12, 1, 6, 2);
+ this->createView();
+
+ // Data: NCHW
+ // Shape: NHWC
+ ASSERT_EQ(this->_view->at(0, 1, 1, 0), 8);
+ ASSERT_EQ(this->_view->at(1, 1, 0), 8);
+
+ // Data: NHWC
+ // Shape: NHWC
+ this->createUsingMockTensor();
+
+ ASSERT_EQ(this->_view->at(0, 1, 1, 0), 6);
+ ASSERT_EQ(this->_view->at(1, 1, 0), 6);
+}
circle::BuiltinOptions_NONE, 0);
}
+uint32_t CircleGen::addOperatorGreater(const OperatorParams ¶ms)
+{
+ auto options = circle::CreateLessOptions(_fbb).Union();
+ return addOperatorWithOptions(params, circle::BuiltinOperator_GREATER,
+ circle::BuiltinOptions_GreaterOptions, options);
+}
+
+uint32_t CircleGen::addOperatorGreaterEqual(const OperatorParams ¶ms)
+{
+ auto options = circle::CreateGreaterOptions(_fbb).Union();
+ return addOperatorWithOptions(params, circle::BuiltinOperator_GREATER_EQUAL,
+ circle::BuiltinOptions_GreaterEqualOptions, options);
+}
+
uint32_t CircleGen::addOperatorL2Normalization(const OperatorParams ¶ms)
{
auto options = circle::CreateL2NormOptions(_fbb).Union();
circle::BuiltinOptions_LessOptions, options);
}
+uint32_t CircleGen::addOperatorLessEqual(const OperatorParams ¶ms)
+{
+ auto options = circle::CreateLessOptions(_fbb).Union();
+ return addOperatorWithOptions(params, circle::BuiltinOperator_LESS_EQUAL,
+ circle::BuiltinOptions_LessEqualOptions, options);
+}
+
uint32_t CircleGen::addOperatorLeakyRelu(const OperatorParams ¶ms, float alpha)
{
auto options = circle::CreateLeakyReluOptions(_fbb, alpha).Union();
circle::BuiltinOptions_NegOptions, options);
}
+uint32_t CircleGen::addOperatorNotEqual(const OperatorParams ¶ms)
+{
+ auto options = circle::CreateEqualOptions(_fbb).Union();
+ return addOperatorWithOptions(params, circle::BuiltinOperator_NOT_EQUAL,
+ circle::BuiltinOptions_NotEqualOptions, options);
+}
+
uint32_t CircleGen::addOperatorOneHot(const OperatorParams ¶ms, int32_t axis)
{
auto options = circle::CreateOneHotOptions(_fbb, axis).Union();
uint32_t addOperatorFullyConnected(const OperatorParams ¶ms,
circle::FullyConnectedOptionsWeightsFormat weights_format =
circle::FullyConnectedOptionsWeightsFormat_DEFAULT);
+ uint32_t addOperatorGreater(const OperatorParams ¶ms);
+ uint32_t addOperatorGreaterEqual(const OperatorParams ¶ms);
uint32_t addOperatorIf(const OperatorParams ¶ms, uint32_t then_subg, uint32_t else_subg);
uint32_t addOperatorInstanceNorm(const OperatorParams ¶ms, float epsilon,
circle::ActivationFunctionType actfn);
uint32_t addOperatorL2Normalization(const OperatorParams ¶ms);
uint32_t addOperatorLeakyRelu(const OperatorParams ¶ms, float alpha);
uint32_t addOperatorLess(const OperatorParams ¶ms);
+ uint32_t addOperatorLessEqual(const OperatorParams ¶ms);
uint32_t addOperatorLogSoftmax(const OperatorParams ¶ms);
uint32_t addOperatorMul(const OperatorParams ¶ms, circle::ActivationFunctionType actfn);
uint32_t addOperatorMean(const OperatorParams ¶ms, bool keep_dims);
uint32_t addOperatorNeg(const OperatorParams ¶ms);
+ uint32_t addOperatorNotEqual(const OperatorParams ¶ms);
uint32_t addOperatorOneHot(const OperatorParams ¶ms, int32_t axis);
uint32_t addOperatorPad(const OperatorParams ¶ms);
uint32_t addOperatorPadV2(const OperatorParams ¶ms);
SUCCEED();
}
-TEST_F(GenModelTest, net_OneOp_Add_VarToVarInt16)
+TEST_F(GenModelTest, neg_OneOp_Add_VarToVarInt16)
{
CircleGen cgen;
int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT16}, 1., 2);
SUCCEED();
}
+TEST_F(GenModelTest, OneOp_Cast_Uint8ToFloat32)
+{
+ CircleGen cgen = genSimpleCastModel(circle::TensorType_UINT8, circle::TensorType_FLOAT32);
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ // clang-format off
+ _context->addTestCase(
+ TestCaseData{}.addInput<uint8_t>({0, 100, 200, 255})
+ .addOutput<float>({0., 100., 200., 255.}));
+ // clang-format on
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, OneOp_Cast_Int64ToFloat32)
+{
+ CircleGen cgen = genSimpleCastModel(circle::TensorType_INT64, circle::TensorType_FLOAT32);
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->addTestCase(TestCaseData{}
+ .addInput<int64_t>({-12345, 3, 100, 2147483648})
+ .addOutput<float>({-12345., 3., 100., 2147483648.}));
+ _context->setBackends({"cpu"});
+
+ SUCCEED();
+}
+
TEST_F(GenModelTest, OneOp_Cast_AfterEqual)
{
CircleGen cgen;
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GenModelTest.h"
+
+struct GreaterVariationParam
+{
+ TestCaseData tcd;
+ circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+ const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class GreaterVariation : public GenModelTest,
+ public ::testing::WithParamInterface<GreaterVariationParam>
+{
+};
+
+// Input shape:
+// Base: {1, 2, 2, 1}
+// Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, GreaterVariation,
+ ::testing::Values(
+ // Float type
+ GreaterVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.1, 0.2, 0.3, 0.4})
+ .addOutput<bool>({false, true, false, true})},
+ // Float type - broadcast
+ GreaterVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.3})
+ .addOutput<bool>({false, false, false, true})},
+ // Int32 type
+ GreaterVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({1, 2, 3, 4})
+ .addOutput<bool>({false, true, false, true}),
+ circle::TensorType::TensorType_INT32},
+ // Int32 type - broadcast
+ GreaterVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({5})
+ .addOutput<bool>({false, false, false, true}),
+ circle::TensorType::TensorType_INT32},
+ // Int64 type
+ // NYI: acl backend
+ GreaterVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1, 2, 3, 4})
+ .addOutput<bool>({false, true, false, true}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}},
+ // Int64 type - broadcast
+ // NYI: acl backend
+ GreaterVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1})
+ .addOutput<bool>({false, true, false, true}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}}));
+
+TEST_P(GreaterVariation, Test)
+{
+ auto ¶m = GetParam();
+
+ auto lhs_data = param.tcd.inputs.at(0);
+ auto rhs_data = param.tcd.inputs.at(1);
+
+ bool broadcast_lhs = false;
+ bool broadcast_rhs = false;
+ if (lhs_data.size() != rhs_data.size())
+ {
+ if (lhs_data.size() < rhs_data.size())
+ broadcast_lhs = true;
+ else
+ broadcast_rhs = true;
+ }
+
+ CircleGen cgen;
+ const auto output_type = circle::TensorType::TensorType_BOOL;
+
+ int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+ cgen.addOperatorGreater({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->addTestCase(param.tcd);
+ _context->setBackends(param.backends);
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Greater_DifferentType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+ cgen.addOperatorGreater({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Greater_InvalidType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ cgen.addOperatorGreater({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GenModelTest.h"
+
+struct GreaterEqualVariationParam
+{
+ TestCaseData tcd;
+ circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+ const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class GreaterEqualVariation : public GenModelTest,
+ public ::testing::WithParamInterface<GreaterEqualVariationParam>
+{
+};
+
+// Input shape:
+// Base: {1, 2, 2, 1}
+// Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(
+ GenModelTest, GreaterEqualVariation,
+ ::testing::Values(
+ // Float type
+ GreaterEqualVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.1, 0.2, 0.3, 0.4})
+ .addOutput<bool>({true, true, false, true})},
+ // Float type - broadcast
+ GreaterEqualVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.3})
+ .addOutput<bool>({false, true, false, true})},
+ // Int32 type
+ GreaterEqualVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({1, 2, 3, 4})
+ .addOutput<bool>({true, true, false, true}),
+ circle::TensorType::TensorType_INT32},
+ // Int32 type - broadcast
+ GreaterEqualVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({5})
+ .addOutput<bool>({false, false, false, true}),
+ circle::TensorType::TensorType_INT32},
+ // Int64 type
+ // NYI: acl backend
+ GreaterEqualVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1, 2, 3, 4})
+ .addOutput<bool>({true, true, false, true}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}},
+ // Int64 type - broadcast
+ // NYI: acl backend
+ GreaterEqualVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1})
+ .addOutput<bool>({true, true, false, true}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}}));
+
+TEST_P(GreaterEqualVariation, Test)
+{
+ auto ¶m = GetParam();
+
+ auto lhs_data = param.tcd.inputs.at(0);
+ auto rhs_data = param.tcd.inputs.at(1);
+
+ bool broadcast_lhs = false;
+ bool broadcast_rhs = false;
+ if (lhs_data.size() != rhs_data.size())
+ {
+ if (lhs_data.size() < rhs_data.size())
+ broadcast_lhs = true;
+ else
+ broadcast_rhs = true;
+ }
+
+ CircleGen cgen;
+ const auto output_type = circle::TensorType::TensorType_BOOL;
+
+ int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+ cgen.addOperatorGreaterEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->addTestCase(param.tcd);
+ _context->setBackends(param.backends);
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_GreaterEqual_DifferentType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+ cgen.addOperatorGreaterEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_GreaterEqual_InvalidType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ cgen.addOperatorGreaterEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GenModelTest.h"
+
+struct LessVariationParam
+{
+ TestCaseData tcd;
+ circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+ const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class LessVariation : public GenModelTest, public ::testing::WithParamInterface<LessVariationParam>
+{
+};
+
+// Input shape:
+// Base: {1, 2, 2, 1}
+// Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, LessVariation,
+ ::testing::Values(
+ // Float type
+ LessVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.1, 0.2, 0.3, 0.4})
+ .addOutput<bool>({false, false, true, false})},
+ // Float type - broadcast
+ LessVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.3})
+ .addOutput<bool>({true, false, true, false})},
+ // Int32 type
+ LessVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({1, 2, 3, 4})
+ .addOutput<bool>({false, false, true, false}),
+ circle::TensorType::TensorType_INT32},
+ // Int32 type - broadcast
+ LessVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({5})
+ .addOutput<bool>({true, true, true, false}),
+ circle::TensorType::TensorType_INT32},
+ // Int64 type
+ // NYI: acl backend
+ LessVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1, 2, 3, 4})
+ .addOutput<bool>({false, false, true, false}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}},
+ // Int64 type - broadcast
+ // NYI: acl backend
+ LessVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1})
+ .addOutput<bool>({false, false, true, false}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}}));
+
+TEST_P(LessVariation, Test)
+{
+ auto ¶m = GetParam();
+
+ auto lhs_data = param.tcd.inputs.at(0);
+ auto rhs_data = param.tcd.inputs.at(1);
+
+ bool broadcast_lhs = false;
+ bool broadcast_rhs = false;
+ if (lhs_data.size() != rhs_data.size())
+ {
+ if (lhs_data.size() < rhs_data.size())
+ broadcast_lhs = true;
+ else
+ broadcast_rhs = true;
+ }
+
+ CircleGen cgen;
+ const auto output_type = circle::TensorType::TensorType_BOOL;
+
+ int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+ cgen.addOperatorLess({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->addTestCase(param.tcd);
+ _context->setBackends(param.backends);
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Less_DifferentType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+ cgen.addOperatorLess({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_Less_InvalidType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ cgen.addOperatorLess({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GenModelTest.h"
+
+struct LessEqualVariationParam
+{
+ TestCaseData tcd;
+ circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+ const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class LessEqualVariation : public GenModelTest,
+ public ::testing::WithParamInterface<LessEqualVariationParam>
+{
+};
+
+// Input shape:
+// Base: {1, 2, 2, 1}
+// Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, LessEqualVariation,
+ ::testing::Values(
+ // Float type
+ LessEqualVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.1, 0.2, 0.3, 0.4})
+ .addOutput<bool>({true, false, true, false})},
+ // Float type - broadcast
+ LessEqualVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.2, 0.7})
+ .addInput<float>({0.3})
+ .addOutput<bool>({true, true, true, false})},
+ // Int32 type
+ LessEqualVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({1, 2, 3, 4})
+ .addOutput<bool>({true, false, true, false}),
+ circle::TensorType::TensorType_INT32},
+ // Int32 type - broadcast
+ LessEqualVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 2, 7})
+ .addInput<int32_t>({5})
+ .addOutput<bool>({true, true, true, false}),
+ circle::TensorType::TensorType_INT32},
+ // Int64 type
+ // NYI: acl backend
+ LessEqualVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1, 2, 3, 4})
+ .addOutput<bool>({true, false, true, false}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}},
+ // Int64 type - broadcast
+ // NYI: acl backend
+ LessEqualVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, -2, 7})
+ .addInput<int64_t>({1})
+ .addOutput<bool>({true, false, true, false}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}}));
+
+TEST_P(LessEqualVariation, Test)
+{
+ auto ¶m = GetParam();
+
+ auto lhs_data = param.tcd.inputs.at(0);
+ auto rhs_data = param.tcd.inputs.at(1);
+
+ bool broadcast_lhs = false;
+ bool broadcast_rhs = false;
+ if (lhs_data.size() != rhs_data.size())
+ {
+ if (lhs_data.size() < rhs_data.size())
+ broadcast_lhs = true;
+ else
+ broadcast_rhs = true;
+ }
+
+ CircleGen cgen;
+ const auto output_type = circle::TensorType::TensorType_BOOL;
+
+ int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+ cgen.addOperatorLessEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->addTestCase(param.tcd);
+ _context->setBackends(param.backends);
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_LessEqual_DifferentType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+ cgen.addOperatorLessEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_LessEqual_InvalidType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ cgen.addOperatorLessEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GenModelTest.h"
+
+struct NotEqualVariationParam
+{
+ TestCaseData tcd;
+ circle::TensorType input_type = circle::TensorType::TensorType_FLOAT32;
+ const std::vector<std::string> backends = {"acl_cl", "acl_neon", "cpu"};
+};
+
+class NotEqualVariation : public GenModelTest,
+ public ::testing::WithParamInterface<NotEqualVariationParam>
+{
+};
+
+// Input shape:
+// Base: {1, 2, 2, 1}
+// Brodcast: {1} on of two input
+// Output shape: {1, 2, 2, 1}
+// Input type: Non-quantization type
+// Output type: BOOL
+// Test with different input type and value
+INSTANTIATE_TEST_SUITE_P(GenModelTest, NotEqualVariation,
+ ::testing::Values(
+ // Float type
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.5, 0.7})
+ .addInput<float>({0.1, 0.2, 0.3, 0.4})
+ .addOutput<bool>({false, true, true, true})},
+ // Float type - broadcast
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<float>({0.1, 0.3, 0.5, 0.7})
+ .addInput<float>({0.3})
+ .addOutput<bool>({true, false, true, true})},
+ // Int32 type
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 5, 7})
+ .addInput<int32_t>({1, 2, 3, 4})
+ .addOutput<bool>({false, true, true, true}),
+ circle::TensorType::TensorType_INT32},
+ // Int32 type - broadcast
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<int32_t>({1, 3, 5, 7})
+ .addInput<int32_t>({5})
+ .addOutput<bool>({true, true, false, true}),
+ circle::TensorType::TensorType_INT32},
+ // Int64 type
+ // NYI: acl backend
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, 5, 7})
+ .addInput<int64_t>({1, 2, 3, 4})
+ .addOutput<bool>({false, true, true, true}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}},
+ // Int64 type - broadcast
+ // NYI: acl backend
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<int64_t>({1, 3, 5, 7})
+ .addInput<int64_t>({1})
+ .addOutput<bool>({false, true, true, true}),
+ circle::TensorType::TensorType_INT64,
+ {"cpu"}},
+ // Bool type
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<bool>({false, false, true, true})
+ .addInput<bool>({false, true, false, true})
+ .addOutput<bool>({false, true, true, false}),
+ circle::TensorType::TensorType_BOOL},
+ // Bool type - broadcast
+ NotEqualVariationParam{TestCaseData{}
+ .addInput<bool>({false, false, true, true})
+ .addInput<bool>({false})
+ .addOutput<bool>({false, false, true, true}),
+ circle::TensorType::TensorType_BOOL}
+
+ ));
+
+TEST_P(NotEqualVariation, Test)
+{
+ auto ¶m = GetParam();
+
+ auto lhs_data = param.tcd.inputs.at(0);
+ auto rhs_data = param.tcd.inputs.at(1);
+
+ bool broadcast_lhs = false;
+ bool broadcast_rhs = false;
+ if (lhs_data.size() != rhs_data.size())
+ {
+ if (lhs_data.size() < rhs_data.size())
+ broadcast_lhs = true;
+ else
+ broadcast_rhs = true;
+ }
+
+ CircleGen cgen;
+ const auto output_type = circle::TensorType::TensorType_BOOL;
+
+ int lhs = broadcast_lhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int rhs = broadcast_rhs ? cgen.addTensor({{1}, param.input_type})
+ : cgen.addTensor({{1, 2, 2, 1}, param.input_type});
+ int out = cgen.addTensor({{1, 2, 2, 1}, output_type});
+ cgen.addOperatorNotEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->addTestCase(param.tcd);
+ _context->setBackends(param.backends);
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_NotEqual_DifferentType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_BOOL});
+ cgen.addOperatorNotEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
+
+TEST_F(GenModelTest, neg_OneOp_NotEqual_InvalidType)
+{
+ CircleGen cgen;
+ int lhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int rhs = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32});
+ int out = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_INT32});
+ cgen.addOperatorNotEqual({{lhs, rhs}, {out}});
+ cgen.setInputsAndOutputs({lhs, rhs}, {out});
+
+ _context = std::make_unique<GenModelTestContext>(cgen.finish());
+ _context->setBackends({"acl_cl", "acl_neon", "cpu"});
+ _context->expectFailModelLoad();
+
+ SUCCEED();
+}
uint32_t num_inputs;
NNPR_ENSURE_STATUS(nnfw_input_size(session_, &num_inputs));
- // TODO: Support multiple inputs
- // Option 1. Get comman-separated input file list like --load:raw in.0,in.1,in.2
- // Option 2. Get prefix --load:raw out
- // Internally access out.0, out.1, out.2, ... out.{N} where N is determined by api.
- if (num_inputs != 1)
- {
- throw std::runtime_error("Only 1 input is supported for raw input");
- }
+ // Support multiple inputs
+ // Option 1: Get comman-separated input file list like --load:raw a,b,c
+ // Option 2: Get prefix --load:raw in
+ // Internally access in.0, in.1, in.2, ... in.{N-1} where N is determined by nnfw info
+ // query api.
+ //
+ // Currently Option 2 is implemented.
try
{
for (uint32_t i = 0; i < num_inputs; ++i)
auto bufsz = bufsize_for(&ti);
inputs[i].alloc(bufsz);
- std::ifstream file(filename, std::ios::ate | std::ios::binary);
+ std::ifstream file(filename + "." + std::to_string(i), std::ios::ate | std::ios::binary);
auto filesz = file.tellg();
if (bufsz != filesz)
{
- throw std::runtime_error("Input Size does not match: " + std::to_string(bufsz) +
+ throw std::runtime_error("Input " + std::to_string(i) +
+ " size does not match: " + std::to_string(bufsz) +
" expected, but " + std::to_string(filesz) + " provided.");
}
file.seekg(0, std::ios::beg);
{
uint32_t num_outputs;
NNPR_ENSURE_STATUS(nnfw_output_size(session_, &num_outputs));
- // TODO: Support multiple outputs
- // Available options are same.
- if (num_outputs != 1)
- {
- throw std::runtime_error("Only 1 output is supported for raw input");
- }
try
{
for (uint32_t i = 0; i < num_outputs; i++)