#include "backend/interface/ITensorBuilder.h"
#include "backend/interface/IStage.h"
#include "model/operation/NodeVisitor.h"
+#include "model/operation/Subgraph.h"
#include "cpp14/memory.h"
-// TODO Remove dependencies for below two headers
-#include "backend/AtomicStage.h"
-// for Subgraph
-//#include "backend/StageSequence.h"
+// TODO Remove dependencies for below header. Should include only interface.
+#include "backend/StageSequence.h"
struct IExecutionBuilder
{
virtual void visit(const model::operation::InternalName &) override {}
#include "model/operation/Op.lst"
#undef OP
- // TODO: Fill this
- virtual void visit(const model::operation::Subgraph &) override {}
+ virtual void visit(const model::operation::Subgraph &subgraph) final override
+ {
+ for (const auto &e : subgraph.operations())
+ {
+ e.node->accept(std::move(*this));
+ }
+ }
protected:
void returnStage(const StageFn fn)
public:
std::unique_ptr<IStage> generate(const model::operation::Node &node)
{
- // TODO Consider Subgraph and
- // remove directly dependency for classes not interface
- _return = nnfw::cpp14::make_unique<AtomicStage>();
+ // TODO Remove directly dependency for classes not interface
+ _return = nnfw::cpp14::make_unique<StageSequence>();
node.accept(std::move(*this));
return std::move(_return);
}
Linear::Linear(const graph::Graph &graph) : _graph(graph)
{
- // Linearize with topological sort
+ // TODO: Move this code to graph
+
+ // Linearize graph with subgraphs by topological sort while assuming that
+ // a subgraph has linear form
//
- // Topological sort algorithm
- // 1. Iterate with DFS
- // 2. Append the node to vector when DFS for the node finishes(post order)
- // 3. Reverse the order of nodes
-
- graph::Graph::PostDfsConstIterator().iterate(
- graph, [&](const model::operation::Index &index, const model::operation::Node &node) {
- const auto lower_info = graph.getLowerInfo(index);
- _operations.emplace_back(&node, lower_info);
- });
+ // algorithm
+ // 0. Create new subgraph
+ // 1. Add a node into current subgraph
+ // 2. Test two stuff for checking new subgraph is needed
+ // - Current node has multiple inputs like concat?
+ // - Does current node have two or more than previous operation?
+ //
+ // [CONV] [CONV] [CONV] [MAX_POOL]
+ // | | | |
+ // [0] [1] [2] [3]
+ // \ | | /
+ // [ C O N C A T ] # current node
+ //
+ // - Current node is on the separated branch at the beginning?
+ // - Does current node's input operand's uses have two or more than?
+ //
+ // [CONV]
+ // |
+ // [0]----.
+ // | |
+ // [CONV] [CONV] # current node
+ // | |
+ // [1] [2]
+ // \ /
+ // [CONCAT]
+ //
+ // 3. If needed, push current subgraph to the set and create new subgraph
+
+ auto subgraph_set =
+ nnfw::cpp14::make_unique<std::vector<std::unique_ptr<model::operation::Subgraph>>>();
+ {
+ std::unique_ptr<model::operation::Subgraph> subgraph = nullptr;
+ graph::Graph::PostDfsConstIterator().iterate(
+ graph, [&](const model::operation::Index &index, const model::operation::Node &node) {
+
+ if (!subgraph)
+ subgraph = nnfw::cpp14::make_unique<model::operation::Subgraph>();
+
+ subgraph->appendOperation(index, node);
+
+ bool new_subgraph = false;
+ size_t prev_op_cnt = 0;
+ for (auto input : node.getInputs())
+ {
+ const auto &operand = graph.operands().at(input);
+ if (operand.getDef().list().size() > 0)
+ ++prev_op_cnt;
+
+ if (prev_op_cnt > 1 || operand.getUses().list().size() > 1)
+ {
+ new_subgraph = true;
+ break;
+ }
+ }
+
+ if (new_subgraph)
+ {
+ subgraph_set->emplace_back(std::move(subgraph));
+ subgraph = nullptr;
+ }
+ });
+
+ // If the last subgraph leaves, append it to the subgraph set
+ if (subgraph && subgraph->operations().size() > 0)
+ subgraph_set->emplace_back(std::move(subgraph));
+
+ // NOTE. Now these subgraph are on the reverse order
+ }
- std::reverse(std::begin(_operations), std::end(_operations));
+ // Set input/output of each subgraph while reversing
+ std::reverse(subgraph_set->begin(), subgraph_set->end());
+ for (auto &subgraph : *subgraph_set)
+ {
+ // output
+ auto it = std::begin(subgraph->operations());
+ subgraph->setOutputs((*it).node->getOutputs());
+
+ std::reverse(std::begin(subgraph->operations()), std::end(subgraph->operations()));
+
+ // input
+ it = std::begin(subgraph->operations());
+ subgraph->setInputs((*it).node->getInputs());
+ }
+
+ // Now ordered subgraphs are ready
+ for (auto &subgraph : *subgraph_set)
+ _elements.emplace_back(std::move(subgraph));
+
+ VERBOSE(LINEAR) << "Subgraphs" << std::endl;
+ for (const auto &element : _elements)
+ VERBOSE(LINEAR) << element.subgraph->getStr() << std::endl;
}
void Linear::accept(model::operation::NodeVisitor &&visitor) const
{
- for (const auto op : _operations)
+ for (const auto &e : _elements)
{
- op.node->accept(std::move(visitor));
+ e.subgraph->accept(std::move(visitor));
}
}
// 2. Scan DEF of outputs. If the DEF, allocate it
VERBOSE(LINEAR) << "TENSORS" << std::endl;
const auto &operands = _graph.operands();
- for (const auto op : _operations)
+ for (const auto &e : _elements)
{
- for (const auto &ind : op.node->getOutputs())
+ for (const auto &op : e.subgraph->operations())
{
- const auto &obj = operands.at(ind);
- if (obj.getDef().size())
+ for (const auto &ind : op.node->getOutputs())
{
- iterTensorBuilders(ind,
- [](const model::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
- tensor_builder->notifyFirstUse(ind);
- });
+ const auto &obj = operands.at(ind);
+ if (obj.getDef().size())
+ {
+ iterTensorBuilders(
+ ind, [](const model::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
+ tensor_builder->notifyFirstUse(ind);
+ });
+ }
}
- }
- for (const auto &ind : op.node->getInputs())
- {
- uses_map[ind]--;
- if (uses_map[ind] == 0)
+ for (const auto &ind : op.node->getInputs())
{
- iterTensorBuilders(ind,
- [](const model::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
- tensor_builder->notifyLastUse(ind);
- });
+ uses_map[ind]--;
+ if (uses_map[ind] == 0)
+ {
+ iterTensorBuilders(
+ ind, [](const model::operand::Index &ind, ITensorBuilderPtr tensor_builder) {
+ tensor_builder->notifyLastUse(ind);
+ });
+ }
}
}
}
void Linear::iterate(const std::function<void(const Element &element)> &fn) const
{
- for (const auto op : _operations)
+ for (const auto &e : _elements)
{
- fn(op);
+ fn(e);
}
}