1 /******************************************************************************
2 * Copyright (c) 2017-18, 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);
106 }
108 }
110 void DotGraph::AddVertexProperties(Vertex& V, Graph* g,
111 const sTIDL_Layer_t& layer)
112 {
113 VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, *g);
114 vpm[V][COLOR] = GetVertexColor(layer.layerType);
115 vpm[V][STYLE] = BOLD;
116 vpm[V][LABEL] = "{";
118 switch (layer.layerType)
119 {
120 case TIDL_ConvolutionLayer:
121 {
122 vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
123 const sTIDL_ConvParams_t& p = layer.layerParams.convParams;
124 vpm[V][LABEL] += "\\n" + std::to_string(p.kernelW) + "x" +
125 std::to_string(p.kernelH);
127 if (p.enablePooling)
128 vpm[V][LABEL] += "|" + PoolingProperties(p.poolParams);
130 if (p.enableRelU)
131 vpm[V][LABEL] += "|" + ReLUProperties(p.reluParams);
133 if (p.enableBias)
134 vpm[V][LABEL] += "|" +
135 std::string(TIDL_LayerString[TIDL_BiasLayer]);
137 break;
138 }
140 case TIDL_PoolingLayer:
141 {
142 const sTIDL_PoolingParams_t& p = layer.layerParams.poolParams;
143 vpm[V][LABEL] += PoolingProperties(p);
144 break;
145 }
147 case TIDL_BiasLayer:
148 {
149 const sTIDL_BiasParams_t& p = layer.layerParams.biasParams;
150 vpm[V][LABEL] += BiasProperties(p);
151 break;
152 }
154 case TIDL_ReLULayer:
155 {
156 const sTIDL_ReLUParams_t& p = layer.layerParams.reluParams;
157 vpm[V][LABEL] += ReLUProperties(p);
158 break;
159 }
161 case TIDL_EltWiseLayer:
162 {
164 vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
165 const sTIDL_EltWiseParams_t& p = layer.layerParams.eltWiseParams;
166 if (p.eltWiseType == TIDL_EltWiseProduct)
167 vpm[V][LABEL] += " Product";
168 else if (p.eltWiseType == TIDL_EltWiseSum)
169 vpm[V][LABEL] += " Sum";
170 else if (p.eltWiseType == TIDL_EltWiseMax)
171 vpm[V][LABEL] += " Max";
173 break;
174 }
176 default:
177 vpm[V][LABEL] += TIDL_LayerString[layer.layerType];
178 break;
179 }
181 vpm[V][LABEL] += "}";
182 }
185 void DotGraph::AddEdges()
186 {
187 // Add edges based on dataId i.e. there is an edge from layer A -> B
188 // iff dataId is in outData for A and inData for B.
189 EdgePropertyMap<Graph> ep = boost::get(boost::edge_attribute, graph_m);
190 for (int i = 0 ; i < net_m.numLayers; i++)
191 {
192 const sTIDL_Layer_t& layer = net_m.TIDLLayers[i];
194 if (layer.numOutBufs < 0)
195 continue;
197 // Create a string to annotate the edge - num_channels, height, width
198 const sTIDL_DataParams_t& outData = layer.outData[0];
199 int32_t out_id = outData.dataId;
200 std::string edge_info = std::to_string(outData.dimValues[1])+ "x" +
201 std::to_string(outData.dimValues[2])+ "x" +
202 std::to_string(outData.dimValues[3]);
204 for (int j = 0; j < net_m.numLayers; j++)
205 {
206 if (j == i) continue;
208 for (int x = 0; x < net_m.TIDLLayers[j].numInBufs; x++)
209 if (net_m.TIDLLayers[j].inData[x].dataId == out_id)
210 {
211 Edge e = add_edge(i, j, graph_m).first;
212 ep[e][LABEL] = edge_info;
213 }
215 }
216 }
217 }
220 void DotGraph::AddMetaData()
221 {
222 get_property(graph_m, graph_name) = "TIDL Network";
223 get_property(graph_m, graph_vertex_attribute)[SHAPE] = "Mrecord";
224 get_property(graph_m, graph_graph_attribute)["fontname"] = "Arial";
225 get_property(graph_m, graph_vertex_attribute)["fontname"] = "Arial";
226 get_property(graph_m, graph_vertex_attribute)["fontsize"] = "12";
227 get_property(graph_m, graph_edge_attribute)["fontname"] = "Arial";
228 get_property(graph_m, graph_edge_attribute)["fontsize"] = "10";
229 }
232 // Generate dot file from boost graph
233 void DotGraph::Write(const std::string& filename) const
234 {
235 std::ofstream dot(filename);
236 boost::write_graphviz(dot, graph_m);
237 }
239 std::string PoolingProperties(const sTIDL_PoolingParams_t& p)
240 {
241 std::string s = TIDL_LayerString[TIDL_PoolingLayer];
243 if (p.poolingType == TIDL_MaxPooling)
244 s += " Max";
245 else if (p.poolingType == TIDL_AveragePooling)
246 s += " Average";
248 s+= "\\n" + std::to_string(p.kernelW) + "x" + std::to_string(p.kernelH);
250 return s;
251 }
253 std::string ReLUProperties(const sTIDL_ReLUParams_t& p)
254 {
255 std::string s;
257 if (p.reluType == TIDL_RelU)
258 s += "ReLU";
259 else if (p.reluType == TIDL_PRelU)
260 s += "PReLU";
261 else if (p.reluType == TIDL_RelU6)
262 s += "ReLU6";
264 return s;
265 }
267 std::string BiasProperties(const sTIDL_BiasParams_t& p)
268 {
269 std::string s = TIDL_LayerString[TIDL_BiasLayer];
270 s += "\\n (BQ: " + std::to_string(p.biasQ) + ")";
271 return s;
272 }
276 const char* GetVertexColor(uint32_t layer_type)
277 {
278 static const char* LayerColors[] =
279 {
280 "lightblue",
281 "red",
282 "darkorange",
283 "royalblue",
284 "darkgreen",
285 "yellow",
286 "magenta",
287 "darkviolet",
288 "brown",
289 "darksalmon",
290 "violet",
291 "darkturquoise",
292 "darkseagreen",
293 "limegreen"
295 };
297 return LayerColors[layer_type % (sizeof(LayerColors)/sizeof(char *))];
298 }
301 const char* TIDL_LayerString[] =
302 {
303 "Data",
304 "Convolution",
305 "Pooling",
306 "ReLU",
307 "PReLU",
308 "EltWise",
309 "InnerProduct",
310 "SoftMax",
311 "BatchNorm",
312 "Bias",
313 "Scale",
314 "Deconv2D",
315 "Concat",
316 "Split",
317 "Slice",
318 "Crop",
319 "Flatten",
320 "DropOut",
321 "ArgMax",
322 "DetectionOutput",
323 "Reshape",
324 };