1 /******************************************************************************
2 * Copyright (c) 2017-2018, Texas Instruments Incorporated - http://www.ti.com/
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Texas Instruments Incorporated nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28 #include "dot_graph.h"
30 using Edge = boost::graph_traits<Graph>::edge_descriptor;
32 template<typename T>
33 using VertexPropertyMap =
34 typename boost::property_map<T, boost::vertex_attribute_t>::type;
36 template<typename T>
37 using EdgePropertyMap =
38 typename boost::property_map<T, boost::edge_attribute_t>::type;
40 using LayerIdMap = std::map<uint32_t, Graph*>;
42 static const char* FILLED = "filled";
43 static const char* LABEL = "label";
44 static const char* COLOR = "color";
45 static const char* STYLE = "style";
46 static const char* SHAPE = "shape";
47 static const char* BOLD = "bold";
49 static const char* GetVertexColor(uint32_t layer_type);
50 static std::string PoolingProperties(const sTIDL_PoolingParams_t& p);
51 static std::string BiasProperties(const sTIDL_BiasParams_t& p);
52 static std::string ReLUProperties(const sTIDL_ReLUParams_t& p);
55 DotGraph::DotGraph(const sTIDL_Network_t& net):
56 graph_m(net.numLayers), net_m(net)
57 {
58 AddVertices();
59 AddEdges();
60 AddMetaData();
61 }
64 void DotGraph::AddVertices()
65 {
66 // Add a vertex for each layer
67 LayerIdMap layerid_map;
68 for (int i = 0 ; i < net_m.numLayers; i++)
69 {
70 const sTIDL_Layer_t& layer = net_m.TIDLLayers[i];
72 // Special handling for input & output data layers:
73 // Do not put them in the same subgraph
74 if (layer.layerType == TIDL_DataLayer)
75 {
76 auto V = vertex(i, graph_m);
77 VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, graph_m);
78 vpm[V][LABEL] = TIDL_LayerString[layer.layerType];
79 vpm[V][COLOR] = GetVertexColor(layer.layerType);
80 vpm[V][STYLE] = FILLED;
81 vpm[V][SHAPE] = "ellipse";
83 continue;
84 }
86 // Use the layer group ID to create subgraphs
87 // Place all layers with the same ID into a single subgraph
88 uint32_t layerGroupId = layer.layersGroupId;
89 Graph* sub = nullptr;
90 if (layerid_map.find(layerGroupId) == layerid_map.end())
91 {
92 sub = &(graph_m.create_subgraph());
93 layerid_map[layerGroupId] = sub;
94 get_property(*sub, graph_name) = std::string("cluster") +
95 std::to_string(layerGroupId);
96 get_property(*sub, graph_graph_attribute)[LABEL] =
97 "Group " + std::to_string(layerGroupId);
98 get_property(*sub, graph_graph_attribute)[STYLE] = BOLD;
99 }
100 else
101 sub = layerid_map[layerGroupId];
103 auto V = add_vertex(i, *sub);
105 AddVertexProperties(V, sub, layer, i);
106 }
108 }
110 void DotGraph::AddVertexProperties(Vertex& V, Graph* g,
111 const sTIDL_Layer_t& layer,
112 int index)
113 {
114 VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, *g);
115 vpm[V][COLOR] = GetVertexColor(layer.layerType);
116 vpm[V][STYLE] = BOLD;
117 vpm[V]["xlabel"] = std::to_string(index);
118 vpm[V][LABEL] = "{";
120 switch (layer.layerType)
121 {
122 case TIDL_ConvolutionLayer:
123 {
124 vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
125 const sTIDL_ConvParams_t& p = layer.layerParams.convParams;
126 vpm[V][LABEL] += " " + std::to_string(p.kernelW) + "x" +
127 std::to_string(p.kernelH);
129 if (p.enablePooling)
130 vpm[V][LABEL] += "|" + PoolingProperties(p.poolParams);
132 if (p.enableRelU)
133 vpm[V][LABEL] += "|" + ReLUProperties(p.reluParams);
135 if (p.enableBias)
136 vpm[V][LABEL] += "|" +
137 std::string(TIDL_LayerString[TIDL_BiasLayer]);
139 break;
140 }
142 case TIDL_PoolingLayer:
143 {
144 const sTIDL_PoolingParams_t& p = layer.layerParams.poolParams;
145 vpm[V][LABEL] += PoolingProperties(p);
146 break;
147 }
149 case TIDL_BiasLayer:
150 {
151 const sTIDL_BiasParams_t& p = layer.layerParams.biasParams;
152 vpm[V][LABEL] += BiasProperties(p);
153 break;
154 }
156 case TIDL_ReLULayer:
157 {
158 const sTIDL_ReLUParams_t& p = layer.layerParams.reluParams;
159 vpm[V][LABEL] += ReLUProperties(p);
160 break;
161 }
163 case TIDL_EltWiseLayer:
164 {
166 vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
167 const sTIDL_EltWiseParams_t& p = layer.layerParams.eltWiseParams;
168 if (p.eltWiseType == TIDL_EltWiseProduct)
169 vpm[V][LABEL] += " Product";
170 else if (p.eltWiseType == TIDL_EltWiseSum)
171 vpm[V][LABEL] += " Sum";
172 else if (p.eltWiseType == TIDL_EltWiseMax)
173 vpm[V][LABEL] += " Max";
175 break;
176 }
178 default:
179 vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
180 break;
181 }
183 vpm[V][LABEL] += "}";
184 }
187 void DotGraph::AddEdges()
188 {
189 // Add edges based on dataId i.e. there is an edge from layer A -> B
190 // iff dataId is in outData for A and inData for B.
191 EdgePropertyMap<Graph> ep = boost::get(boost::edge_attribute, graph_m);
192 for (int i = 0 ; i < net_m.numLayers; i++)
193 {
194 const sTIDL_Layer_t& layer = net_m.TIDLLayers[i];
196 if (layer.numOutBufs < 0)
197 continue;
199 // Create a string to annotate the edge - num_channels, height, width
200 const sTIDL_DataParams_t& outData = layer.outData[0];
201 int32_t out_id = outData.dataId;
202 std::string edge_info = std::to_string(outData.dimValues[1])+ "x" +
203 std::to_string(outData.dimValues[2])+ "x" +
204 std::to_string(outData.dimValues[3]);
206 for (int j = 0; j < net_m.numLayers; j++)
207 {
208 if (j == i) continue;
210 for (int x = 0; x < net_m.TIDLLayers[j].numInBufs; x++)
211 if (net_m.TIDLLayers[j].inData[x].dataId == out_id)
212 {
213 Edge e = add_edge(i, j, graph_m).first;
214 ep[e][LABEL] = edge_info;
215 }
217 }
218 }
219 }
222 void DotGraph::AddMetaData()
223 {
224 get_property(graph_m, graph_name) = "TIDL Network";
225 get_property(graph_m, graph_vertex_attribute)[SHAPE] = "Mrecord";
226 get_property(graph_m, graph_graph_attribute)["fontname"] = "Arial";
227 get_property(graph_m, graph_vertex_attribute)["fontname"] = "Arial";
228 get_property(graph_m, graph_vertex_attribute)["fontsize"] = "12";
229 get_property(graph_m, graph_edge_attribute)["fontname"] = "Arial";
230 get_property(graph_m, graph_edge_attribute)["fontsize"] = "10";
231 }
234 // Generate dot file from boost graph
235 void DotGraph::Write(const std::string& filename) const
236 {
237 std::ofstream dot(filename);
238 boost::write_graphviz(dot, graph_m);
239 }
241 std::string PoolingProperties(const sTIDL_PoolingParams_t& p)
242 {
243 std::string s = TIDL_LayerString[TIDL_PoolingLayer];
245 if (p.poolingType == TIDL_MaxPooling)
246 s += " Max";
247 else if (p.poolingType == TIDL_AveragePooling)
248 s += " Average";
250 if (p.kernelW != 0 && p.kernelH != 0)
251 s+= "\\n" + std::to_string(p.kernelW) + "x" + std::to_string(p.kernelH);
253 return s;
254 }
256 std::string ReLUProperties(const sTIDL_ReLUParams_t& p)
257 {
258 std::string s;
260 if (p.reluType == TIDL_RelU)
261 s += "ReLU";
262 else if (p.reluType == TIDL_PRelU)
263 s += "PReLU";
264 else if (p.reluType == TIDL_RelU6)
265 s += "ReLU6";
267 return s;
268 }
270 std::string BiasProperties(const sTIDL_BiasParams_t& p)
271 {
272 std::string s = TIDL_LayerString[TIDL_BiasLayer];
273 s += "\\n (BQ: " + std::to_string(p.biasQ) + ")";
274 return s;
275 }
279 const char* GetVertexColor(uint32_t layer_type)
280 {
281 static const char* LayerColors[] =
282 {
283 "lightblue",
284 "red",
285 "darkorange",
286 "royalblue",
287 "darkgreen",
288 "yellow",
289 "magenta",
290 "darkviolet",
291 "brown",
292 "darksalmon",
293 "violet",
294 "darkturquoise",
295 "darkseagreen",
296 "limegreen"
298 };
300 return LayerColors[layer_type % (sizeof(LayerColors)/sizeof(char *))];
301 }
304 const char* TIDL_LayerString[] =
305 {
306 "Data",
307 "Convolution",
308 "Pooling",
309 "ReLU",
310 "PReLU",
311 "EltWise",
312 "InnerProduct",
313 "SoftMax",
314 "BatchNorm",
315 "Bias",
316 "Scale",
317 "Deconv2D",
318 "Concat",
319 "Split",
320 "Slice",
321 "Crop",
322 "Flatten",
323 "DropOut",
324 "ArgMax",
325 "DetectionOutput",
326 "Reshape",
327 };