[graph] Migrate to graph core
authorParichay Kapoor <pk.kapoor@samsung.com>
Mon, 17 May 2021 05:07:43 +0000 (14:07 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Wed, 26 May 2021 00:35:00 +0000 (09:35 +0900)
Migrate neural network independent and generic sections of the graph
to graph core. This is not yet complete and will be done over a few commits.
This will cleanup NetworkGraph class and will allow optimizations to
be done on the GraphCore/NetworkGraph class easily, and simplify
the classes as well.

Now, graph related structures can move out from layer to layer node.

**Self evaluation:**
1. Build test: [x]Passed [ ]Failed [ ]Skipped
2. Run test: [x]Passed [ ]Failed [ ]Skipped

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
jni/Android.mk
nntrainer/graph/graph_core.cpp
nntrainer/graph/graph_core.h
nntrainer/graph/graph_node.h
nntrainer/graph/network_graph.cpp
nntrainer/graph/network_graph.h
nntrainer/layers/layer_node.cpp
nntrainer/layers/layer_node.h
nntrainer/models/model_loader.cpp
nntrainer/models/neuralnet.cpp
nntrainer/models/neuralnet.h

index 3f5ef9f..439b999 100644 (file)
@@ -124,6 +124,7 @@ NNTRAINER_SRCS := $(NNTRAINER_ROOT)/nntrainer/models/neuralnet.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/layers/acti_func.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/layers/common_properties.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/graph/network_graph.cpp \
+                  $(NNTRAINER_ROOT)/nntrainer/graph/graph_core.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/optimizers/optimizer_devel.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/optimizers/optimizer_impl.cpp \
                   $(NNTRAINER_ROOT)/nntrainer/optimizers/adam.cpp \
index f0cb70a..3f45f3a 100644 (file)
@@ -18,6 +18,7 @@
 #include <graph_core.h>
 #include <nntrainer_error.h>
 #include <nntrainer_log.h>
+#include <parse_util.h>
 
 namespace nntrainer {
 
@@ -40,7 +41,7 @@ void GraphCore::addGraphNode(std::shared_ptr<GraphNode> node) {
   adj.push_back(std::list<std::shared_ptr<GraphNode>>({node}));
 }
 
-std::shared_ptr<GraphNode> &GraphCore::getGraphNode(unsigned int ith) {
+std::shared_ptr<GraphNode> &GraphCore::getNode(unsigned int ith) {
   if (ith >= size())
     throw std::invalid_argument("Exceed total number of nodes");
 
@@ -50,7 +51,7 @@ std::shared_ptr<GraphNode> &GraphCore::getGraphNode(unsigned int ith) {
   return adj[ith].front();
 }
 
-std::shared_ptr<GraphNode> &GraphCore::getSortedGraphNode(unsigned int ith) {
+std::shared_ptr<GraphNode> &GraphCore::getSortedNode(unsigned int ith) {
   if (ith >= getSorted().size())
     throw std::invalid_argument("Exceed total number of nodes");
 
@@ -69,7 +70,7 @@ void GraphCore::topologicalSortUtil(
       topologicalSortUtil(index, visited, Stack);
   }
 
-  Stack.push(getGraphNode(ith));
+  Stack.push(getNode(ith));
 }
 
 void GraphCore::topologicalSort() {
@@ -96,10 +97,11 @@ void GraphCore::topologicalSort() {
   }
 }
 
-std::shared_ptr<GraphNode> &GraphCore::getGraphNode(const std::string &name) {
+std::shared_ptr<GraphNode> &GraphCore::getNode(const std::string &name) {
   for (auto &lnode_list : adj) {
     auto &lnode = lnode_list.front();
-    if (lnode->getName() == name)
+    /// TODO: make this name checking case sensitive
+    if (istrequal(lnode->getName(), name))
       return lnode;
   }
 
@@ -108,14 +110,15 @@ std::shared_ptr<GraphNode> &GraphCore::getGraphNode(const std::string &name) {
   throw std::invalid_argument(ss.str());
 }
 
-void GraphCore::addEdge(unsigned int ith, std::shared_ptr<GraphNode> &node) {
+void GraphCore::addEdge(unsigned int ith,
+                        const std::shared_ptr<GraphNode> &node) {
   if (ith >= adj.size())
     throw std::invalid_argument("Exceed total number of nodes");
 
   adj[ith].push_back(node);
 }
 
-std::vector<std::shared_ptr<GraphNode>> GraphCore::getGraphNodes() const {
+std::vector<std::shared_ptr<GraphNode>> GraphCore::getNodes() const {
   std::vector<std::shared_ptr<GraphNode>> ret;
   if (!Sorted.empty()) {
     std::transform(Sorted.begin(), Sorted.end(), std::back_inserter(ret),
@@ -128,9 +131,10 @@ std::vector<std::shared_ptr<GraphNode>> GraphCore::getGraphNodes() const {
   return ret;
 }
 
-void GraphCore::addNode(std::shared_ptr<GraphNode> node) {
+void GraphCore::addNode(std::shared_ptr<GraphNode> node, bool ensure_name) {
   /** Ensure that the node has a name and is unique */
-  ensureName(node);
+  if (ensure_name)
+    ensureName(*node);
 
   /** Insert the node to the graph */
   addGraphNode(node);
@@ -150,10 +154,9 @@ std::vector<std::shared_ptr<GraphNode>> &GraphCore::getSorted() {
   return Sorted;
 }
 
-void GraphCore::ensureName(std::shared_ptr<GraphNode> &node,
-                           const std::string &prefix,
+void GraphCore::ensureName(GraphNode &node, const std::string &prefix,
                            const std::string &postfix, bool force_rename) {
-  std::string orig_name = node->getName();
+  std::string orig_name = node.getName();
   bool orig_name_empty = orig_name.empty();
   /** If node already has name which is unique and valid, and force is
    * disabled, then nothing to do.
@@ -168,7 +171,7 @@ void GraphCore::ensureName(std::shared_ptr<GraphNode> &node,
   if (!orig_name_empty) {
     std::string direct_name = prefix + orig_name + postfix;
     if (node_names.find(direct_name) == node_names.end()) {
-      node->setName(direct_name);
+      node.setName(direct_name);
       node_names.insert(direct_name);
       return;
     }
@@ -177,7 +180,7 @@ void GraphCore::ensureName(std::shared_ptr<GraphNode> &node,
   std::set<std::string>::iterator iter;
   std::string name;
   if (orig_name_empty) {
-    orig_name = node->getType();
+    orig_name = node.getType();
   }
 
   std::string direct_name = prefix + orig_name + postfix;
@@ -187,7 +190,7 @@ void GraphCore::ensureName(std::shared_ptr<GraphNode> &node,
     iter = node_names.find(name);
   } while (iter != node_names.end());
 
-  node->setName(name);
+  node.setName(name);
   node_names.insert(name);
 }
 
index 2dca7ff..7a72073 100644 (file)
@@ -36,15 +36,6 @@ class GraphCore {
 
 public:
   /**
-   * @brief     Iterators to traverse the GraphCore object
-   */
-  typedef typename std::vector<std::shared_ptr<GraphNode>>::const_iterator
-    const_iterator;
-  typedef
-    typename std::vector<std::shared_ptr<GraphNode>>::const_reverse_iterator
-      const_reverse_iterator;
-
-  /**
    * @brief     Constructor of Graph Core Class
    */
   GraphCore() : def_name_count(0) {}
@@ -53,7 +44,7 @@ public:
    * @brief Add the given node into Graph
    * @param[in] node shared_ptr of node
    */
-  void addNode(std::shared_ptr<GraphNode> node);
+  void addNode(std::shared_ptr<GraphNode> node, bool ensure_name = true);
 
   /**
    * @brief getter of number of nodes
@@ -84,7 +75,10 @@ public:
     using std::swap;
 
     swap(lhs.adj, rhs.adj);
+    swap(lhs.node_list, rhs.node_list);
     swap(lhs.Sorted, rhs.Sorted);
+    swap(lhs.node_names, rhs.node_names);
+    swap(lhs.def_name_count, rhs.def_name_count);
   }
 
   /**
@@ -93,28 +87,31 @@ public:
   void reset() {
     adj.clear();
     Sorted.clear();
+    node_names.clear();
+    def_name_count = 0;
   }
 
   /**
    * @brief getter of GraphNode with index number
    * @param[in] index
    * @ret GraphNode
+   * TODO: make this func const
    */
-  std::shared_ptr<GraphNode> &getGraphNode(unsigned int ith);
+  std::shared_ptr<GraphNode> &getNode(unsigned int ith);
 
   /**
    * @brief getter of Sorted GraphNode with index number
    * @param[in] index
    * @ret GraphNode
    */
-  std::shared_ptr<GraphNode> &getSortedGraphNode(unsigned int ith);
+  std::shared_ptr<GraphNode> &getSortedNode(unsigned int ith);
 
   /**
    * @brief getter of GraphNode with node name
    * @param[in] node name
    * @retval GraphNode
    */
-  std::shared_ptr<GraphNode> &getGraphNode(const std::string &name);
+  std::shared_ptr<GraphNode> &getNode(const std::string &name);
 
   /**
    * @brief getter all the node nodes in the model
@@ -123,7 +120,7 @@ public:
    * otherwise the order is the order of addition of node nodes in the model.
    * TODO: deprecate this
    */
-  std::vector<std::shared_ptr<GraphNode>> getGraphNodes() const;
+  std::vector<std::shared_ptr<GraphNode>> getNodes() const;
 
   /**
    * @brief     join passed graph into the existing graph model
@@ -156,25 +153,45 @@ public:
    * @brief     get begin iterator for the forwarding
    * @retval    const iterator marking the begin of forwarding
    */
-  inline const_iterator cbegin() { return Sorted.cbegin(); }
+  template <
+    typename T = GraphNode,
+    std::enable_if_t<std::is_base_of<GraphNode, T>::value, T> * = nullptr>
+  inline graph_iterator<T const> cbegin() {
+    return graph_iterator<T const>(&Sorted[0]);
+  }
 
   /**
    * @brief     get end iterator for the forwarding
    * @retval    const iterator marking the emd of forwarding
    */
-  inline const_iterator cend() { return Sorted.cend(); }
+  template <
+    typename T = GraphNode,
+    std::enable_if_t<std::is_base_of<GraphNode, T>::value, T> * = nullptr>
+  inline graph_iterator<T const> cend() {
+    return graph_iterator<T const>((&Sorted.back()) + 1);
+  }
 
   /**
    * @brief     get begin iterator for the backwarding
    * @retval    const reverse iterator marking the begin of backwarding
    */
-  inline const_reverse_iterator crbegin() { return Sorted.crbegin(); }
+  template <
+    typename T = GraphNode,
+    std::enable_if_t<std::is_base_of<GraphNode, T>::value, T> * = nullptr>
+  inline graph_reverse_iterator<T const> crbegin() {
+    return graph_reverse_iterator<T const>(cend<T>());
+  }
 
   /**
    * @brief     get end iterator for the backwarding
    * @retval    const reverse iterator marking the end of backwarding
    */
-  inline const_reverse_iterator crend() { return Sorted.crend(); }
+  template <
+    typename T = GraphNode,
+    std::enable_if_t<std::is_base_of<GraphNode, T>::value, T> * = nullptr>
+  inline graph_reverse_iterator<T const> crend() {
+    return graph_reverse_iterator<T const>(cbegin<T>());
+  }
 
   /**
    * @brief Sorting and Define order to calculate : Depth First Search
@@ -187,11 +204,11 @@ public:
    * @retval    Graph Object copyed
    */
   GraphCore &copy(GraphCore &from) {
-    // if (this != &from) {
-    //   // FIXME: this assumes elements already in nodes/adj, solve that
-    //   for (unsigned int i = 0; i < adj.size(); i++)
-    //     adj[i].front()->getObject()->copy(from.adj[i].front()->getObject());
-    // }
+    if (this != &from) {
+      // FIXME: this assumes elements already in nodes/adj, solve that
+      // for (unsigned int i = 0; i < adj.size(); i++)
+      //   adj[i].front()->getObject()->copy(from.adj[i].front()->getObject());
+    }
     return *this;
   }
 
@@ -200,7 +217,7 @@ public:
    * @param[in] ith Node index : From
    * @param[in] node GraphNode object to be added : To
    */
-  void addEdge(unsigned int ith, std::shared_ptr<GraphNode> &node);
+  void addEdge(unsigned int ith, const std::shared_ptr<GraphNode> &node);
 
   /**
    * @brief     make connection between nodes
@@ -224,14 +241,37 @@ public:
    * @details   Ensures that the node has a unique and a valid name. A valid
    * name pre-assigned to the node can be changed if force_rename is enabled.
    */
-  void ensureName(std::shared_ptr<GraphNode> &node,
-                  const std::string &prefix = "",
+  void ensureName(GraphNode &node, const std::string &prefix = "",
                   const std::string &postfix = "", bool force_rename = false);
 
+  void remove_last_node() {
+    auto last_node = Sorted.back();
+    Sorted.pop_back();
+    adj.erase(adj.begin() + last_node->getIndex());
+
+    /**
+     * Remove all the connections for the current lasy layer as it will now only
+     */
+    last_node = Sorted.back();
+    adj[last_node->getIndex()].resize(1);
+  }
+
+  void addLossToSorted() { Sorted.push_back(adj.back().front()); }
+
+  bool verifyNode(const std::string &name) {
+    if (node_names.find(name) == node_names.end())
+      return false;
+    return true;
+  }
+
 private:
   std::vector<std::list<std::shared_ptr<GraphNode>>>
     adj; /**< adjacency list for graph */
+  std::vector<std::shared_ptr<GraphNode>> node_list; /**< Ordered Node List  */
+
   std::vector<std::shared_ptr<GraphNode>> Sorted; /**< Ordered Node List  */
+  bool sorted; /** if the node_list is sorted */
+
   std::set<std::string>
     node_names;       /**< Set containing all the names of nodes in the model */
   int def_name_count; /**< Count assigned to node names declared by default */
index b11f48c..335db6b 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef __GRAPH_NODE_H__
 #define __GRAPH_NODE_H__
 
+#include <iterator>
 #include <memory>
 #include <string>
 #include <vector>
@@ -74,5 +75,228 @@ public:
   // virtual GraphNode &copy(const GraphNode &from) = 0;
 };
 
+/**
+ * @brief   Iterator for GraphNode which return LayerNode object upon realize
+ *
+ * @note    This does not include the complete list of required functions. Add
+ * them as per need.
+ */
+template <typename T>
+class GraphNodeIterator : public std::iterator<std::random_access_iterator_tag,
+                                               std::shared_ptr<GraphNode>> {
+  std::shared_ptr<GraphNode> *p; /** underlying object of GraphNode */
+
+public:
+  /**
+   * @brief   iterator_traits types definition
+   *
+   * @note    these are not requried to be explicitly defined now, but maintains
+   *          forward compatibility for c++17 and later
+   *
+   * @note    valute_type, pointer and reference are different from standard
+   * iterator
+   */
+  typedef std::shared_ptr<T> value_type;
+  typedef std::random_access_iterator_tag iterator_category;
+  typedef std::ptrdiff_t difference_type;
+  typedef std::shared_ptr<T> *pointer;
+  typedef std::shared_ptr<T> &reference;
+
+  /**
+   * @brief Construct a new Graph Node Iterator object
+   *
+   * @param x underlying object of GraphNode
+   */
+  GraphNodeIterator(std::shared_ptr<GraphNode> *x) : p(x) {}
+
+  /**
+   * @brief reference operator
+   *
+   * @return value_type
+   * @note this is different from standard iterator
+   */
+  value_type operator*() const { return std::static_pointer_cast<T>(*p); }
+
+  /**
+   * @brief pointer operator
+   *
+   * @return value_type
+   * @note this is different from standard iterator
+   */
+  value_type operator->() const { return std::static_pointer_cast<T>(*p); }
+
+  /**
+   * @brief == comparison operator override
+   *
+   * @param lhs iterator lhs
+   * @param rhs iterator rhs
+   * @return true if match
+   * @return false if mismatch
+   */
+  friend bool operator==(GraphNodeIterator const &lhs,
+                         GraphNodeIterator const &rhs) {
+    return lhs.p == rhs.p;
+  }
+
+  /**
+   * @brief != comparison operator override
+   *
+   * @param lhs iterator lhs
+   * @param rhs iterator rhs
+   * @return true if mismatch
+   * @return false if match
+   */
+  friend bool operator!=(GraphNodeIterator const &lhs,
+                         GraphNodeIterator const &rhs) {
+    return lhs.p != rhs.p;
+  }
+
+  /**
+   * @brief override for ++ operator
+   *
+   * @return GraphNodeIterator&
+   */
+  GraphNodeIterator &operator++() {
+    p += 1;
+    return *this;
+  }
+
+  /**
+   * @brief override for operator++
+   *
+   * @return GraphNodeIterator
+   */
+  GraphNodeIterator operator++(int) {
+    GraphNodeIterator temp(p);
+    p += 1;
+    return temp;
+  }
+
+  /**
+   * @brief override for -- operator
+   *
+   * @return GraphNodeIterator&
+   */
+  GraphNodeIterator &operator--() {
+    p -= 1;
+    return *this;
+  }
+
+  /**
+   * @brief override for operator--
+   *
+   * @return GraphNodeIterator
+   */
+  GraphNodeIterator operator--(int) {
+    GraphNodeIterator temp(p);
+    p -= 1;
+    return temp;
+  }
+
+  /**
+   * @brief override for subtract operator
+   *
+   * @param offset offset to subtract
+   * @return GraphNodeIterator
+   */
+  GraphNodeIterator operator-(const difference_type offset) const {
+    return GraphNodeIterator(p - offset);
+  }
+
+  /**
+   * @brief override for subtract operator
+   *
+   * @param other iterator to subtract
+   * @return difference_type
+   */
+  difference_type operator-(const GraphNodeIterator &other) const {
+    return p - other.p;
+  }
+
+  /**
+   * @brief override for subtract and return result operator
+   *
+   * @param offset offset to subtract
+   * @return GraphNodeIterator&
+   */
+  GraphNodeIterator &operator-=(const difference_type offset) {
+    p -= offset;
+    return *this;
+  }
+
+  /**
+   * @brief override for add operator
+   *
+   * @param offset offset to add
+   * @return GraphNodeIterator
+   */
+  GraphNodeIterator operator+(const difference_type offset) const {
+    return GraphNodeIterator(p + offset);
+  }
+
+  /**
+   * @brief override for add and return result operator
+   *
+   * @param offset offset to add
+   * @return GraphNodeIterator&
+   */
+  GraphNodeIterator &operator+=(const difference_type offset) {
+    p += offset;
+    return *this;
+  }
+};
+
+/**
+ * @brief   Reverse Iterator for GraphNode which return LayerNode object upon
+ * realize
+ *
+ * @note    This just extends GraphNodeIterator and is limited by its
+ * functionality.
+ */
+template <typename T_iterator>
+class GraphNodeReverseIterator : public std::reverse_iterator<T_iterator> {
+public:
+  /**
+   * @brief Construct a new Graph Node Reverse Iterator object
+   *
+   * @param iter Iterator
+   */
+  explicit GraphNodeReverseIterator(T_iterator iter) :
+    std::reverse_iterator<T_iterator>(iter) {}
+
+  /**
+   *  @brief reference operator
+   *
+   * @return T_iterator::value_type
+   * @note this is different from standard iterator
+   */
+  typename T_iterator::value_type operator*() const {
+    auto temp = std::reverse_iterator<T_iterator>::current - 1;
+    return *temp;
+  }
+
+  /**
+   *  @brief pointer operator
+   *
+   * @return T_iterator::value_type
+   * @note this is different from standard iterator
+   */
+  typename T_iterator::value_type operator->() const {
+    auto temp = std::reverse_iterator<T_iterator>::current - 1;
+    return *temp;
+  }
+};
+
+/**
+ * @brief     Iterators to traverse the graph
+ */
+template <class T> using graph_iterator = GraphNodeIterator<T>;
+
+/**
+ * @brief     Iterators to traverse the graph
+ */
+template <class T>
+using graph_reverse_iterator = GraphNodeReverseIterator<GraphNodeIterator<T>>;
+
 } // namespace nntrainer
 #endif // __GRAPH_NODE_H__
index 1db88b0..3c5a798 100644 (file)
@@ -31,6 +31,8 @@
 #include <rnn.h>
 #include <time_dist.h>
 
+#define LNODE(x) std::static_pointer_cast<LayerNode>(x)
+
 namespace nntrainer {
 
 /**
@@ -59,7 +61,9 @@ int NetworkGraph::compile(const LossType loss_type) {
   status = connectGraph();
   NN_RETURN_STATUS();
 
-  topologicalSort();
+  graph.topologicalSort();
+
+  countNonTrainableLayersAtBegin();
 
   status = addLossLayer(loss_type);
   NN_RETURN_STATUS();
@@ -67,18 +71,8 @@ int NetworkGraph::compile(const LossType loss_type) {
   status = checkCompiledGraph();
   NN_RETURN_STATUS();
 
-  /**
-   * Now that graph is compiled, remove all edges to save memory.
-   * NodeList is kept for now for O(1) access of layers by idx.
-   */
-  for (unsigned int i = 0; i < adj.size(); ++i) {
-    /**
-     * As this resize is guaranteed to not insert new elements,  create a
-     * default element needed by resize.
-     */
-    adj[i].resize(1);
-  }
-
+  /** Save memory by removing edges once it has been compiled */
+  graph.removeEdges();
   compiled = true;
 
   return status;
@@ -86,8 +80,10 @@ int NetworkGraph::compile(const LossType loss_type) {
 
 void NetworkGraph::updateConnectionName(const std::string &from,
                                         const std::string &to) {
-  for (unsigned int i = 0; i < adj.size(); ++i) {
-    auto &layer = adj[i].front()->getObject();
+
+  const std::vector<std::shared_ptr<GraphNode>> &node_list = graph.getNodes();
+  for (unsigned int i = 0; i < node_list.size(); ++i) {
+    auto &layer = LNODE(node_list[i])->getObject();
     if (istrequal(layer->getName(), to))
       continue;
     for (unsigned int j = 0; j < layer->input_layers.size(); ++j) {
@@ -99,9 +95,10 @@ void NetworkGraph::updateConnectionName(const std::string &from,
 }
 
 void NetworkGraph::addDefaultInputLayers() {
-  for (unsigned int i = 1; i < adj.size(); ++i) {
-    auto &layer = adj[i].front()->getObject();
-    auto &prev_layer = adj[i - 1].front()->getObject();
+  const std::vector<std::shared_ptr<GraphNode>> &node_list = graph.getNodes();
+  for (unsigned int i = 1; i < node_list.size(); ++i) {
+    auto &layer = LNODE(node_list[i])->getObject();
+    auto &prev_layer = LNODE(node_list[i - 1])->getObject();
     if (layer->input_layers.size() == 0) {
       layer->input_layers.push_back(prev_layer->getName());
     }
@@ -109,123 +106,19 @@ void NetworkGraph::addDefaultInputLayers() {
 }
 
 void NetworkGraph::addLayerNode(std::shared_ptr<Layer> layer) {
-  addLayerNode(std::make_unique<LayerNode>(layer, adj.size()));
-}
-
-void NetworkGraph::addLayerNode(std::shared_ptr<LayerNode> layer) {
-  std::list<std::shared_ptr<LayerNode>> l;
-  layer->setIndex(adj.size());
-
-  l.push_back(layer);
-  adj.push_back(l);
-}
-
-std::shared_ptr<LayerNode> &NetworkGraph::getLayerNode(unsigned int ith) {
-  if (ith >= size())
-    throw std::invalid_argument("Exceed total number of layer");
-
-  if (adj[ith].front()->getIndex() != ith)
-    throw std::runtime_error("Graph internal index mismatch");
-
-  return adj[ith].front();
-}
-
-std::shared_ptr<LayerNode> &NetworkGraph::getSortedLayerNode(unsigned int ith) {
-  if (ith >= getSorted().size())
-    throw std::invalid_argument("Exceed total number of layer");
-
-  return getSorted()[ith];
-}
-
-void NetworkGraph::topologicalSortUtil(
-  unsigned int ith, std::vector<bool> &visited,
-  std::stack<std::shared_ptr<LayerNode>> &Stack) {
-  visited[ith] = true;
-
-  std::list<std::shared_ptr<LayerNode>>::iterator i;
-  for (i = adj[ith].begin(); i != adj[ith].end(); ++i) {
-    auto index = (*i)->getIndex();
-    if (!visited[index])
-      topologicalSortUtil(index, visited, Stack);
-  }
-
-  Stack.push(getLayerNode(ith));
+  graph.addNode(std::make_unique<LayerNode>(layer, graph.size()));
 }
 
 void NetworkGraph::countNonTrainableLayersAtBegin() {
-  for (auto iter = Sorted.cbegin(); iter != Sorted.cend(); iter++) {
+  for (auto iter = graph.cbegin<LayerNode>(); iter != graph.cend<LayerNode>();
+       iter++) {
     if ((*iter)->getObject()->getTrainable()) {
-      skip_non_trainable_layers = iter - Sorted.cbegin();
+      skip_non_trainable_layers = iter - graph.cbegin<LayerNode>();
       return;
     }
   }
-  skip_non_trainable_layers = Sorted.size();
-}
-
-void NetworkGraph::topologicalSort() {
-  std::stack<std::shared_ptr<LayerNode>> Stack;
-  std::vector<bool> visited(adj.size());
-  Sorted.clear();
-
-  std::fill(visited.begin(), visited.end(), false);
-
-  // TODO : After make node list of graph, we have to find root. (That means it
-  // should be the only one input for now.). Need to support multiple input and
-  // support search.
-
-  for (unsigned int i = 0; i < adj.size(); ++i) {
-    if (visited[i] == false) {
-      topologicalSortUtil(i, visited, Stack);
-    }
-  }
-
-  while (Stack.empty() == false) {
-    Sorted.push_back(Stack.top());
-    Stack.pop();
-  }
 
-  countNonTrainableLayersAtBegin();
-}
-
-void NetworkGraph::ensureName(std::shared_ptr<Layer> layer,
-                              const std::string &prefix,
-                              const std::string &postfix, bool force_rename) {
-  std::string orig_name = layer->getName();
-  bool orig_name_empty = orig_name.empty();
-  /** If layer already has name which is unique and valid, and force is
-   * disabled, then nothing to do.
-   */
-  if (!orig_name_empty && !force_rename &&
-      layer_names.end() == layer_names.find(orig_name)) {
-    layer_names.insert(orig_name);
-    return;
-  }
-
-  /** If just prefix with layer name makes it unique - directly set the name */
-  if (!orig_name_empty) {
-    std::string direct_name = prefix + orig_name + postfix;
-    if (layer_names.find(direct_name) == layer_names.end()) {
-      layer->setName(direct_name);
-      layer_names.insert(direct_name);
-      return;
-    }
-  }
-
-  std::set<std::string>::iterator iter;
-  std::string name;
-  if (orig_name_empty) {
-    orig_name = layer->getType();
-  }
-
-  std::string direct_name = prefix + orig_name + postfix;
-
-  do {
-    name = direct_name + std::to_string(def_name_count++);
-    iter = layer_names.find(name);
-  } while (iter != layer_names.end());
-
-  layer->setName(name);
-  layer_names.insert(name);
+  skip_non_trainable_layers = graph.size();
 }
 
 int NetworkGraph::realizeMultiInputType(Layer &current) {
@@ -234,8 +127,9 @@ int NetworkGraph::realizeMultiInputType(Layer &current) {
     return ML_ERROR_NONE;
 
   // TODO: this can be addition or concat layer - add support
-  std::shared_ptr<Layer> layer = nntrainer::createLayer(AdditionLayer::type);
-  ensureName(layer, current.getName());
+  std::shared_ptr<LayerNode> lnode = createLayerNode(AdditionLayer::type);
+  std::shared_ptr<Layer> layer = lnode->getObject();
+  graph.ensureName(*lnode, current.getName());
 
   layer->setNumInputs(current.getNumInputs());
   layer->input_layers.clear();
@@ -249,17 +143,12 @@ int NetworkGraph::realizeMultiInputType(Layer &current) {
   current.input_layers.push_back(layer->getName());
   /** output layers for layer obj will be set in setOutputLayers() */
 
-  addLayerNode(layer);
+  graph.addNode(lnode, false);
 
   return status;
 }
 
 int NetworkGraph::realizeFlattenType(Layer &current) {
-  if (adj.empty()) {
-    ml_loge("layer is empty");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
-
   if (current.getType() == FlattenLayer::type) {
     ml_loge(
       "It is not allowed to realize flatten layer, possibly flatten layer is "
@@ -267,9 +156,9 @@ int NetworkGraph::realizeFlattenType(Layer &current) {
     return ML_ERROR_INVALID_PARAMETER;
   }
 
-  std::shared_ptr<Layer> layer = nntrainer::createLayer(FlattenLayer::type);
-
-  ensureName(layer, current.getName());
+  std::shared_ptr<LayerNode> lnode = createLayerNode(FlattenLayer::type);
+  std::shared_ptr<Layer> layer = lnode->getObject();
+  graph.ensureName(*lnode, current.getName());
 
   layer->setNumInputs(current.getNumInputs());
   layer->input_layers.clear();
@@ -278,7 +167,7 @@ int NetworkGraph::realizeFlattenType(Layer &current) {
   /** output layers for layer obj will be set in setOutputLayers() */
 
   updateConnectionName(current.getName(), layer->getName());
-  addLayerNode(layer);
+  graph.addNode(lnode, false);
 
   return ML_ERROR_NONE;
 }
@@ -302,11 +191,6 @@ int NetworkGraph::realizeActivationType(Layer &current) {
     return ML_ERROR_NONE;
   }
 
-  if (adj.empty()) {
-    ml_loge("layer is empty");
-    return ML_ERROR_INVALID_PARAMETER;
-  }
-
   if (current.getType() == ActivationLayer::type) {
     ml_loge("It is not allowed to realize ativation layer, possibly layer is "
             "added right after activation");
@@ -318,15 +202,17 @@ int NetworkGraph::realizeActivationType(Layer &current) {
     return ML_ERROR_INVALID_PARAMETER;
   }
 
-  std::shared_ptr<Layer> layer = nntrainer::createLayer(ActivationLayer::type);
-  layer->setActivation(act);
+  std::shared_ptr<LayerNode> lnode = createLayerNode(ActivationLayer::type);
+  std::shared_ptr<Layer> layer = lnode->getObject();
 
-  ensureName(layer, current.getName());
+  layer->setActivation(act);
+  graph.ensureName(*lnode, current.getName());
 
   if (current.getType() == TimeDistLayer::type) {
     std::string unit_str = layer->getName();
-    ensureName(layer, "", "_unit");
+    graph.ensureName(*lnode, "", "_unit");
     layer = distributeLayer(layer);
+    lnode = std::make_shared<LayerNode>(layer);
     layer->setName(unit_str);
   }
 
@@ -334,10 +220,10 @@ int NetworkGraph::realizeActivationType(Layer &current) {
   layer->input_layers.clear();
   layer->input_layers.push_back(current.getName());
   layer->setNumOutputs(current.getNumOutputs());
-  /** output layers for layer obj will be set in setOutputLayers() */
+  /** output layers for layer aobj will be set in setOutputLayers() */
 
   updateConnectionName(current.getName(), layer->getName());
-  addLayerNode(layer);
+  graph.addNode(lnode, false);
 
   return status;
 }
@@ -347,8 +233,9 @@ int NetworkGraph::realizeMultiOutputType(Layer &current) {
   if (current.getNumOutputs() == 1)
     return ML_ERROR_NONE;
 
-  std::shared_ptr<Layer> layer = nntrainer::createLayer(OutputLayer::type);
-  ensureName(layer, current.getName());
+  std::shared_ptr<LayerNode> lnode = createLayerNode(OutputLayer::type);
+  std::shared_ptr<Layer> layer = lnode->getObject();
+  graph.ensureName(*lnode, current.getName());
 
   layer->setNumInputs(current.getNumInputs());
   layer->input_layers.clear();
@@ -362,20 +249,23 @@ int NetworkGraph::realizeMultiOutputType(Layer &current) {
 
   current.setNumOutputs(layer->getNumInputs());
 
-  addLayerNode(layer);
+  graph.addNode(lnode, false);
 
   return status;
 }
 
+/** TODO: this needs special attention */
 int NetworkGraph::addLossLayer(const LossType loss_type) {
 
   int status = ML_ERROR_NONE;
 
-  if (Sorted.back()->getObject()->getType() == LossLayer::type)
+  auto last_node = graph.getSortedNode(graph.size() - 1);
+  auto last_layer_node = LNODE(last_node);
+  if (last_node->getType() == LossLayer::type)
     return status;
 
-  if (Sorted.back()->getObject()->getType() == TimeDistLayer::type) {
-    if (std::static_pointer_cast<TimeDistLayer>(Sorted.back()->getObject())
+  if (last_node->getType() == TimeDistLayer::type) {
+    if (std::static_pointer_cast<TimeDistLayer>(last_layer_node->getObject())
           ->getDistLayerType() == LossLayer::type)
       return status;
   }
@@ -385,17 +275,13 @@ int NetworkGraph::addLossLayer(const LossType loss_type) {
   }
 
   LossType updated_loss_type = loss_type;
-  if (adj.empty()) {
-    status = ML_ERROR_INVALID_PARAMETER;
-    NN_RETURN_STATUS();
-  }
 
-  auto last_node = Sorted.back();
   if (updated_loss_type == LossType::LOSS_ENTROPY) {
-    auto type = last_node->getObject()->getType();
+    auto type = last_node->getType();
     if (type == TimeDistLayer::type) {
-      type = std::dynamic_pointer_cast<TimeDistLayer>(last_node->getObject())
-               ->getDistLayerType();
+      type =
+        std::dynamic_pointer_cast<TimeDistLayer>(last_layer_node->getObject())
+          ->getDistLayerType();
     }
 
     if (type != "activation") {
@@ -404,10 +290,9 @@ int NetworkGraph::addLossLayer(const LossType loss_type) {
       return ML_ERROR_NOT_SUPPORTED;
     }
 
-    Sorted.pop_back();
-    adj.erase(adj.begin() + last_node->getIndex());
+    graph.remove_last_node();
 
-    switch (last_node->getObject()->getActivationType()) {
+    switch (last_layer_node->getObject()->getActivationType()) {
     case ActivationType::ACT_SIGMOID:
       updated_loss_type = LossType::LOSS_ENTROPY_SIGMOID;
       break;
@@ -420,35 +305,30 @@ int NetworkGraph::addLossLayer(const LossType loss_type) {
     }
   }
 
-  last_node = Sorted.back();
-
-  /**
-   * Remove all the connections for the current lasy layer as it will now only
-   * connect with the new loss layer to be added.
-   * @note this assumes that loss layer only supports single input
-   */
-  if (updated_loss_type == LossType::LOSS_ENTROPY_SIGMOID ||
-      updated_loss_type == LossType::LOSS_ENTROPY_SOFTMAX)
-    adj[last_node->getIndex()].resize(1);
+  auto const sorted = graph.getSorted();
+  last_node = sorted.back();
 
   std::shared_ptr<Layer> layer = nntrainer::createLayer(LossLayer::type);
+  std::shared_ptr<LayerNode> lnode = std::make_shared<LayerNode>(layer);
   status =
     std::dynamic_pointer_cast<LossLayer>(layer)->setLoss(updated_loss_type);
   NN_RETURN_STATUS();
-  ensureName(layer);
+  graph.ensureName(*lnode);
 
-  std::string input_str = last_node->getObject()->getName();
+  std::string input_str = last_node->getName();
 
-  if (last_node->getObject()->getType() == TimeDistLayer::type) {
+  if (last_node->getType() == TimeDistLayer::type) {
     std::string unit_str = layer->getName();
-    ensureName(layer, "", "_unit");
+    graph.ensureName(*lnode, "", "_unit");
     layer = distributeLayer(layer);
+    lnode = std::make_shared<LayerNode>(layer);
     layer->setName(unit_str);
   }
 
-  last_node->getObject()->setNumOutputs(1);
-  last_node->getObject()->output_layers.clear();
-  last_node->getObject()->output_layers.push_back(layer->getName());
+  last_layer_node = LNODE(last_node);
+  last_layer_node->getObject()->setNumOutputs(1);
+  last_layer_node->getObject()->output_layers.clear();
+  last_layer_node->getObject()->output_layers.push_back(layer->getName());
 
   layer->setNumInputs(1);
   layer->input_layers.clear();
@@ -465,20 +345,22 @@ int NetworkGraph::addLossLayer(const LossType loss_type) {
    * As the loss layer is always the last, it could be added manually to Sorted
    * for performance.
    */
-  addLayerNode(layer);
-  connectGraph(adj.size() - 1);
-  Sorted.push_back(adj.back().front());
+  graph.addNode(lnode, false);
+  connectGraph(graph.size() - 1);
+  graph.addLossToSorted();
 
   return ML_ERROR_NONE;
 }
 
 void NetworkGraph::setOutputLayers() {
 
+  const std::vector<std::shared_ptr<GraphNode>> &node_list = graph.getNodes();
+
   size_t last_layer_count = 0;
-  for (unsigned int idx = 0; idx < adj.size(); ++idx) {
-    auto &layer_idx = adj[idx].front()->getObject();
-    for (unsigned int i = 0; i < adj.size(); ++i) {
-      auto &layer_i = adj[i].front()->getObject();
+  for (unsigned int idx = 0; idx < graph.size(); ++idx) {
+    auto &layer_idx = LNODE(node_list[idx])->getObject();
+    for (unsigned int i = 0; i < graph.size(); ++i) {
+      auto &layer_i = LNODE(node_list[i])->getObject();
       if (istrequal(layer_i->getName(), layer_idx->getName()))
         continue;
       for (unsigned int j = 0; j < layer_i->input_layers.size(); ++j) {
@@ -521,8 +403,8 @@ void NetworkGraph::setOutputLayers() {
       "Error: Multiple last layers in the model not supported");
   }
 
-  for (auto iter = adj.begin(); iter < adj.end(); ++iter) {
-    if ((*iter).front()->getObject()->output_layers.size() == 0)
+  for (unsigned int idx = 0; idx < graph.size(); ++idx) {
+    if (LNODE(node_list[idx])->getObject()->output_layers.size() == 0)
       throw std::runtime_error("There is un-connected node");
   }
 }
@@ -533,8 +415,8 @@ int NetworkGraph::isCompilable() {
     return ML_ERROR_NOT_SUPPORTED;
   }
 
-  if (adj.empty()) {
-    ml_loge("Layer is empty");
+  if (graph.empty()) {
+    ml_loge("Graph is empty");
     return ML_ERROR_INVALID_PARAMETER;
   }
 
@@ -542,7 +424,7 @@ int NetworkGraph::isCompilable() {
 }
 
 int NetworkGraph::checkCompiledGraph() {
-  auto &l = Sorted[0]->getObject();
+  auto const &l = getSortedLayerNode(0)->getObject();
   /** First layer cannot be activation, batch normalization or loss */
   const std::string &type = l->getType();
   if (istrequal(type, ActivationLayer::type) ||
@@ -554,7 +436,9 @@ int NetworkGraph::checkCompiledGraph() {
   }
 
   /** Dimension of input layers must be known */
-  for (auto const &lnode : Sorted) {
+  for (auto iter = graph.cbegin<LayerNode>(); iter != graph.cend<LayerNode>();
+       iter++) {
+    auto lnode = (*iter);
     if (lnode->getObject()->getType() == InputLayer::type) {
       if (lnode->getObject()->getInputDimension().size() == 0) {
         ml_loge("InputDimension of first layer is not set");
@@ -572,11 +456,12 @@ int NetworkGraph::realizeGraph() {
 
   addDefaultInputLayers();
 
-  size_t adj_size_before_realize = adj.size();
-  /** This loop modifes adj. Get the size of adj preemptively. */
+  /** This loop modifes the graph. Get the size of graph preemptively. */
+  size_t num_nodes = graph.size();
+  std::vector<std::shared_ptr<GraphNode>> node_list = graph.getNodes();
 
-  for (unsigned int i = 0; i < adj_size_before_realize; ++i) {
-    Layer &l = *adj[i].front()->getObject();
+  for (unsigned int i = 0; i < num_nodes; ++i) {
+    Layer &l = *LNODE(node_list[i])->getObject();
     ml_logd("layer name: %s", l.getName().c_str());
 
     /** If a layer does not has input nodes, then it must have input dimension
@@ -623,40 +508,21 @@ int NetworkGraph::realizeGraph() {
   return status;
 }
 
-std::shared_ptr<LayerNode> &
-NetworkGraph::getLayerNode(const std::string &layer_name) {
-  for (auto &lnode_list : adj) {
-    auto &lnode = lnode_list.front();
-    if (istrequal(lnode->getObject()->getName(), layer_name))
-      return lnode;
-  }
-
-  std::stringstream ss;
-  ss << "Cannot find Layer: " << layer_name;
-  throw std::invalid_argument(ss.str());
-}
-
-void NetworkGraph::addEdge(unsigned int ith, std::shared_ptr<LayerNode> &node) {
-  if (ith >= adj.size())
-    throw std::invalid_argument("Exceed total number of layer");
-
-  adj[ith].push_back(node);
-}
-
 void NetworkGraph::connectGraph(unsigned int adj_idx) {
-  std::list<std::shared_ptr<LayerNode>>::iterator iter = adj[adj_idx].begin();
 
-  for (unsigned int j = 0; j < (*iter)->getObject()->input_layers.size(); ++j) {
-    if (istrequal((*iter)->getObject()->input_layers[j], "__data__"))
+  std::shared_ptr<LayerNode> node = LNODE(graph.getNode(adj_idx));
+
+  for (unsigned int j = 0; j < node->getObject()->input_layers.size(); ++j) {
+    if (istrequal(node->getObject()->input_layers[j], "__data__"))
       continue;
     unsigned int to_node_id =
-      getLayerNode((*iter)->getObject()->input_layers[j])->getIndex();
-    addEdge(to_node_id, (*iter));
+      getLayerNode(node->getObject()->input_layers[j])->getIndex();
+    graph.addEdge(to_node_id, node);
   }
 }
 
 int NetworkGraph::connectGraph() {
-  for (unsigned int i = 0; i < adj.size(); ++i) {
+  for (unsigned int i = 0; i < graph.size(); ++i) {
     connectGraph(i);
   }
 
@@ -664,13 +530,16 @@ int NetworkGraph::connectGraph() {
 }
 
 void NetworkGraph::setBatchSize(unsigned int batch_size) {
-  for (auto const &layer_adj_list : adj) {
-    layer_adj_list.front()->getObject()->setBatch(batch_size);
+  auto const &node_list = graph.getNodes();
+  for (auto const &node : node_list) {
+    LNODE(node)->getObject()->setBatch(batch_size);
   }
 }
 
 sharedConstTensors NetworkGraph::forwarding(bool training) {
-  for (auto const &ln : getSorted()) {
+  auto const &node_list = graph.getNodes();
+  for (auto const &node : node_list) {
+    auto const &ln = LNODE(node);
     START_PROFILE(ln->event_key);
     ln->getObject()->forwarding(training);
     END_PROFILE(ln->event_key);
@@ -702,26 +571,29 @@ NetworkGraph::getUnsortedLayers(const std::string &input_layer,
   /// Further, this function must be removed. There should be rather
   /// getAllNames and getLayerByName instead of getUnsortedLayers.
 
+  auto const &unsortedNodes = graph.getNodes();
+
   /** count layers after output layer */
   unsigned int num_layers_remove_end = 0;
   if (!output_layer.empty()) {
-    for (auto iter = adj.rbegin(); iter != adj.rend(); iter++) {
-      if ((*iter).front()->getObject()->getName() != output_layer)
+    for (auto iter = unsortedNodes.rbegin(); iter != unsortedNodes.rend();
+         iter++) {
+      if ((*iter)->getName() != output_layer)
         num_layers_remove_end++;
       else
         break;
     }
   }
 
-  if (num_layers_remove_end == adj.size())
+  if (num_layers_remove_end == graph.size())
     return {};
 
   /** count layers before input layer */
   unsigned int num_layers_remove_start = 0;
   if (!input_layer.empty()) {
-    for (auto iter = adj.begin(); iter != adj.end() - num_layers_remove_end;
-         iter++) {
-      if ((*iter).front()->getObject()->getName() != input_layer)
+    for (auto iter = unsortedNodes.begin();
+         iter != unsortedNodes.end() - num_layers_remove_end; iter++) {
+      if ((*iter)->getName() != input_layer)
         num_layers_remove_start++;
       else
         break;
@@ -730,75 +602,71 @@ NetworkGraph::getUnsortedLayers(const std::string &input_layer,
 
   /** copy the graph and return */
   std::vector<std::shared_ptr<LayerNode>> ret;
-  std::transform(adj.begin() + num_layers_remove_start,
-                 adj.end() - num_layers_remove_end, std::back_inserter(ret),
-                 [](auto const &elem) { return elem.front(); });
+  std::transform(unsortedNodes.begin() + num_layers_remove_start,
+                 unsortedNodes.end() - num_layers_remove_end,
+                 std::back_inserter(ret),
+                 [](auto const &elem) { return LNODE(elem); });
 
   return ret;
 }
 
 std::vector<std::shared_ptr<LayerNode>> NetworkGraph::getLayerNodes() const {
+  auto nodes = graph.getNodes();
   std::vector<std::shared_ptr<LayerNode>> ret;
-  if (compiled) {
-    std::transform(Sorted.begin(), Sorted.end(), std::back_inserter(ret),
-                   [](auto const &elem) { return elem; });
-  } else {
-    std::transform(adj.begin(), adj.end(), std::back_inserter(ret),
-                   [](auto const &elem) { return elem.front(); });
-  }
+  std::transform(nodes.begin(), nodes.end(), std::back_inserter(ret),
+                 [](auto const &elem) { return LNODE(elem); });
 
   return ret;
 }
 
-void NetworkGraph::extendGraph(std::vector<std::shared_ptr<LayerNode>> graph,
+void NetworkGraph::extendGraph(std::vector<std::shared_ptr<LayerNode>> ex_graph,
                                std::string &prefix) {
 
   if (compiled)
     throw std::runtime_error("Cannot modify graph after compile");
 
   /**
-   * The input_layers for graph[0] here is provided to the backbone by the ini
-   * file and is overwritten here by the model loader for connection making.
+   * The input_layers for ex_graph[0] here is provided to the backbone by the
+   * ini file and is overwritten here by the model loader for connection making.
    *
    * This loop intends to connect a new backbone to be added with an old
    * backbone.
    */
-  auto &layer0_in = graph[0]->getObject()->input_layers;
+  auto &layer0_in = ex_graph[0]->getObject()->input_layers;
   for (unsigned int i = 0; i < layer0_in.size(); ++i) {
     if (sub_in_out.find(layer0_in[i]) != sub_in_out.end()) {
       layer0_in[i] = sub_in_out[layer0_in[i]];
-    } else if (layer_names.find(layer0_in[i]) == layer_names.end()) {
+    } else if (!graph.verifyNode(layer0_in[i])) {
       throw std::runtime_error("Input layer name for backbone not found.");
     }
   }
 
   /** Insert the layer to the graph */
-  for (auto &layernode : graph) {
+  for (auto &layernode : ex_graph) {
     /**
      * Add prefix to the existing layer name,
-     * and ensure it is unique in this new graph
+     * and ensure it is unique in this new ex_graph
      */
     auto &layer = layernode->getObject();
-    std::string orig_name = prefix + layer->getName();
-    ensureName(layer, prefix, "", true);
-    sub_in_out.insert(std::make_pair(orig_name, layer->getName()));
+    std::string orig_name = prefix + layernode->getName();
+    graph.ensureName(*layernode, prefix, "", true);
+    sub_in_out.insert(std::make_pair(orig_name, layernode->getName()));
 
     for (unsigned int i = 0; i < layer->input_layers.size(); ++i) {
       if (sub_in_out.find(prefix + layer->input_layers[i]) !=
           sub_in_out.end()) {
         layer->input_layers[i] = sub_in_out[prefix + layer->input_layers[i]];
-      } else if (layer_names.find(layer->input_layers[i]) ==
-                 layer_names.end()) {
+      } else if (!graph.verifyNode(layer->input_layers[i])) {
         throw std::runtime_error("Input layer name for backbone not found.");
       }
     }
 
-    addLayerNode(layernode);
+    graph.addNode(layernode, false);
   }
 
   /** This allows connecting a layer to the backbone */
   sub_in_out.insert(
-    std::make_pair(prefix, adj.back().front()->getObject()->getName()));
+    std::make_pair(prefix, graph.getNode(graph.size() - 1)->getName()));
 }
 
 void NetworkGraph::addLayer(std::shared_ptr<LayerNode> layer) {
@@ -806,10 +674,10 @@ void NetworkGraph::addLayer(std::shared_ptr<LayerNode> layer) {
     throw std::runtime_error("Cannot modify graph after compile");
 
   /** Ensure that the layer has a name and is unique */
-  ensureName(layer->getObject());
+  // graph.ensureName(*layer);
 
   /** Insert the layer to the graph */
-  addLayerNode(layer);
+  graph.addNode(layer);
 }
 
 void NetworkGraph::inPlaceOptimize(const std::string &layer_type,
@@ -911,18 +779,16 @@ void NetworkGraph::inPlaceOptimize(Manager &manager) {
     inPlaceOptimize(layer_type, manager);
 }
 
-const std::vector<std::shared_ptr<LayerNode>> &NetworkGraph::getSorted() const {
+const std::vector<std::shared_ptr<LayerNode>> NetworkGraph::getSorted() const {
   if (!compiled)
     throw std::runtime_error("Cannot get sorted graph before compiling graph");
 
-  return Sorted;
-}
-
-std::vector<std::shared_ptr<LayerNode>> &NetworkGraph::getSorted() {
-  if (!compiled)
-    throw std::runtime_error("Cannot get sorted graph before compiling graph");
+  auto const &sorted = graph.getSorted();
+  std::vector<std::shared_ptr<LayerNode>> ret;
+  std::transform(sorted.begin(), sorted.end(), std::back_inserter(ret),
+                 [](auto const &elem) { return LNODE(elem); });
 
-  return Sorted;
+  return ret;
 }
 
 int NetworkGraph::initialize(std::shared_ptr<Manager> manager) {
index 6cfa3b4..6761ff1 100644 (file)
@@ -22,6 +22,7 @@
 #include <stack>
 #include <vector>
 
+#include <graph_core.h>
 #include <layer_internal.h>
 #include <layer_node.h>
 #include <loss_layer.h>
@@ -38,10 +39,7 @@ public:
   /**
    * @brief     Constructor of NeuralNetwork Graph Class
    */
-  NetworkGraph() :
-    def_name_count(0),
-    skip_non_trainable_layers(0),
-    compiled(false) {}
+  NetworkGraph() : graph(), skip_non_trainable_layers(0), compiled(false) {}
 
   /**
    * @brief     Compile the graph
@@ -73,23 +71,13 @@ public:
    * @brief getter of number of nodes
    * @param[out] number of nodes
    */
-  unsigned int size() const {
-    if (!compiled)
-      return adj.size();
-    else
-      return Sorted.size();
-  }
+  unsigned int size() const { return graph.size(); }
 
   /**
    * @brief get if the graph is empty
    * @param[out] true if empty, else false
    */
-  bool empty() const {
-    if (!compiled)
-      return adj.empty();
-    else
-      return Sorted.empty();
-  }
+  bool empty() const { return graph.empty(); }
 
   /**
    * @brief     Swap function for the class
@@ -97,10 +85,7 @@ public:
   friend void swap(NetworkGraph &lhs, NetworkGraph &rhs) {
     using std::swap;
 
-    swap(lhs.adj, rhs.adj);
-    swap(lhs.Sorted, rhs.Sorted);
-    swap(lhs.layer_names, rhs.layer_names);
-    swap(lhs.def_name_count, rhs.def_name_count);
+    swap(lhs.graph, rhs.graph);
     swap(lhs.skip_non_trainable_layers, rhs.skip_non_trainable_layers);
   }
 
@@ -109,10 +94,7 @@ public:
    */
   void reset() {
 
-    adj.clear();
-    Sorted.clear();
-    layer_names.clear();
-    def_name_count = 0;
+    graph.reset();
     skip_non_trainable_layers = 0;
   }
 
@@ -121,21 +103,27 @@ public:
    * @param[in] index
    * @ret LayerNode
    */
-  std::shared_ptr<LayerNode> &getLayerNode(unsigned int ith);
+  std::shared_ptr<LayerNode> getLayerNode(unsigned int ith) {
+    return std::static_pointer_cast<LayerNode>(graph.getNode(ith));
+  }
 
   /**
    * @brief getter of Sorted LayerNode with index number
    * @param[in] index
    * @ret LayerNode
    */
-  std::shared_ptr<LayerNode> &getSortedLayerNode(unsigned int ith);
+  std::shared_ptr<LayerNode> getSortedLayerNode(unsigned int ith) {
+    return std::static_pointer_cast<LayerNode>(graph.getSortedNode(ith));
+  }
 
   /**
    * @brief getter of LayerNode with layer name
    * @param[in] layer name
    * @retval LayerNode
    */
-  std::shared_ptr<LayerNode> &getLayerNode(const std::string &layer_name);
+  std::shared_ptr<LayerNode> getLayerNode(const std::string &layer_name) {
+    return std::static_pointer_cast<LayerNode>(graph.getNode(layer_name));
+  }
 
   /**
    * @brief getter of Layer with layer name
@@ -185,30 +173,24 @@ public:
    * @brief     getter of ordered graph
    * @retval    ordered LayerNode list
    */
-  const std::vector<std::shared_ptr<LayerNode>> &getSorted() const;
-
-  /**
-   * @brief     getter of ordered graph
-   * @retval    ordered LayerNode list
-   */
-  std::vector<std::shared_ptr<LayerNode>> &getSorted();
+  const std::vector<std::shared_ptr<LayerNode>> getSorted() const;
 
   /**
    * @brief     get begin iterator for the backwarding
    * @retval    const reverse iterator marking the begin of backwarding
    */
-  std::vector<std::shared_ptr<LayerNode>>::const_reverse_iterator
-  getBackwardingBeginIter() {
-    return Sorted.crbegin();
+  graph_reverse_iterator<const LayerNode> getBackwardingBeginIter() {
+    return graph.crbegin<LayerNode>();
   }
 
   /**
    * @brief     get end iterator for the backwarding
    * @retval    const reverse iterator marking the end of backwarding
    */
-  std::vector<std::shared_ptr<LayerNode>>::const_reverse_iterator
-  getBackwardingEndIter() {
-    return Sorted.crend() - skip_non_trainable_layers;
+  graph_reverse_iterator<const LayerNode> getBackwardingEndIter() {
+    graph_reverse_iterator<const LayerNode> iter = graph.crend<LayerNode>();
+    iter -= skip_non_trainable_layers;
+    return iter;
   }
 
   /**
@@ -234,11 +216,8 @@ public:
    * @retval    Graph Object copyed
    */
   NetworkGraph &copy(NetworkGraph &from) {
-    if (this != &from) {
-      // FIXME: this assumes elements already in layers/adj, solve that
-      for (unsigned int i = 0; i < adj.size(); i++)
-        adj[i].front()->getObject()->copy(from.adj[i].front()->getObject());
-    }
+    graph.copy(from.graph);
+    skip_non_trainable_layers = from.skip_non_trainable_layers;
     return *this;
   }
 
@@ -253,13 +232,8 @@ public:
 private:
   std::map<std::string, std::string> sub_in_out; /** This is map to identify
                    input and output layer name of subgraph */
-  std::vector<std::list<std::shared_ptr<LayerNode>>>
-    adj; /**< Graph Structure */
-  std::vector<std::shared_ptr<LayerNode>>
-    Sorted; /**< Ordered Graph Node List  */
-  std::set<std::string>
-    layer_names; /**< Set containing all the names of layers in the model */
-  int def_name_count; /**< Count assigned to layer names declared by default */
+
+  GraphCore graph; /** core graph object */
   unsigned int
     skip_non_trainable_layers; /**< denotes the number of non-trainable layers
                                   at the start of the graph */
@@ -289,13 +263,6 @@ private:
   int checkCompiledGraph();
 
   /**
-   * @brief add Edge between graph nodes
-   * @param[in] ith Node index : From
-   * @param[in] node LayerNode object to be added : To
-   */
-  void addEdge(unsigned int ith, std::shared_ptr<LayerNode> &node);
-
-  /**
    * @brief     make connection between nodes
    * @retval #ML_ERROR_NONE Successful.
    * @retval #ML_ERROR_INVALID_PARAMETER invalid parameter.
@@ -385,17 +352,6 @@ private:
   void addLayerNode(std::shared_ptr<Layer> layer);
 
   /**
-   * @brief Add given LayerNode to the Graph
-   * @param[in] layer shared_ptr of LayerNode
-   */
-  void addLayerNode(std::shared_ptr<LayerNode> layer);
-
-  /**
-   * @brief Sorting and Define order to calculate : Depth First Search
-   */
-  void topologicalSort();
-
-  /**
    * @brief     update name of the the connections
    */
   void updateConnectionName(const std::string &from, const std::string &to);
index 20b8cb4..19553c1 100644 (file)
  * @brief  This is the layer node for network graph
  */
 
+#include <layer_factory.h>
 #include <layer_node.h>
 
-namespace nntrainer {}; // namespace nntrainer
+namespace nntrainer {
+
+/**
+ * @brief Layer factory creator with constructor
+ */
+std::unique_ptr<LayerNode> createLayerNode(const std::string &type) {
+  return std::make_unique<LayerNode>(createLayer(type));
+}
+
+}; // namespace nntrainer
index a3e651e..3116b82 100644 (file)
@@ -140,5 +140,10 @@ private:
     activation_type; /**< activation applied to the output of this node */
 };
 
+/**
+ * @brief LayerNode creator with constructor
+ */
+std::unique_ptr<LayerNode> createLayerNode(const std::string &type);
+
 } // namespace nntrainer
 #endif // __LAYER_NODE_H__
index 3e40d20..6b2b230 100644 (file)
@@ -354,7 +354,7 @@ int ModelLoader::loadFromIni(std::string ini_file, NeuralNetwork &model,
     model.model_graph = *ini_interpreter->deserialize(ini_file);
     ml_logd("parsing graph finished");
 
-    if (model.getFlatGraph().empty()) {
+    if (model.empty()) {
       ml_loge("there is no layer section in the ini file");
       status = ML_ERROR_INVALID_PARAMETER;
     }
index 0df6d5d..23569d8 100644 (file)
@@ -175,7 +175,7 @@ int NeuralNetwork::initialize() {
   if (opt) {
     opt->initialize();
     for (unsigned int idx = 0; idx < n_layers; ++idx) {
-      auto &lnode = model_graph.getSortedLayerNode(idx);
+      auto const &lnode = model_graph.getSortedLayerNode(idx);
       opt->addOptimizerVariable(lnode->getObject()->getWeightsRef());
     }
   }
@@ -298,7 +298,7 @@ void NeuralNetwork::backwarding(int iteration) {
   auto iter_begin = model_graph.getBackwardingBeginIter();
   auto iter_end = model_graph.getBackwardingEndIter();
 
-  auto &lptr_begin = (*iter_begin);
+  auto const &lptr_begin = (*iter_begin);
   if (lptr_begin->getObject()->getType() != LossLayer::type) {
     bool has_loss = false;
     if (lptr_begin->getObject()->getType() == TimeDistLayer::type) {
@@ -310,18 +310,18 @@ void NeuralNetwork::backwarding(int iteration) {
       throw std::runtime_error("Error: no loss provided for training.");
   }
 
-  for (auto iter = iter_begin; iter != iter_end - 1; iter++) {
+  auto iter = iter_begin;
+  for (; iter != iter_end - 1; iter++) {
     backwarding((*iter)->getObject(), iteration, true);
   }
 
-  auto last_layer = (*(iter_end - 1))->getObject();
   /**
    * The last trainable layer need not calculate the derivatives
    */
 #ifdef ENABLE_TEST
-  backwarding(last_layer, iteration, true);
+  backwarding((*iter)->getObject(), iteration, true);
 #else
-  backwarding(last_layer, iteration, false);
+  backwarding((*iter)->getObject(), iteration, false);
 #endif
 }
 
@@ -812,10 +812,8 @@ void NeuralNetwork::print(std::ostream &out, unsigned int flags,
     printInstance(out, this);
   }
 
-  // TODO: get sorted layers if initialized
-  auto layers = model_graph.getLayerNodes();
   if (flags & PRINT_GRAPH_INFO) {
-    out << "graph contains " << layers.size() << " operation nodes\n";
+    out << "graph contains " << model_graph.size() << " operation nodes\n";
     /// @todo print graph info
   }
 
@@ -835,12 +833,14 @@ void NeuralNetwork::print(std::ostream &out, unsigned int flags,
     /// initialized, loss layer will be printed)
   }
 
-  if (layers.empty()) {
+  if (model_graph.empty()) {
     out << "model is empty!" << std::endl;
     return;
   }
 
   /** print layer properties */
+  // TODO: get sorted layers if initialized
+  auto layers = model_graph.getLayerNodes();
   for (auto &layer : layers)
     layer->getObject()->printPreset(out, layerPrintPreset);
 
index 497bf11..1a6d3ea 100644 (file)
@@ -374,6 +374,12 @@ public:
   FlatGraphType getFlatGraph() { return model_graph.getLayerNodes(); }
 
   /**
+   * @brief get if the model is empty
+   * @param[out] true if empty, else false
+   */
+  bool empty() const { return model_graph.empty(); }
+
+  /**
    * @brief     get network graph
    * @retval NetowrkGraphType
    */