--- /dev/null
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(locop STATIC ${SOURCES})
+set_target_properties(locop PROPERTIES POSITION_INDEPENDENT_CODE ON)
+target_include_directories(locop PUBLIC include)
+target_link_libraries(locop PUBLIC loco)
+# Let's apply nncc common compile options
+#
+# NOTE This will enable strict compilation (warnings as error).
+# Please refer to the top-level CMakeLists.txt for details
+target_link_libraries(locop PRIVATE nncc_common)
+target_link_libraries(locop PUBLIC nncc_coverage)
+target_link_libraries(locop PRIVATE pp)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+# Google Test is mandatory for internal testing
+nncc_find_package(GTest REQUIRED)
+
+GTest_AddTest(locop_test ${TESTS})
+target_link_libraries(locop_test stdex)
+target_link_libraries(locop_test locop)
--- /dev/null
+# locop
+
+_locop_ is a collection of _loco_ pretty printers.
--- /dev/null
+#ifndef __LOCOP_FORMATTED_GRAPH_H__
+#define __LOCOP_FORMATTED_GRAPH_H__
+
+#include <loco.h>
+
+#include <ostream>
+
+namespace locop
+{
+
+struct FormattedGraph
+{
+ virtual ~FormattedGraph() = default;
+
+ virtual void dump(std::ostream &os) const = 0;
+};
+
+std::ostream &operator<<(std::ostream &, const FormattedGraph &);
+
+enum Formatter
+{
+ LinearV1,
+ // TO BE ADDED
+};
+
+template <Formatter F> class FormattedGraphImpl;
+
+template <> class FormattedGraphImpl<Formatter::LinearV1> final : public FormattedGraph
+{
+public:
+ FormattedGraphImpl(loco::Graph *graph) : _graph{graph} {}
+
+public:
+ void dump(std::ostream &os) const final;
+
+private:
+ loco::Graph *_graph;
+};
+
+template <Formatter F> FormattedGraphImpl<F> fmt(loco::Graph *g)
+{
+ return FormattedGraphImpl<F>{g};
+}
+
+template <Formatter F> FormattedGraphImpl<F> fmt(const std::unique_ptr<loco::Graph> &g)
+{
+ return fmt<F>(g.get());
+}
+
+} // namespace locop
+
+#endif // __LOCOP_FORMATTED_GRAPH_H__
--- /dev/null
+#include "locop/FormattedGraph.h"
+
+#include <pp/Format.h>
+
+#include <map>
+#include <set>
+
+namespace
+{
+
+std::string opname(loco::Node *node)
+{
+ // TODO Use Visitor later
+ if (dynamic_cast<loco::Push *>(node))
+ {
+ return "Push";
+ }
+
+ if (dynamic_cast<loco::Pull *>(node))
+ {
+ return "Pull";
+ }
+
+ if (dynamic_cast<loco::MaxPool2D *>(node))
+ {
+ return "MaxPool2D";
+ }
+
+ if (dynamic_cast<loco::FeatureEncode *>(node))
+ {
+ return "FeatureEncode";
+ }
+
+ return "Unknown";
+}
+
+} // namespace
+
+namespace locop
+{
+
+std::ostream &operator<<(std::ostream &os, const FormattedGraph &fmt)
+{
+ fmt.dump(os);
+ return os;
+}
+
+} // namespace locop
+
+namespace locop
+{
+
+void FormattedGraphImpl<Formatter::LinearV1>::dump(std::ostream &os) const
+{
+ // TODO Introduce SymolTable
+ std::map<loco::Node *, std::string> symbols;
+
+ auto symbol = [&symbols](loco::Node *node) -> std::string {
+ if (node == nullptr)
+ {
+ return "(null)";
+ }
+
+ return symbols.at(node);
+ };
+
+ for (uint32_t n = 0; n < _graph->nodes()->size(); ++n)
+ {
+ symbols[_graph->nodes()->at(n)] = pp::fmt("%", n);
+ }
+
+ // Find the disjoint node clusters
+ //
+ // TODO Move this implementation into loco Algorithms.h
+ std::map<loco::Node *, loco::Node *> parents;
+
+ for (auto node : loco::all_nodes(_graph))
+ {
+ parents[node] = nullptr;
+ }
+
+ for (auto node : loco::all_nodes(_graph))
+ {
+ for (uint32_t n = 0; n < node->arity(); ++n)
+ {
+ if (auto arg = node->arg(n))
+ {
+ parents[arg] = node;
+ }
+ }
+ }
+
+ auto find = [&parents](loco::Node *node) {
+ loco::Node *cur = node;
+
+ while (parents.at(cur) != nullptr)
+ {
+ cur = parents.at(cur);
+ }
+
+ return cur;
+ };
+
+ std::set<loco::Node *> roots;
+
+ for (auto node : loco::all_nodes(_graph))
+ {
+ roots.insert(find(node));
+ }
+
+ std::map<loco::Node *, std::set<loco::Node *>> clusters;
+
+ // Create clusters
+ for (auto root : roots)
+ {
+ clusters[root] = std::set<loco::Node *>{};
+ }
+
+ for (auto node : loco::all_nodes(_graph))
+ {
+ clusters.at(find(node)).insert(node);
+ }
+
+ for (auto it = clusters.begin(); it != clusters.end(); ++it)
+ {
+ std::vector<loco::Node *> cluster_outputs;
+
+ for (auto node : it->second)
+ {
+ // NOTE This is inefficient but anyway working :)
+ if (loco::succs(node).empty())
+ {
+ cluster_outputs.emplace_back(node);
+ }
+ }
+
+ // TODO How to print arguments
+ for (auto node : loco::postorder_traversal(cluster_outputs))
+ {
+ os << symbol(node) << " = " << opname(node) << "(";
+ for (uint32_t n = 0; n < node->arity(); ++n)
+ {
+ os << " " << symbol(node->arg(n));
+ }
+ os << " )" << std::endl;
+ }
+ os << std::endl;
+ }
+}
+
+} // namespace locop
--- /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 "locop/FormattedGraph.h"
+
+#include <gtest/gtest.h>
+
+TEST(LinearV1FormatterTest, simple)
+{
+ auto g = loco::make_graph();
+ {
+ auto pull = g->nodes()->create<loco::Pull>();
+ auto push = g->nodes()->create<loco::Push>();
+
+ push->from(pull);
+ }
+
+ // TODO Validate the output (when the implementation becomes stable)
+ std::cout << locop::fmt<locop::LinearV1>(g) << std::endl;
+}