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) {}
* @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
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);
}
/**
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
* 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
* @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
* @retval Graph Object copyed
*/
GraphCore ©(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;
}
* @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
* @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 */
#ifndef __GRAPH_NODE_H__
#define __GRAPH_NODE_H__
+#include <iterator>
#include <memory>
#include <string>
#include <vector>
// virtual GraphNode ©(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__
#include <rnn.h>
#include <time_dist.h>
+#define LNODE(x) std::static_pointer_cast<LayerNode>(x)
+
namespace nntrainer {
/**
status = connectGraph();
NN_RETURN_STATUS();
- topologicalSort();
+ graph.topologicalSort();
+
+ countNonTrainableLayersAtBegin();
status = addLossLayer(loss_type);
NN_RETURN_STATUS();
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;
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) {
}
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());
}
}
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 ¤t) {
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();
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 ¤t) {
- 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 "
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();
/** 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;
}
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");
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);
}
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;
}
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();
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;
}
}
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") {
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;
}
}
- 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();
* 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) {
"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");
}
}
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;
}
}
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) ||
}
/** 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");
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
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);
}
}
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);
/// 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;
/** 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) {
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,
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) {
#include <stack>
#include <vector>
+#include <graph_core.h>
#include <layer_internal.h>
#include <layer_node.h>
#include <loss_layer.h>
/**
* @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
* @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
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);
}
*/
void reset() {
- adj.clear();
- Sorted.clear();
- layer_names.clear();
- def_name_count = 0;
+ graph.reset();
skip_non_trainable_layers = 0;
}
* @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
* @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;
}
/**
* @retval Graph Object copyed
*/
NetworkGraph ©(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;
}
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 */
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.
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);