Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT][tools/mec] Display the name of the inputs for each operator #14458

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions compiler/circle-input-names-test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)

get_target_property(CIRCLE_INPUT_NAMES_PATH circle_input_names BINARY_DIR)
set(CIRCLE_INPUT_NAMES_PATH "${CIRCLE_INPUT_NAMES_PATH}/circle_input_names")

nnas_find_package(GTest REQUIRED)
nnas_find_package(Jsoncpp REQUIRED)

file(GLOB TESTS_SOURCE "src/*.test.cpp")

GTest_AddTest(circle-input-names-test ${TESTS_SOURCE})

set_tests_properties(circle-input-names-test
PROPERTIES
ENVIRONMENT "CIRCLE_INPUT_NAMES_PATH=${CIRCLE_INPUT_NAMES_PATH}")
3 changes: 3 additions & 0 deletions compiler/circle-input-names-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# circle-input-names-test

`circle-input-names-test` verifies that the `circle-input-names` compiler works as expected.
1 change: 1 addition & 0 deletions compiler/circle-input-names-test/requires.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("circle-input-names")
124 changes: 124 additions & 0 deletions compiler/circle-input-names-test/src/circle-input-names.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <gtest/gtest.h>

#include <cstdlib>
#include <cstring>
#include <fstream>
#include <vector>
#include <regex>

class circle_input_names_test : public ::testing::Test
{
protected:
void SetUp(void) override;
std::vector<std::string> get_input_names_of(std::string op);

protected:
std::string _input_names_dict_str;
};

void circle_input_names_test::SetUp(void)
{
std::string cmd = std::getenv("CIRCLE_INPUT_NAMES_PATH");
if (cmd.empty())
{
std::runtime_error("CIRCLE_INPUT_NAMES_PATH is not found");
return;
}

FILE *fp = popen(cmd.c_str(), "r");
if (!fp)
{
std::runtime_error("popen() failed");
return;
}

char buff[1024];
std::string result = "";
try
{
while (fgets(buff, sizeof(buff), fp) != NULL)
{
result += buff;
}
}
catch (...)
{
pclose(fp);
throw;
}

_input_names_dict_str = result;
pclose(fp);

return;
}

std::vector<std::string> circle_input_names_test::get_input_names_of(std::string op)
{
std::vector<std::string> input_names{};

// Find op string key from _input_names_dict_str and parse the values input input_names vector
size_t pos = _input_names_dict_str.find(op);
if (pos == std::string::npos)
{
return input_names;
}
else
{
std::string substr = _input_names_dict_str.substr(pos);
size_t start_pos = substr.find("[");
size_t end_pos = substr.find("]");
if (start_pos != std::string::npos && end_pos != std::string::npos)
{
std::string names = substr.substr(start_pos + 1, end_pos - start_pos - 1);
std::stringstream ss(names);
std::string name;
while (std::getline(ss, name, ','))
{
std::smatch match;
std::regex pattern = std::regex(R"(^\s*\"([^\"]+)\"\s*$)");
if (std::regex_match(name, match, pattern))
{
input_names.push_back(match[1].str());
}
}
}
}
return std::move(input_names);
}

TEST_F(circle_input_names_test, valid_command) { ASSERT_FALSE(_input_names_dict_str.empty()); }

TEST_F(circle_input_names_test, valid_names_softmax)
{
// "SOFTMAX" should have single input: "logits"
auto names = get_input_names_of("SOFTMAX");
ASSERT_EQ(names.size(), 1);
ASSERT_EQ(names[0], "logits");
}

TEST_F(circle_input_names_test, valid_names_conv2d)
{
// "CONV_2D" should have three inputs: "input", "filter", "bias"
auto names = get_input_names_of("CONV_2D");
ASSERT_EQ(names.size(), 3);
ASSERT_EQ(names[0], "input");
ASSERT_EQ(names[1], "filter");
ASSERT_EQ(names[2], "bias");
}
8 changes: 8 additions & 0 deletions compiler/circle-input-names/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
file(GLOB SOURCES "src/*.cpp")

add_executable(circle_input_names ${SOURCES})
target_include_directories(circle_input_names PUBLIC include)
target_link_libraries(circle_input_names PRIVATE luci_logex)
target_link_libraries(circle_input_names PRIVATE luci_lang)
target_link_libraries(circle_input_names PRIVATE crew)
install(TARGETS circle_input_names DESTINATION bin)
3 changes: 3 additions & 0 deletions compiler/circle-input-names/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# circle-input-names

_circle-input-names_ is a tool to generate input names of the Circle model's operators in JSON format.
2 changes: 2 additions & 0 deletions compiler/circle-input-names/requires.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require("crew")
require("luci")
181 changes: 181 additions & 0 deletions compiler/circle-input-names/src/circle-input-names.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <iostream>
#include <crew/PConfigJson.h>
#include <luci/CircleNodeSummaryBuilders.h>

using namespace luci;

