+++ /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.
- */
-
-#include "ConstantFoldingTransform.h"
-
-#include <loco.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <moco/Log.h>
-#include <stdex/Memory.h>
-
-#include <locomotiv/Session.h>
-
-#include <cassert>
-#include <stdexcept>
-
-namespace
-{
-
-using namespace moco::tf;
-
-uint64_t num_elements(const loco::NodeMixin<loco::NodeTrait::TensorShape> &shape)
-{
- if (shape.rank() == 0)
- {
- return 0;
- }
-
- uint64_t res = 1;
-
- for (uint32_t axis = 0; axis < shape.rank(); ++axis)
- {
- assert(shape.dim(axis).known());
- res *= shape.dim(axis).value();
- }
-
- return res;
-}
-
-/// @brief For some op, constant folding should not be performed. This returns true if node is such
-/// op.
-bool skip(const loco::Node *node)
-{
- static std::set<uint32_t> skip_op = {
- // TODO Current implementation works for 'Tensor' domain only. Support other domains such as
- // `Feature`, `Filter`, `Bias`, etc.
- static_cast<uint32_t>(loco::CanonicalOpcode::FilterEncode),
- static_cast<uint32_t>(loco::CanonicalOpcode::FeatureEncode),
- static_cast<uint32_t>(loco::CanonicalOpcode::BiasEncode),
- static_cast<uint32_t>(loco::CanonicalOpcode::DepthwiseFilterEncode),
-
- // We don't perform constant folding for Push
- static_cast<uint32_t>(loco::CanonicalOpcode::Push),
- };
-
- if (node->dialect() == loco::CanonicalDialect::get())
- {
- if (skip_op.find(node->opnum()) != skip_op.end())
- return true;
- }
-
- return false;
-}
-
-/// @brief Checks if a node is a target of constant folding transform
-bool foldable(const loco::Node *node)
-{
- if (node->dialect() == loco::CanonicalDialect::get())
- {
- if (skip(node))
- return false;
-
- if (node->arity() == 0) // e.g., when a node is e.g, ConstGen or Pull
- return false;
-
- // When all args are ConstGen, let's do Constant Folding Transforms
- for (int i = 0; i < node->arity(); i++)
- {
- if (node->arg(i)->opnum() != static_cast<uint32_t>(loco::CanonicalOpcode::ConstGen))
- return false;
- }
-
- return true;
- }
- else
- {
- return false;
- }
-}
-
-void fold(loco::Graph *graph, loco::Node *node)
-{
- assert(foldable(node)); // sanity check to find a mistake when this function is reused later
-
- // calcluate foldable node
- locomotiv::Session sess(graph, std::vector<loco::Node *>{node});
- sess.infer();
- auto data = sess.get_output(0);
-
- assert(data != nullptr);
-
- auto shape = data->shape();
- auto dtype = data->dtype();
-
- // build ConstGen
- auto new_const = graph->nodes()->create<loco::ConstGen>();
- {
- new_const->dtype(dtype);
-
- new_const->rank(shape->rank());
- for (int d = 0; d < shape->rank(); d++)
- new_const->dim(d) = shape->dim(d);
-
- auto count = num_elements(*new_const);
-
- if (dtype == loco::DataType::FLOAT32)
- {
- new_const->size<loco::DataType::FLOAT32>(count);
-
- auto const_buf = data->as_f32_bufptr()->base();
- for (int x = 0; x < count; x++)
- new_const->at<loco::DataType::FLOAT32>(x) = const_buf[x];
- }
- else if (dtype == loco::DataType::S32)
- {
- new_const->size<loco::DataType::S32>(count);
-
- auto const_buf = data->as_s32_bufptr()->base();
- for (int x = 0; x < count; x++)
- new_const->at<loco::DataType::S32>(x) = const_buf[x];
- }
- }
-
- // replace node with new_const
- loco::replace(node).with(new_const);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool ConstantFoldingTransform::run(loco::Graph *graph)
-{
- auto outputs = loco::output_nodes(graph);
-
- bool changed = false;
- for (auto node : loco::postorder_traversal(outputs))
- {
- if (foldable(node))
- {
- fold(graph, node);
- changed = true;
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
+++ /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 __MOCO_TF_CONSTANT_FOLDING_TRANSFORM_H__
-#define __MOCO_TF_CONSTANT_FOLDING_TRANSFORM_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Performs constant folding optimization
- */
-class ConstantFoldingTransform : public Transform
-{
-public:
- const char *name(void) const final { return "ConstantFoldingTransform"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_CONSTANT_FOLDING_TRANSFORM_H__
+++ /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.
- */
-
-#include "ConstantFoldingTransform.h"
-
-#include "LogHelper.h"
-#include "TestHelper.h"
-#include "IR/TFFusedBatchNorm.h"
-#include "Importer.h"
-#include "Canonicalizer.h"
-
-#include <loco.h>
-#include <moco/Log.h>
-#include <plier/tf/TestHelper.h>
-
-#include <gtest/gtest.h>
-
-using namespace moco::tf::test;
-
-namespace
-{
-// clang-format off
-
-/*
- test case:
- ConstGen -- Relu -- Push
-
- after constant folding
- ConstGen -- Push
-*/
-const char *case01 = STRING_CONTENT(
-node {
- name: "input"
- op: "Const"
- attr {
- key: "dtype" value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape { dim { size: 2 } }
- float_val: -3.14
- float_val: 3.14
- }
- }
- }
-}
-node {
- name: "relu"
- op: "Relu"
- input: "input"
- attr { key: "T" value { type: DT_FLOAT } }
-}
-);
-// clang-format on
-
-} // namespace
-
-namespace
-{
-
-char to_char(bool b) { return b ? 'Y' : 'N'; }
-
-} // namespace
-
-TEST(ConstantFolding, case01)
-{
- LOGGER(l);
-
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("relu", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(case01, graph_def));
- auto graph = importer.import(signature, graph_def);
-
- // Convert graph to hold only Canonical dialect
- moco::tf::Canonicalizer canonicalizer;
- canonicalizer.canonicalize(graph.get());
-
- INFO(l) << "Before ConstantFolding";
- INFO(l) << moco::tf::fmt(graph);
-
- moco::tf::ConstantFoldingTransform transform;
- while (transform.run(graph.get()) == true)
- {
- INFO(l) << "running ConstantFolding...";
- }
-
- INFO(l) << "After ConstantFolding ";
- INFO(l) << moco::tf::fmt(graph);
-
- auto push = moco::tf::test::find_first_node_bytype<loco::Push>(graph.get());
- auto const_gen = dynamic_cast<loco::ConstGen *>(push->from());
-
- ASSERT_NE(const_gen, nullptr);
- ASSERT_EQ(const_gen->size<loco::DataType::FLOAT32>(), 2);
- ASSERT_EQ(const_gen->at<loco::DataType::FLOAT32>(0), 0); // result of relu (-3.14)
- ASSERT_EQ(const_gen->at<loco::DataType::FLOAT32>(1), 3.14f);
-}
-
-namespace
-{
-// clang-format off
-
-/*
- test case:
-
- input1 (Const) -- Relu -+
- (-1, 1) (0, 1) |
- input2 (Const) -+-- ConcatV2
- (2, 3) | (0, 1, 2, 3)
- axis (Const)-+
-
- after constant folding:
-
- ConstGen ------Push
- (0, 1, 2, 3)
-*/
-const char *case02 = STRING_CONTENT(
-node {
- name: "input1"
- op: "Const"
- attr {
- key: "dtype" value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape { dim { size: 2 } }
- float_val: -1
- float_val: 1
- }
- }
- }
-}
-node {
- name: "relu"
- op: "Relu"
- input: "input1"
- attr { key: "T" value { type: DT_FLOAT } }
-}
-node {
- name: "input2"
- op: "Const"
- attr {
- key: "dtype" value { type: DT_FLOAT }
- }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_FLOAT
- tensor_shape { dim { size: 2 } }
- float_val: 2
- float_val: 3
- }
- }
- }
-}
-node {
- name: "axis"
- op: "Const"
- attr { key: "dtype" value { type: DT_INT32 } }
- attr {
- key: "value"
- value {
- tensor {
- dtype: DT_INT32
- tensor_shape { }
- int_val: 0
- }
- }
- }
-}
-node {
- name: "concat"
- op: "ConcatV2"
- input: "relu"
- input: "input2"
- input: "axis"
- attr {
- key: "N"
- value { i: 2 }
- }
- attr {
- key: "T"
- value { type: DT_FLOAT }
- }
- attr {
- key: "Tidx"
- value { type: DT_INT32 }
- }
-}
-);
-// clang-format on
-} // namespace
-
-TEST(ConstantFolding, case02)
-{
- LOGGER(l);
-
- // load graph
- moco::tf::Importer importer;
- moco::tf::ModelSignature signature;
- signature.add_output(moco::tf::TensorName("concat", 0));
-
- tensorflow::GraphDef graph_def;
- EXPECT_TRUE(plier::tf::parse_graphdef(case02, graph_def));
- auto graph = importer.import(signature, graph_def);
-
- // Convert graph to hold only Canonical dialect
- moco::tf::Canonicalizer canonicalizer;
- canonicalizer.canonicalize(graph.get());
-
- INFO(l) << "Before ConstantFolding";
- INFO(l) << moco::tf::fmt(graph);
-
- moco::tf::ConstantFoldingTransform transform;
- while (transform.run(graph.get()) == true)
- {
- INFO(l) << "running ConstantFolding...";
- }
-
- INFO(l) << "After ConstantFolding ";
- INFO(l) << moco::tf::fmt(graph);
-
- auto push = moco::tf::test::find_first_node_bytype<loco::Push>(graph.get());
- auto const_gen = dynamic_cast<loco::ConstGen *>(push->from());
-
- ASSERT_NE(const_gen, nullptr);
- ASSERT_EQ(const_gen->size<loco::DataType::FLOAT32>(), 4);
- ASSERT_EQ(const_gen->at<loco::DataType::FLOAT32>(0), 0);
- ASSERT_EQ(const_gen->at<loco::DataType::FLOAT32>(1), 1);
- ASSERT_EQ(const_gen->at<loco::DataType::FLOAT32>(2), 2);
- ASSERT_EQ(const_gen->at<loco::DataType::FLOAT32>(3), 3);
-}
+++ /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.
- */
-
-#include "RemoveDeadNodeTransform.h"
-
-#include <loco/IR/Algorithm.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/IR/CanonicalNode.h>
-
-#include <set>
-
-namespace moco
-{
-namespace tf
-{
-
-bool RemoveDeadNodeTransform::run(loco::Graph *g)
-{
- // Let's enumerate nodes required to compute output nodes
- auto active_nodes = loco::active_nodes(loco::output_nodes(g));
-
- // Find dead(= non-active) nodes
- std::set<loco::Node *> candidates;
-
- for (auto node : loco::all_nodes(g))
- {
- if (active_nodes.find(node) == active_nodes.end())
- {
- candidates.insert(node);
- }
- }
-
- // Let's drop the references from each dead node first and then remove these dead nodes
- //
- // Why?
- //
- // Let us consider the following example:
- // %0 = Pull(...)
- // %1 = ConstGen(...)
- // %2 = Forward(input: %1)
- // %3 = Push(from: %0) <- OUTPUT
- //
- // Forward (%2) is dead as it does not contribute to the final result (%3). However, it
- // refers to another dead node (%1).
- //
- // This example indicates that naive implementation results in dangling references.
- //
- // There are two possible solutions:
- // 1. Destroy nodes in topological order
- // 2. Drop the reference first and then destroy them
- //
- // The current implementation takes the latter approach for the simplicity of implementation.
- for (auto node : candidates)
- {
- node->drop();
- }
-
- for (auto node : candidates)
- {
- g->nodes()->destroy(node);
- }
-
- return candidates.size() > 0;
-}
-
-} // namespace tf
-} // namespace moco
+++ /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 __MOCO_TF_REMOVE_DEAD_NODE_TRANSFORM_H__
-#define __MOCO_TF_REMOVE_DEAD_NODE_TRANSFORM_H__
-
-#include "Transform.h"
-
-namespace moco
-{
-namespace tf
-{
-
-struct RemoveDeadNodeTransform final : public Transform
-{
- const char *name(void) const final { return "RemoveDeadNodeTransform"; }
-
- bool run(loco::Graph *g);
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_REMOVE_DEAD_NODE_TRANSFORM_H__
+++ /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.
- */
-
-#include "RemoveForwardNodeTransform.h"
-
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/IR/CanonicalNode.h>
-
-#include <set>
-
-namespace moco
-{
-namespace tf
-{
-
-bool RemoveForwardNodeTransform::run(loco::Graph *g)
-{
- struct Collector final : public loco::CanonicalNodeMutableVisitor<void>
- {
- void visit(loco::Forward *node) final
- {
- if (node->input() != nullptr)
- {
- candidates.insert(node);
- }
- }
-
- void visit(loco::Node *) final { return; }
-
- std::set<loco::Forward *> candidates;
- };
-
- Collector collector;
-
- for (auto node : loco::all_nodes(g))
- {
- if (node->dialect() == loco::CanonicalDialect::get())
- {
- auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node);
- canonical_node->accept(&collector);
- }
- }
-
- for (auto node : collector.candidates)
- {
- replace(node).with(node->input());
- node->input(nullptr);
- }
-
- return collector.candidates.size() > 0;
-}
-
-} // namespace tf
-} // namespace moco
+++ /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 __MOCO_TF_REMOVE_FORWARD_NODE_TRANSFORM_H__
-#define __MOCO_TF_REMOVE_FORWARD_NODE_TRANSFORM_H__
-
-#include "Transform.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Use the input of "Forward" node instead
- *
- * BEFORE:
- * [X] -> [Forward] -> [Y]
- *
- * AFTER:
- * [X] -> [Y]
- * [Forward]
- *
- * NOTE This transform does not remove "Forward" node
- */
-struct RemoveForwardNodeTransform final : public Transform
-{
- const char *name(void) const final { return "RemoveForwardNodeTransform"; }
-
- bool run(loco::Graph *g) final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_REMOVE_FORWARD_NODE_TRANSFORM_H__
+++ /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.
- */
-
-#include "ReorderDecodeTransform.h"
-
-#include "Knob.h"
-
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/IR/CanonicalNode.h>
-
-#include <cassert>
-#include <queue>
-
-namespace
-{
-
-bool isTensorBiasAdd(const loco::Node *node)
-{
- return node->opnum() == static_cast<uint32_t>(loco::CanonicalOpcode::TensorBiasAdd);
-}
-
-bool isReLU(const loco::Node *node)
-{
- return node->opnum() == static_cast<uint32_t>(loco::CanonicalOpcode::ReLU);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool ReorderDecodeTransform::run(loco::Graph *g)
-{
- std::queue<loco::FeatureDecode *> q;
-
- // Update queue
- class Collector final : public loco::CanonicalNodeMutableVisitor<void>
- {
- public:
- Collector(std::queue<loco::FeatureDecode *> *out) : _out{out}
- {
- // DO NOTHING
- }
-
- void visit(loco::FeatureDecode *node) final
- {
- if (node->input() != nullptr)
- {
- _out->push(node);
- }
- }
-
- void visit(loco::Node *) final { return; }
-
- private:
- // TODO This definition should be revised to support other decode operations
- std::queue<loco::FeatureDecode *> *_out;
- };
-
- Collector collector{&q};
-
- for (auto node : loco::all_nodes(g))
- {
- if (node->dialect() == loco::CanonicalDialect::get())
- {
- auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node);
- canonical_node->accept(&collector);
- }
- }
-
- bool changed = false;
-
- while (!q.empty())
- {
- auto cur_decode = q.front();
- q.pop();
-
- // Collector IS EXPECTED TO guarantee this property
- assert(cur_decode->input() != nullptr);
-
- for (auto u : loco::succs(cur_decode))
- {
- /**
- * Let us consider the following graph:
- *
- * A ---> FeatureDecode(1) ---> ReLU(2)
- *
- * ReorderDecodeTransform rewrites this graph as follows:
- *
- * A -+-> FeatureDecode(1) ---> ReLU(2)
- * |
- * +-> ReLU(2') ---> FeatureDecode(1')
- *
- * Let us feed this updates graph to ReorderDecodeTransform.
- *
- * The naive implementation will create a new ReLU->FeatureDecode
- * chain again, and results in unbounded graph blow-up.
- *
- * A -+-> FeatureDeocde(1) ---> ReLU(2)
- * |
- * +-> ReLU(2') ---> FeatureDecode(1')
- * |
- * +-> ReLU(2'') ---> FeatureDecode(1'')
- *
- * This check prevents such unbounded graph blow-up.
- */
- if (loco::succs(u).empty())
- {
- continue;
- }
-
- // TODO Make it easy to extend
- if (u->dialect() == loco::CanonicalDialect::get())
- {
- // Q. Is it better to create an independent transform for this rewriting rule?
- if (isTensorBiasAdd(u) && get<Knob::ReorderDecodeTensorBiasAdd>())
- {
- auto old_badd = dynamic_cast<loco::TensorBiasAdd *>(u);
-
- assert(old_badd != nullptr);
-
- /**
- * Let us consider the following example:
- *
- * A -=-> FeatureDecode(1) -+-> TensorBiasAdd(2) -+-> B1
- * | |
- * | +-> B2
- * | |
- * | +-> ...
- * |
- * +-> ...
- *
- * At this point, "cur_decode" points to (1) and "u" points to (2).
- *
- * First rewrite the graph as follows:
- *
- * A -+-> FeatureBiasAdd(2') ---> FeatureDecode(1') -+-> B1
- * | |
- * | +-> B2
- * | |
- * | +-> ...
- * |
- * +-> FeatureDecode(1) -+-> TensorBiasAdd(2) ; NO USE
- * |
- * +-> ...
- *
- * Q. Is it safe to apply this transform without "decoder" check?
- */
- auto new_badd = g->nodes()->create<loco::FeatureBiasAdd>();
- auto new_decode = g->nodes()->create<loco::FeatureDecode>();
-
- new_badd->value(cur_decode->input());
- new_badd->bias(old_badd->bias());
-
- new_decode->input(new_badd);
- new_decode->decoder(cur_decode->decoder()->clone());
-
- loco::replace(u).with(new_decode);
-
- // Enque FeatureDeocde(1') for the further optimization.
- q.push(new_decode);
-
- changed = true;
- continue;
- }
-
- if (isReLU(u) && get<Knob::ReorderDecodeReLU>())
- {
- /**
- * Let us consider the following example:
- *
- * A -=-> FeatureDecode(1) -+-> ReLU(2) -+-> B1
- * | |
- * | +-> B2
- * | |
- * | +-> ...
- * |
- * +-> ...
- *
- * At this point, "cur_decode" points to FeatureDecode(1) and "u" points to ReLU(2).
- *
- * First rewrite the graph as follows:
- *
- * A -+-> ReLU(2') ---> FeatureDecode(1') -+-> B1
- * | |
- * | +-> B2
- * | |
- * | +-> ...
- * |
- * +-> FeatureDecode -+-> ReLU(2) ; NO USE
- * |
- * +-> ...
- */
- auto new_relu = g->nodes()->create<loco::ReLU>();
- auto new_decode = g->nodes()->create<loco::FeatureDecode>();
-
- new_relu->input(cur_decode->input());
-
- new_decode->input(new_relu);
- new_decode->decoder(cur_decode->decoder()->clone());
-
- loco::replace(u).with(new_decode);
-
- /**
- * Enque FeatureDeocde(1') for the further optimization.
- */
- q.push(new_decode);
-
- changed = true;
- }
- }
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
+++ /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 __MOCO_TF_REORDER_DECODE_TRANSFORM_H__
-#define __MOCO_TF_REORDER_DECODE_TRANSFORM_H__
-
-#include "Transform.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Reorder XXXDecode -> ? as ? -> XXXDecode if possible
- *
- * This transformation increases the chance of domain conversion simplification.
- */
-struct ReorderDecodeTransform final : public Transform
-{
- const char *name(void) const final { return "ReorderDecodeTransform "; }
-
- bool run(loco::Graph *g);
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_REORDER_DECODE_TRANSFORM_H__
+++ /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.
- */
-
-#include "ResolveDuplicateReshape.h"
-
-#include <loco.h>
-
-#include <cassert>
-
-namespace
-{
-
-/// @return true when 'node' and its input node are both FixedReshapes
-bool is_duplicate_reshape(loco::Node *node)
-{
- auto node_as_reshape = dynamic_cast<loco::FixedReshape *>(node);
-
- if (!node_as_reshape)
- return false;
-
- auto input_as_reshape = dynamic_cast<loco::FixedReshape *>(node_as_reshape->input());
-
- if (!input_as_reshape)
- return false;
-
- return true;
-}
-
-/**
- * @brief Remap reshape's input to its input's input, i.e. bypass input reshape
- *
- * Before:
- *
- * In ----- FixedReshape_1 ----- [Out_1]*
- * \
- * ------- FixedReshape_2 --- [Out_2]*
- * ('reshape' arg)
- *
- * After:
- *
- * In ----- FixedReshape_1 ----- [Out_1]*
- * \
- * --------------------------- FixedReshape_2 --- [Out_2]*
- *
- * Note: In case of no Out_1, FixedReshape_1 becomes dead node.
- * Out_1 can be another FixedReshape as well, which would be resolved in
- * another occurance of this transform pass.
- */
-void remap_input(loco::FixedReshape *reshape)
-{
- auto input_reshape = dynamic_cast<loco::FixedReshape *>(reshape->input());
-
- auto volume = [](loco::FixedReshape *node) {
- uint32_t vol = 1;
- for (uint32_t axis = 0; axis < node->rank(); ++axis)
- {
- assert(node->dim(axis).known());
- vol *= node->dim(axis).value();
- }
- return vol;
- };
-
- // Volume mismatch between duplicate reshapes is pointless
- assert(volume(reshape) == volume(input_reshape));
-
- // Set node's input as input's input, i.e. bypass
- reshape->input(input_reshape->input());
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-bool ResolveDuplicateReshape::run(loco::Graph *graph)
-{
- auto outputs = loco::output_nodes(graph);
-
- bool changed = false;
- for (auto node : loco::postorder_traversal(outputs))
- {
- if (is_duplicate_reshape(node))
- {
- auto node_as_reshape = dynamic_cast<loco::FixedReshape *>(node);
-
- remap_input(node_as_reshape);
-
- changed = true;
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
+++ /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 __MOCO_TF_RESOLVE_DUPLICATE_RESHAPE_H__
-#define __MOCO_TF_RESOLVE_DUPLICATE_RESHAPE_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Resolve duplicated Reshape nodes in a row
- */
-class ResolveDuplicateReshape : public Transform
-{
-public:
- const char *name(void) const final { return "ResolveDuplicateReshape"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_RESOLVE_DUPLICATE_RESHAPE_H__
+++ /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.
- */
-
-#include "ResolveRedundantReshape.h"
-
-#include <loco/Service/ShapeInference.h>
-
-#include <loco.h>
-
-#include <cassert>
-
-namespace
-{
-
-bool shape_inference_done(loco::FixedReshape *reshape)
-{
- return loco::shape_known(reshape) && loco::shape_known(reshape->input());
-}
-
-bool are_same_tensor_shapes(const loco::NodeShape &lhs, const loco::NodeShape &rhs)
-{
- assert(lhs.domain() == loco::Domain::Tensor);
- assert(rhs.domain() == loco::Domain::Tensor);
-
- auto lts = lhs.as<loco::TensorShape>();
- auto rts = rhs.as<loco::TensorShape>();
-
- if (lts.rank() != rts.rank())
- return false;
-
- for (uint32_t axis = 0; axis < lts.rank(); ++axis)
- {
- assert(lts.dim(axis).known());
- assert(rts.dim(axis).known());
- if (lts.dim(axis).value() != rts.dim(axis).value())
- return false;
- }
- return true;
-}
-
-/// @return true when 'reshape' has same input and output shape
-bool is_redundant_reshape(loco::FixedReshape *reshape)
-{
- auto input_shape = loco::shape_get(reshape->input());
- auto output_shape = loco::shape_get(reshape);
-
- // Note that FixedReshape's input and output are always tensor
- return are_same_tensor_shapes(input_shape, output_shape);
-}
-
-} // namespace
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Bypass redundant FixedReshape
- *
- * Before:
- *
- * In ----- FixedReshape ----- [Out]*
- *
- * After:
- *
- * In ------------------------ [Out]*
- * \
- * ------ FixedReshape
- */
-bool ResolveRedundantReshape::run(loco::Graph *graph)
-{
- bool changed = false;
- for (auto node : loco::postorder_traversal(loco::output_nodes(graph)))
- {
- if (auto reshape = dynamic_cast<loco::FixedReshape *>(node))
- {
- if (shape_inference_done(reshape))
- {
- if (is_redundant_reshape(reshape))
- {
- replace(reshape).with(reshape->input());
- changed = true;
- }
- }
- }
- }
-
- return changed;
-}
-
-} // namespace tf
-} // namespace moco
+++ /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 __MOCO_TF_RESOLVE_REDUNDANT_RESHAPE_H__
-#define __MOCO_TF_RESOLVE_REDUNDANT_RESHAPE_H__
-
-#include "Transform.h"
-
-#include <loco.h>
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Remove redundant canonical FixedReshape
- *
- * @note To effectively run this transform, canonical shape inference should be
- * done ahead
- */
-class ResolveRedundantReshape : public Transform
-{
-public:
- const char *name(void) const final { return "ResolveRedundantReshape"; }
-
-public:
- bool run(loco::Graph *graph) override;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_RESOLVE_REDUNDANT_RESHAPE_H__
+++ /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.
- */
-
-#include "SimplifyDomainConversionTransform.h"
-
-#include <loco/IR/Algorithm.h>
-#include <loco/IR/CanonicalDialect.h>
-#include <loco/IR/CanonicalNode.h>
-
-#include <set>
-#include <cassert>
-
-namespace moco
-{
-namespace tf
-{
-
-bool SimplifyDomainConversionTransform::run(loco::Graph *g)
-{
- // Let's find FeatureDecode followed by FeatureEncode
- //
- // TODO Introduce and Use "Pattern Match"
- struct Collector final : public loco::CanonicalNodeMutableVisitor<void>
- {
- void visit(loco::FeatureEncode *encode_node) final
- {
- 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)
- {
- return;
- }
- assert(decode_node->input() != nullptr);
-
- auto decoder = decode_node->decoder();
- assert(decoder != nullptr);
-
- // NOTE Work only for permuting codec
- auto perm_decoder = dynamic_cast<const PermutingDecoder<Domain::Feature> *>(decoder);
- auto perm_encoder = dynamic_cast<const PermutingEncoder<Domain::Feature> *>(encoder);
-
- if (perm_encoder == nullptr || perm_decoder == nullptr)
- {
- return;
- }
-
- // TODO Move this helper into loco
- auto equal = [](const Permutation<Domain::Feature> *lhs,
- const Permutation<Domain::Feature> *rhs) {
- for (const auto &axis :
- {FeatureAxis::Count, FeatureAxis::Depth, FeatureAxis::Height, FeatureAxis::Width})
- {
- if (lhs->axis(axis) != rhs->axis(axis))
- {
- return false;
- }
- }
- return true;
- };
-
- if (equal(perm_encoder->perm(), perm_decoder->perm()))
- {
- candidates.insert({encode_node, decode_node->input()});
- }
- }
-
- void visit(loco::Node *) final { return; }
-
- std::set<std::pair<loco::FeatureEncode *, loco::Node *>> candidates;
- };
-
- Collector collector;
-
- for (auto node : loco::all_nodes(g))
- {
- if (node->dialect() == loco::CanonicalDialect::get())
- {
- auto canonical_node = dynamic_cast<loco::CanonicalNode *>(node);
- canonical_node->accept(&collector);
- }
- }
-
- for (auto p : collector.candidates)
- {
- auto forward_node = g->nodes()->create<loco::Forward>();
- forward_node->input(p.second);
- replace(p.first).with(forward_node);
- p.first->input(nullptr);
- }
-
- return collector.candidates.size() > 0;
-}
-
-} // namespace tf
-} // namespace moco
+++ /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 __MOCO_TF_SIMPLIFY_DOMAIN_CONVERSION_H__
-#define __MOCO_TF_SIMPLIFY_DOMAIN_CONVERSION_H__
-
-#include "Transform.h"
-
-namespace moco
-{
-namespace tf
-{
-
-/**
- * @brief Simplify redundant domain conversion
- *
- * SimplifyDomainConversionTransform recognizes the following patterns:
- * - FeatureDecode followed by FeatureEncode
- * - (TO BE ADDED)
- */
-struct SimplifyDomainConversionTransform final : public Transform
-{
- const char *name(void) const final { return "SimplifyDomainConversion"; }
-
- bool run(loco::Graph *g) final;
-};
-
-} // namespace tf
-} // namespace moco
-
-#endif // __MOCO_TF_SIMPLIFY_DOMAIN_CONVERSION_H__