summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 2acb582)
raw | patch | inline | side by side (parent: 2acb582)
author | Ajay Jayaraj <ajayj@ti.com> | |
Wed, 9 May 2018 15:20:48 +0000 (10:20 -0500) | ||
committer | Ajay Jayaraj <ajayj@ti.com> | |
Wed, 9 May 2018 17:30:23 +0000 (12:30 -0500) |
* Additional layer specific annotations to nodes
* Generate SVG file if /usr/bin/dot is available
* Refactoring and cleanup
(MCT-975)
* Generate SVG file if /usr/bin/dot is available
* Refactoring and cleanup
(MCT-975)
utils/Makefile | patch | blob | history | |
utils/main.cpp | patch | blob | history | |
utils/src/dot_graph.cpp | [new file with mode: 0644] | patch | blob |
utils/src/dot_graph.h | [new file with mode: 0644] | patch | blob |
utils/src/tinn_utils.cpp | patch | blob | history |
diff --git a/utils/Makefile b/utils/Makefile
index 369e7dcf7512464495bb7de480d5f0f8ca303195..f36f670cfee4ea40bed33be175d4b47548f139d4 100644 (file)
--- a/utils/Makefile
+++ b/utils/Makefile
TARGET ?= arm
-EXE = $(TARGET)/network_display
+EXE = $(TARGET)/netviz
TINN_API_DIR=../tinn_api
UNAME_M :=$(shell uname -m)
+# -m32 required on x86 to ensure consistent layout of struct sTIDL_Network_t
ifeq ($(TARGET),x86)
CXX = g++
- CXXFLAGS = -m32
+ CXXFLAGS = -m32 -static
LDFLAGS =
endif
-CXXFLAGS += -o3 --std=c++11 -Wall -Werror
+CXXFLAGS += -o3
+#CXXFLAGS += -g -ggdb
+CXXFLAGS += --std=c++11 -Wall -Werror
CXXFLAGS += -Iinc -I$(TINN_API_DIR)/inc -I$(TINN_API_DIR)/src
-SOURCES = main.cpp src/tinn_utils.cpp $(TINN_API_DIR)/src/util.cpp
+SOURCES = main.cpp src/tinn_utils.cpp src/dot_graph.cpp $(TINN_API_DIR)/src/util.cpp
$(EXE): $(TINN_LIB) $(HEADERS) $(SOURCES)
mkdir -p $(TARGET)
diff --git a/utils/main.cpp b/utils/main.cpp
index 13372798c2df053dcfb5ca920ee514c4fae093e6..06f556da4b3ac60b483fb14540b824d4d946fc8d 100644 (file)
--- a/utils/main.cpp
+++ b/utils/main.cpp
#include <fstream>
#include <cassert>
#include <string>
+#include <cstdlib>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
#include "tinn_utils.h"
+using namespace tinn;
static void ProcessArgs(int argc, char *argv[], std::string& network_file,
bool& do_print, std::string& dot_file);
static void DisplayHelp();
+static bool fs_exists(std::string path);
int main(int argc, char *argv[])
{
bool status = true;
+ // Dump network to stdout if requested
if (do_print)
- status &= tinn::util::PrintNetwork(network_file);
+ status &= util::PrintNetwork(network_file);
+
+ // Generate a dot file
+ status &= util::GenerateDotGraphForNetwork(network_file, dot_file);
+
+ // If the dot utility exists, generate a SVG file
+ const std::string dot_bin("/usr/bin/dot");
+ if (fs_exists(dot_bin))
+ {
+ std::string command = dot_bin;
+ command += " -Tsvg " + dot_file + " -o " + dot_file + ".svg";
+ int x = std::system(command.c_str());
+ if (x != 0) status = false;
- if (!dot_file.empty())
- status &= tinn::util::GenerateDotGraphForNetwork(network_file, dot_file);
+ }
if (!status)
return EXIT_FAILURE;
void ProcessArgs(int argc, char *argv[], std::string& network_file,
bool& do_print, std::string& dot_file)
{
- if (argc < 2)
+ if (argc < 4)
{
DisplayHelp();
exit(EXIT_SUCCESS);
break;
}
}
+
+ if (dot_file.empty())
+ {
+ std::cerr << "ERROR: output dot file not specified." << std::endl;
+ DisplayHelp();
+ exit(EXIT_FAILURE);
+ }
}
void DisplayHelp()
{
- std::cout << "Usage: tinn_display [option] <Network binary file>\n"
+ std::cout << "Usage: netviz -d <dot file name> <network binary file>\n"
"Options: \n"
" -p Print network layer info\n"
- " -d <file name> Generate network graph (dot file)\n"
" -h Display this help message\n";
}
+
+bool fs_exists(std::string path)
+{
+ struct stat statbuf;
+ if (stat(path.c_str(), &statbuf) == 0) return true;
+ else return false;
+}
+
+
diff --git a/utils/src/dot_graph.cpp b/utils/src/dot_graph.cpp
--- /dev/null
+++ b/utils/src/dot_graph.cpp
@@ -0,0 +1,324 @@
+/******************************************************************************
+ * Copyright (c) 2017-18, Texas Instruments Incorporated - http://www.ti.com/
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Texas Instruments Incorporated nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include "dot_graph.h"
+
+using Edge = boost::graph_traits<Graph>::edge_descriptor;
+
+template<typename T>
+using VertexPropertyMap =
+ typename boost::property_map<T, boost::vertex_attribute_t>::type;
+
+template<typename T>
+using EdgePropertyMap =
+ typename boost::property_map<T, boost::edge_attribute_t>::type;
+
+using LayerIdMap = std::map<uint32_t, Graph*>;
+
+static const char* FILLED = "filled";
+static const char* LABEL = "label";
+static const char* COLOR = "color";
+static const char* STYLE = "style";
+static const char* SHAPE = "shape";
+static const char* BOLD = "bold";
+
+static const char* GetVertexColor(uint32_t layer_type);
+static std::string PoolingProperties(const sTIDL_PoolingParams_t& p);
+static std::string BiasProperties(const sTIDL_BiasParams_t& p);
+static std::string ReLUProperties(const sTIDL_ReLUParams_t& p);
+
+
+DotGraph::DotGraph(const sTIDL_Network_t& net):
+ graph_m(net.numLayers), net_m(net)
+{
+ AddVertices();
+ AddEdges();
+ AddMetaData();
+}
+
+
+void DotGraph::AddVertices()
+{
+ // Add a vertex for each layer
+ LayerIdMap layerid_map;
+ for (int i = 0 ; i < net_m.numLayers; i++)
+ {
+ const sTIDL_Layer_t& layer = net_m.TIDLLayers[i];
+
+ // Special handling for input & output data layers:
+ // Do not put them in the same subgraph
+ if (layer.layerType == TIDL_DataLayer)
+ {
+ auto V = vertex(i, graph_m);
+ VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, graph_m);
+ vpm[V][LABEL] = TIDL_LayerString[layer.layerType];
+ vpm[V][COLOR] = GetVertexColor(layer.layerType);
+ vpm[V][STYLE] = FILLED;
+ vpm[V][SHAPE] = "ellipse";
+
+ continue;
+ }
+
+ // Use the layer group ID to create subgraphs
+ // Place all layers with the same ID into a single subgraph
+ uint32_t layerGroupId = layer.layersGroupId;
+ Graph* sub = nullptr;
+ if (layerid_map.find(layerGroupId) == layerid_map.end())
+ {
+ sub = &(graph_m.create_subgraph());
+ layerid_map[layerGroupId] = sub;
+ get_property(*sub, graph_name) = std::string("cluster") +
+ std::to_string(layerGroupId);
+ get_property(*sub, graph_graph_attribute)[LABEL] =
+ "Group " + std::to_string(layerGroupId);
+ get_property(*sub, graph_graph_attribute)[STYLE] = BOLD;
+ }
+ else
+ sub = layerid_map[layerGroupId];
+
+ auto V = add_vertex(i, *sub);
+
+ AddVertexProperties(V, sub, layer);
+ }
+
+}
+
+void DotGraph::AddVertexProperties(Vertex& V, Graph* g,
+ const sTIDL_Layer_t& layer)
+{
+ VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, *g);
+ vpm[V][COLOR] = GetVertexColor(layer.layerType);
+ vpm[V][STYLE] = BOLD;
+ vpm[V][LABEL] = "{";
+
+ switch (layer.layerType)
+ {
+ case TIDL_ConvolutionLayer:
+ {
+ vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
+ const sTIDL_ConvParams_t& p = layer.layerParams.convParams;
+ vpm[V][LABEL] += "\\n" + std::to_string(p.kernelW) + "x" +
+ std::to_string(p.kernelH);
+
+ if (p.enablePooling)
+ vpm[V][LABEL] += "|" + PoolingProperties(p.poolParams);
+
+ if (p.enableRelU)
+ vpm[V][LABEL] += "|" + ReLUProperties(p.reluParams);
+
+ if (p.enableBias)
+ vpm[V][LABEL] += "|" +
+ std::string(TIDL_LayerString[TIDL_BiasLayer]);
+
+ break;
+ }
+
+ case TIDL_PoolingLayer:
+ {
+ const sTIDL_PoolingParams_t& p = layer.layerParams.poolParams;
+ vpm[V][LABEL] += PoolingProperties(p);
+ break;
+ }
+
+ case TIDL_BiasLayer:
+ {
+ const sTIDL_BiasParams_t& p = layer.layerParams.biasParams;
+ vpm[V][LABEL] += BiasProperties(p);
+ break;
+ }
+
+ case TIDL_ReLULayer:
+ {
+ const sTIDL_ReLUParams_t& p = layer.layerParams.reluParams;
+ vpm[V][LABEL] += ReLUProperties(p);
+ break;
+ }
+
+ case TIDL_EltWiseLayer:
+ {
+
+ vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
+ const sTIDL_EltWiseParams_t& p = layer.layerParams.eltWiseParams;
+ if (p.eltWiseType == TIDL_EltWiseProduct)
+ vpm[V][LABEL] += " Product";
+ else if (p.eltWiseType == TIDL_EltWiseSum)
+ vpm[V][LABEL] += " Sum";
+ else if (p.eltWiseType == TIDL_EltWiseMax)
+ vpm[V][LABEL] += " Max";
+
+ break;
+ }
+
+ default:
+ vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
+ break;
+ }
+
+ vpm[V][LABEL] += "}";
+}
+
+
+void DotGraph::AddEdges()
+{
+ // Add edges based on dataId i.e. there is an edge from layer A -> B
+ // iff dataId is in outData for A and inData for B.
+ EdgePropertyMap<Graph> ep = boost::get(boost::edge_attribute, graph_m);
+ for (int i = 0 ; i < net_m.numLayers; i++)
+ {
+ const sTIDL_Layer_t& layer = net_m.TIDLLayers[i];
+
+ if (layer.numOutBufs < 0)
+ continue;
+
+ // Create a string to annotate the edge - num_channels, height, width
+ const sTIDL_DataParams_t& outData = layer.outData[0];
+ int32_t out_id = outData.dataId;
+ std::string edge_info = std::to_string(outData.dimValues[1])+ "x" +
+ std::to_string(outData.dimValues[2])+ "x" +
+ std::to_string(outData.dimValues[3]);
+
+ for (int j = 0; j < net_m.numLayers; j++)
+ {
+ if (j == i) continue;
+
+ for (int x = 0; x < net_m.TIDLLayers[j].numInBufs; x++)
+ if (net_m.TIDLLayers[j].inData[x].dataId == out_id)
+ {
+ Edge e = add_edge(i, j, graph_m).first;
+ ep[e][LABEL] = edge_info;
+ }
+
+ }
+ }
+}
+
+
+void DotGraph::AddMetaData()
+{
+ get_property(graph_m, graph_name) = "TIDL Network";
+ get_property(graph_m, graph_vertex_attribute)[SHAPE] = "Mrecord";
+ get_property(graph_m, graph_graph_attribute)["fontname"] = "Arial";
+ get_property(graph_m, graph_vertex_attribute)["fontname"] = "Arial";
+ get_property(graph_m, graph_vertex_attribute)["fontsize"] = "12";
+ get_property(graph_m, graph_edge_attribute)["fontname"] = "Arial";
+ get_property(graph_m, graph_edge_attribute)["fontsize"] = "10";
+}
+
+
+// Generate dot file from boost graph
+void DotGraph::Write(const std::string& filename) const
+{
+ std::ofstream dot(filename);
+ boost::write_graphviz(dot, graph_m);
+}
+
+std::string PoolingProperties(const sTIDL_PoolingParams_t& p)
+{
+ std::string s = TIDL_LayerString[TIDL_PoolingLayer];
+
+ if (p.poolingType == TIDL_MaxPooling)
+ s += " Max";
+ else if (p.poolingType == TIDL_AveragePooling)
+ s += " Average";
+
+ s+= "\\n" + std::to_string(p.kernelW) + "x" + std::to_string(p.kernelH);
+
+ return s;
+}
+
+std::string ReLUProperties(const sTIDL_ReLUParams_t& p)
+{
+ std::string s;
+
+ if (p.reluType == TIDL_RelU)
+ s += "ReLU";
+ else if (p.reluType == TIDL_PRelU)
+ s += "PReLU";
+ else if (p.reluType == TIDL_RelU6)
+ s += "ReLU6";
+
+ return s;
+}
+
+std::string BiasProperties(const sTIDL_BiasParams_t& p)
+{
+ std::string s = TIDL_LayerString[TIDL_BiasLayer];
+ s += "\\n (BQ: " + std::to_string(p.biasQ) + ")";
+ return s;
+}
+
+
+
+const char* GetVertexColor(uint32_t layer_type)
+{
+ static const char* LayerColors[] =
+ {
+ "lightblue",
+ "red",
+ "darkorange",
+ "royalblue",
+ "darkgreen",
+ "yellow",
+ "magenta",
+ "darkviolet",
+ "brown",
+ "darksalmon",
+ "violet",
+ "darkturquoise",
+ "darkseagreen",
+ "limegreen"
+
+ };
+
+ return LayerColors[layer_type % (sizeof(LayerColors)/sizeof(char *))];
+}
+
+
+const char* TIDL_LayerString[] =
+{
+ "Data",
+ "Convolution",
+ "Pooling",
+ "ReLU",
+ "PReLU",
+ "EltWise",
+ "InnerProduct",
+ "SoftMax",
+ "BatchNorm",
+ "Bias",
+ "Scale",
+ "Deconv2D",
+ "Concat",
+ "Split",
+ "Slice",
+ "Crop",
+ "Flatten",
+ "DropOut",
+ "ArgMax",
+ "DetectionOutput",
+ "Reshape",
+};
diff --git a/utils/src/dot_graph.h b/utils/src/dot_graph.h
--- /dev/null
+++ b/utils/src/dot_graph.h
@@ -0,0 +1,101 @@
+/******************************************************************************
+ * Copyright (c) 2017-18, Texas Instruments Incorporated - http://www.ti.com/
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Texas Instruments Incorporated nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#pragma once
+
+#include <cstdint>
+#include "tidl_create_params.h"
+
+#include <boost/graph/graphviz.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/subgraph.hpp>
+
+using boost::adjacency_list;
+using boost::property;
+using boost::subgraph;
+using boost::get_property;
+using boost::add_vertex;
+using boost::add_edge;
+using boost::edge_index_t;
+using boost::edge_attribute_t;
+using boost::vertex_attribute_t;
+using boost::vertex_attribute;
+using boost::graph_name_t;
+using boost::graph_name;
+using boost::graph_graph_attribute_t;
+using boost::graph_graph_attribute;
+using boost::graph_vertex_attribute_t;
+using boost::graph_vertex_attribute;
+using boost::graph_edge_attribute_t;
+using boost::graph_edge_attribute;
+using boost::vecS;
+
+using GraphvizAttributes = std::map<std::string, std::string>;
+
+
+
+// Boost graph with notes and edges annotated with Dot properties.
+// These properties are used by the write_graphviz function.
+//
+// From https://www.boost.org/doc/libs/1_55_0/libs/graph/doc/subgraph.html
+// When creating a subgraph, the underlying graph type is required to have
+// vertex_index and edge_index internal properties. Add an edge index property
+// to the adjacency list. We do not need to add a vertex index property
+// because it is built in to the adjacency_list.
+using Graph =
+ subgraph<
+ adjacency_list<vecS, vecS, boost::directedS,
+ property<vertex_attribute_t, GraphvizAttributes>,
+ property<edge_index_t,int,property<edge_attribute_t, GraphvizAttributes>>,
+ property<graph_name_t, std::string,
+ property<graph_graph_attribute_t, GraphvizAttributes,
+ property<graph_vertex_attribute_t, GraphvizAttributes,
+ property<graph_edge_attribute_t, GraphvizAttributes>
+ >>>
+ >>;
+
+using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
+
+class DotGraph
+{
+ public:
+ DotGraph(const sTIDL_Network_t& net);
+ ~DotGraph() {}
+
+ void Write(const std::string& filename) const;
+
+ private:
+ void AddVertices();
+ void AddEdges();
+ void AddMetaData();
+ void AddVertexProperties(Vertex& V, Graph* g, const sTIDL_Layer_t& layer);
+
+ Graph graph_m;
+ const sTIDL_Network_t& net_m;
+};
+
+extern const char* TIDL_LayerString[];
index c6932c73b42af67c13cb7e4c3420cb806c71a3d3..ba5e3178e91c3a6ea47f2df7a1ef1a9a174b1600 100644 (file)
--- a/utils/src/tinn_utils.cpp
+++ b/utils/src/tinn_utils.cpp
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
-#include <boost/graph/graphviz.hpp>
-#include <boost/graph/adjacency_list.hpp>
-#include <boost/graph/subgraph.hpp>
-#include <iostream>
-
#include "util.h"
#include "tinn_utils.h"
-#include "tidl_create_params.h"
+#include "dot_graph.h"
using namespace tinn::util;
-const char* TIDL_LayerString[] =
-{
- "Data",
- "Convolution",
- "Pooling",
- "ReLU",
- "PReLU",
- "EltWise",
- "InnerProduct",
- "SoftMax",
- "BatchNorm",
- "Bias",
- "Scale",
- "Deconv2D",
- "Concat",
- "Split",
- "Slice",
- "Crop",
- "Flatten",
- "DropOut",
- "ArgMax",
- "DetectionOutput",
- "Reshape",
-};
-
-
-static bool CreateGraph(const sTIDL_Network_t& net,
- const std::string& dot_file);
-
-static const char* GetVertexColor(uint32_t layer_type);
-
bool tinn::util::PrintNetwork(const std::string& network_binary,
std::ostream& os)
{
reinterpret_cast<char *>(&net),
sizeof(sTIDL_Network_t));
if (!status)
- return false;
+ {
+ std::cerr << "ERROR: Invalid network binary: "
+ << network_binary << std::endl;
+ exit(EXIT_FAILURE);
+ }
printf("%3s %-20s %3s %3s %3s "
" %3s %3s %3s %3s %3s %3s %3s %3s %3s "
return true;
}
+
bool tinn::util::GenerateDotGraphForNetwork(const std::string& network_binary,
const std::string& dot_file)
{
@@ -130,185 +99,15 @@ bool tinn::util::GenerateDotGraphForNetwork(const std::string& network_binary,
reinterpret_cast<char *>(&net),
sizeof(sTIDL_Network_t));
if (!status)
- return false;
-
- return CreateGraph(net, dot_file);
-}
-
-
-
-using boost::adjacency_list;
-using boost::property;
-using boost::subgraph;
-using boost::get_property;
-using boost::add_vertex;
-using boost::add_edge;
-using boost::edge_index_t;
-using boost::edge_attribute_t;
-using boost::vertex_attribute_t;
-using boost::vertex_attribute;
-using boost::graph_name_t;
-using boost::graph_name;
-using boost::graph_graph_attribute_t;
-using boost::graph_graph_attribute;
-using boost::graph_vertex_attribute_t;
-using boost::graph_vertex_attribute;
-using boost::graph_edge_attribute_t;
-using boost::graph_edge_attribute;
-using boost::vecS;
-
-using GraphvizAttributes = std::map<std::string, std::string>;
-
-
-// Boost graph with notes and edges annotated with Dot properties.
-// These properties are used by the write_graphviz function.
-//
-// From https://www.boost.org/doc/libs/1_55_0/libs/graph/doc/subgraph.html
-// When creating a subgraph, the underlying graph type is required to have
-// vertex_index and edge_index internal properties. Add an edge index property
-// to the adjacency list. We do not need to add a vertex index property
-// because it is built in to the adjacency_list.
-using Graph =
- subgraph<
- adjacency_list<vecS, vecS, boost::directedS,
- property<vertex_attribute_t, GraphvizAttributes>,
- property<edge_index_t,int,property<edge_attribute_t, GraphvizAttributes>>,
- property<graph_name_t, std::string,
- property<graph_graph_attribute_t, GraphvizAttributes,
- property<graph_vertex_attribute_t, GraphvizAttributes,
- property<graph_edge_attribute_t, GraphvizAttributes>
- >>>
- >>;
-
-using Edge = boost::graph_traits<Graph>::edge_descriptor;
-
-template<typename T>
-using VertexPropertyMap = typename boost::property_map<T, boost::vertex_attribute_t>::type;
-
-template<typename T>
-using EdgePropertyMap = typename boost::property_map<T, boost::edge_attribute_t>::type;
-
-using LayerIdMap = std::map<uint32_t, Graph*>;
-
-
-bool CreateGraph(const sTIDL_Network_t& net, const std::string& dot_file)
-{
- static const char* FILLED = "filled";
- static const char* LABEL = "label";
- static const char* COLOR = "color";
- static const char* STYLE = "style";
- static const char* SHAPE = "shape";
-
- Graph tidl(net.numLayers);
-
- // Add a vertex for each layer
- LayerIdMap layerid_map;
- for (int i = 0 ; i < net.numLayers; i++)
{
- uint32_t layer_type = net.TIDLLayers[i].layerType;
-
- // Special handling for input & output data layers:
- // Do not put them in the same subgraph
- if (net.TIDLLayers[i].layerType == TIDL_DataLayer)
- {
- auto V = vertex(i, tidl);
- VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, tidl);
- vpm[V][LABEL] = TIDL_LayerString[layer_type];
- vpm[V][COLOR] = GetVertexColor(layer_type);
- vpm[V][STYLE] = FILLED;
- vpm[V][SHAPE] = "ellipse";
-
- continue;
- }
-
- // Use the layer group ID to create subgraphs
- // Place all layers with the same ID into a single subgraph
- uint32_t layerGroupId = net.TIDLLayers[i].layersGroupId;
- Graph* sub = nullptr;
- if (layerid_map.find(layerGroupId) == layerid_map.end())
- {
- sub = &(tidl.create_subgraph());
- layerid_map[layerGroupId] = sub;
- get_property(*sub, graph_name) = std::string("cluster") +
- std::to_string(layerGroupId);
- get_property(*sub, graph_graph_attribute)[LABEL] =
- std::to_string(layerGroupId);
- get_property(*sub, graph_graph_attribute)[STYLE] = FILLED;
- get_property(*sub, graph_graph_attribute)["fillcolor"] = "lightgrey";
- }
- else
- sub = layerid_map[layerGroupId];
-
- auto V = add_vertex(i, *sub);
- VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, *sub);
- vpm[V][LABEL] = TIDL_LayerString[layer_type];
- vpm[V][COLOR] = GetVertexColor(layer_type);
- vpm[V][STYLE] = FILLED;
-
- }
-
- // Add edges based on dataId i.e. there is an edge from layer A -> B
- // iff dataId is in outData for A and inData for B.
- EdgePropertyMap<Graph> ep = boost::get(boost::edge_attribute, tidl);
- for (int i = 0 ; i < net.numLayers; i++)
- {
- if (net.TIDLLayers[i].numOutBufs < 0)
- continue;
-
- // Create a string to annotate the edge - num_channels, height, width
- int32_t out_id = net.TIDLLayers[i].outData[0].dataId;
- std::string edge_info =
- std::to_string(net.TIDLLayers[i].outData[0].dimValues[1])+
- "x" +
- std::to_string(net.TIDLLayers[i].outData[0].dimValues[2])+
- "x" +
- std::to_string(net.TIDLLayers[i].outData[0].dimValues[3]);
-
- for (int j = 0; j < net.numLayers; j++)
- {
- if (j == i) continue;
-
- for (int x = 0; x < net.TIDLLayers[j].numInBufs; x++)
- if (net.TIDLLayers[j].inData[x].dataId == out_id)
- {
- Edge e = add_edge(i, j, tidl).first;
- ep[e][LABEL] = edge_info;
- }
-
- }
+ std::cerr << "ERROR: Invalid network binary: "
+ << network_binary << std::endl;
+ exit(EXIT_FAILURE);
}
- get_property(tidl, graph_name) = "TIDL Network";
- get_property(tidl, graph_vertex_attribute)[SHAPE] = "Mrecord";
- // Generate dot file from boost graph
- std::ofstream dot(dot_file);
- boost::write_graphviz(dot, tidl);
+ DotGraph g(net);
+ g.Write(dot_file);
return true;
}
-
-
-const char* GetVertexColor(uint32_t layer_type)
-{
- static const char* LayerColors[] =
- {
- "lightblue",
- "lightseagreen",
- "lightpink",
- "sienna",
- "lightyellow",
- "palegoldenrod",
- "lightsalmon",
- "lightcyan",
- "wheat",
- "olivedrab",
- "lightskyblue",
- "beige",
- "lavender",
- "linen"
-
- };
-
- return LayerColors[layer_type % (sizeof(LayerColors)/sizeof(char *))];
-}