// clang-format off
// For Variadic Arity Nodes which have multiple inputs.
template <typename T> struct is_VariadicArity : std::false_type {};
template <> struct is_VariadicArity<CircleConcatenation> : std::true_type {};
template <> struct is_VariadicArity<CircleCustom> : std::true_type {};
template <> struct is_VariadicArity<CirclePack> : std::true_type {};
template <> struct is_VariadicArity<CircleAddN> : std::true_type {};
template <> struct is_VariadicArity<CircleIf> : std::true_type {};
template <> struct is_VariadicArity<CircleWhile> : std::true_type {};

// For Variadic Outputs Nodes which have multiple outputs.
template <typename T> struct is_VariadicOut : std::false_type {};
template <> struct is_VariadicOut<CircleCustom> : std::true_type{};
template <> struct is_VariadicOut<CircleIf> : std::true_type {};
template <> struct is_VariadicOut<CircleWhile> : std::true_type {};
// clang-format on

// For Circle Nodes which have variadic arity and variadic outputs
template <typename CircleOp, typename std::enable_if_t<is_VariadicArity<CircleOp>::value &&
is_VariadicOut<CircleOp>::value> * = nullptr>
auto CircleNodeCreator()
{
return CircleOp(1, 1);
}

// For Circle Nodes which have variadic arity but single output
template <typename CircleOp,
typename std::enable_if_t<is_VariadicArity<CircleOp>::value &&
!is_VariadicOut<CircleOp>::value> * = nullptr>
auto CircleNodeCreator()
{
return CircleOp(1);
}

// For Circle Nodes which have fixed arity
template <typename CircleOp,
typename std::enable_if_t<!is_VariadicArity<CircleOp>::value> * = nullptr>
auto CircleNodeCreator()
{
return CircleOp();
}

// Add fused activation function option to CircleNode if it supports FusedActFunc traits
void add_fused_actfn_option(CircleNode *node)
{
auto node_ = dynamic_cast<CircleNodeMixin<CircleNodeTrait::FusedActFunc> *>(node);
if (node_)
{
node_->fusedActivationFunction(luci::FusedActFunc::RELU);
}
}

// Add padding option to AVERAGE_POOL_2D, CONV_2D, DEPTHWISE_CONV_2D, L2_POOL_2D, MAX_POOL_2D,
// TRANSPOSE_CONV nodes
void add_padding_option(CircleNode *node)
{
switch (node->opcode())
{
#define CIRCLE_NODE(OPCODE, CLASS) \
case luci::CircleOpcode::OPCODE: \
{ \
auto node_ = dynamic_cast<CLASS *>(node); \
node_->padding(Padding::SAME); \
break; \
}
CIRCLE_NODE(AVERAGE_POOL_2D, CircleAveragePool2D)
CIRCLE_NODE(CONV_2D, CircleConv2D)
CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2D)
CIRCLE_NODE(L2_POOL_2D, CircleL2Pool2D)
CIRCLE_NODE(MAX_POOL_2D, CircleMaxPool2D)
CIRCLE_NODE(TRANSPOSE_CONV, CircleTransposeConv)
#undef CIRCLE_NODE
default:
{
break;
}
}
return;
}

// Add mode option to MIRROR_PAD, ROPE nodes
void add_mode_option(CircleNode *node)
{
switch (node->opcode())
{
#define CIRCLE_NODE(OPCODE, CLASS) \
case luci::CircleOpcode::OPCODE: \
{ \
auto node_ = dynamic_cast<CLASS *>(node); \
auto mode_ = node_->mode(); \
node_->mode(decltype(mode_)(1)); \
break; \
}
CIRCLE_NODE(MIRROR_PAD, CircleMirrorPad)
CIRCLE_NODE(ROPE, CircleRoPE)
#undef CIRCLE_NODE
default:
{
break;
}
}
return;
}

// Mock Symbol Table for CircleNodeSummaryBuilder
class MockSymbolTable : public locop::SymbolTable
{
std::string lookup(const loco::Node *) const override { return ""; }
};

// Get input names of CircleNode and export as JSON format
void get_input_names(CircleNode *node, crew::JsonExport &json_export)
{
locop::NodeSummary s;
MockSymbolTable tbl;
CircleNodeSummaryBuilder builder;

builder.build(node, &tbl, s);

std::vector<std::string> arg_names;
for (int i = 0; i < node->arity(); i++)
{
auto args = s.args().at(i);
// args : pair(name, value)
arg_names.emplace_back(args.first);
}

// s.opname() : "Circle.Opname""
auto opname = s.opname().substr(7);
// "Opname" : ["arg1", "arg2",...],"
json_export.key_val(opname, arg_names, true);
}

int main(void)
{
std::stringstream ss;
crew::JsonExport json_export(ss);
// "{"
json_export.open_brace();
#define CIRCLE_NODE(OP, CIRCLE_OP) \
{ \
auto node = CircleNodeCreator<CIRCLE_OP>(); \
add_fused_actfn_option(&node); \
add_padding_option(&node); \
add_mode_option(&node); \
get_input_names(&node, json_export); \
}
#define CIRCLE_VNODE(_1, _2)
#include <luci/IR/CircleNodes.lst>
#undef CIRCLE_NODE
#undef CIRCLE_VNODE
// Remove last comma from stringstream 'ss'
ss.seekp(-2, std::ios_base::end) << '\n';
// "}"
json_export.close_brace(false);
std::cout << ss.str();

return 0;
}
Loading
Loading