c6932c73b42af67c13cb7e4c3420cb806c71a3d3
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 <boost/graph/graphviz.hpp>
29 #include <boost/graph/adjacency_list.hpp>
30 #include <boost/graph/subgraph.hpp>
31 #include <iostream>
33 #include "util.h"
34 #include "tinn_utils.h"
35 #include "tidl_create_params.h"
37 using namespace tinn::util;
39 const char* TIDL_LayerString[] =
40 {
41 "Data",
42 "Convolution",
43 "Pooling",
44 "ReLU",
45 "PReLU",
46 "EltWise",
47 "InnerProduct",
48 "SoftMax",
49 "BatchNorm",
50 "Bias",
51 "Scale",
52 "Deconv2D",
53 "Concat",
54 "Split",
55 "Slice",
56 "Crop",
57 "Flatten",
58 "DropOut",
59 "ArgMax",
60 "DetectionOutput",
61 "Reshape",
62 };
65 static bool CreateGraph(const sTIDL_Network_t& net,
66 const std::string& dot_file);
68 static const char* GetVertexColor(uint32_t layer_type);
70 bool tinn::util::PrintNetwork(const std::string& network_binary,
71 std::ostream& os)
72 {
73 if (network_binary.empty())
74 return false;
76 sTIDL_Network_t net;
77 bool status = ReadBinary(network_binary,
78 reinterpret_cast<char *>(&net),
79 sizeof(sTIDL_Network_t));
80 if (!status)
81 return false;
83 printf("%3s %-20s %3s %3s %3s "
84 " %3s %3s %3s %3s %3s %3s %3s %3s %3s "
85 " %5s %5s %5s %5s %5s %5s %5s %5s\n",
86 "#", "Name", "gId", "#i", "#o",
87 "i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7", "o",
88 "#roi", "#ch", "h", "w", "#roi", "#ch", "h", "w");
90 for (int i = 0 ; i < net.numLayers; i++)
91 {
92 printf("%3d, %-20s,",i,
93 TIDL_LayerString[net.TIDLLayers[i].layerType]);
94 printf("%3d, %3d ,%3d ,",net.TIDLLayers[i].layersGroupId,
95 net.TIDLLayers[i].numInBufs,
96 net.TIDLLayers[i].numOutBufs);
98 for (int j = 0; j < net.TIDLLayers[i].numInBufs; j++)
99 {
100 printf("%3d ,",net.TIDLLayers[i].inData[j].dataId);
101 }
102 for (int j = (net.TIDLLayers[i].numInBufs > 0 ?
103 net.TIDLLayers[i].numInBufs : 0); j < 8; j++)
104 {
105 printf(" x ,");
106 }
107 printf("%3d ,",net.TIDLLayers[i].outData[0].dataId);
108 for (int j = 0; j < 4; j++)
109 {
110 printf("%5d ,",net.TIDLLayers[i].inData[0].dimValues[j]);
111 }
112 for (int j = 0; j < 4; j++)
113 {
114 printf("%5d ,",net.TIDLLayers[i].outData[0].dimValues[j]);
115 }
116 printf("\n");
117 }
119 return true;
120 }
122 bool tinn::util::GenerateDotGraphForNetwork(const std::string& network_binary,
123 const std::string& dot_file)
124 {
125 if (network_binary.empty())
126 return false;
128 sTIDL_Network_t net;
129 bool status = ReadBinary(network_binary,
130 reinterpret_cast<char *>(&net),
131 sizeof(sTIDL_Network_t));
132 if (!status)
133 return false;
135 return CreateGraph(net, dot_file);
136 }
140 using boost::adjacency_list;
141 using boost::property;
142 using boost::subgraph;
143 using boost::get_property;
144 using boost::add_vertex;
145 using boost::add_edge;
146 using boost::edge_index_t;
147 using boost::edge_attribute_t;
148 using boost::vertex_attribute_t;
149 using boost::vertex_attribute;
150 using boost::graph_name_t;
151 using boost::graph_name;
152 using boost::graph_graph_attribute_t;
153 using boost::graph_graph_attribute;
154 using boost::graph_vertex_attribute_t;
155 using boost::graph_vertex_attribute;
156 using boost::graph_edge_attribute_t;
157 using boost::graph_edge_attribute;
158 using boost::vecS;
160 using GraphvizAttributes = std::map<std::string, std::string>;
163 // Boost graph with notes and edges annotated with Dot properties.
164 // These properties are used by the write_graphviz function.
165 //
166 // From https://www.boost.org/doc/libs/1_55_0/libs/graph/doc/subgraph.html
167 // When creating a subgraph, the underlying graph type is required to have
168 // vertex_index and edge_index internal properties. Add an edge index property
169 // to the adjacency list. We do not need to add a vertex index property
170 // because it is built in to the adjacency_list.
171 using Graph =
172 subgraph<
173 adjacency_list<vecS, vecS, boost::directedS,
174 property<vertex_attribute_t, GraphvizAttributes>,
175 property<edge_index_t,int,property<edge_attribute_t, GraphvizAttributes>>,
176 property<graph_name_t, std::string,
177 property<graph_graph_attribute_t, GraphvizAttributes,
178 property<graph_vertex_attribute_t, GraphvizAttributes,
179 property<graph_edge_attribute_t, GraphvizAttributes>
180 >>>
181 >>;
183 using Edge = boost::graph_traits<Graph>::edge_descriptor;
185 template<typename T>
186 using VertexPropertyMap = typename boost::property_map<T, boost::vertex_attribute_t>::type;
188 template<typename T>
189 using EdgePropertyMap = typename boost::property_map<T, boost::edge_attribute_t>::type;
191 using LayerIdMap = std::map<uint32_t, Graph*>;
194 bool CreateGraph(const sTIDL_Network_t& net, const std::string& dot_file)
195 {
196 static const char* FILLED = "filled";
197 static const char* LABEL = "label";
198 static const char* COLOR = "color";
199 static const char* STYLE = "style";
200 static const char* SHAPE = "shape";
202 Graph tidl(net.numLayers);
204 // Add a vertex for each layer
205 LayerIdMap layerid_map;
206 for (int i = 0 ; i < net.numLayers; i++)
207 {
208 uint32_t layer_type = net.TIDLLayers[i].layerType;
210 // Special handling for input & output data layers:
211 // Do not put them in the same subgraph
212 if (net.TIDLLayers[i].layerType == TIDL_DataLayer)
213 {
214 auto V = vertex(i, tidl);
215 VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, tidl);
216 vpm[V][LABEL] = TIDL_LayerString[layer_type];
217 vpm[V][COLOR] = GetVertexColor(layer_type);
218 vpm[V][STYLE] = FILLED;
219 vpm[V][SHAPE] = "ellipse";
221 continue;
222 }
224 // Use the layer group ID to create subgraphs
225 // Place all layers with the same ID into a single subgraph
226 uint32_t layerGroupId = net.TIDLLayers[i].layersGroupId;
227 Graph* sub = nullptr;
228 if (layerid_map.find(layerGroupId) == layerid_map.end())
229 {
230 sub = &(tidl.create_subgraph());
231 layerid_map[layerGroupId] = sub;
232 get_property(*sub, graph_name) = std::string("cluster") +
233 std::to_string(layerGroupId);
234 get_property(*sub, graph_graph_attribute)[LABEL] =
235 std::to_string(layerGroupId);
236 get_property(*sub, graph_graph_attribute)[STYLE] = FILLED;
237 get_property(*sub, graph_graph_attribute)["fillcolor"] = "lightgrey";
238 }
239 else
240 sub = layerid_map[layerGroupId];
242 auto V = add_vertex(i, *sub);
243 VertexPropertyMap<Graph> vpm = boost::get(vertex_attribute, *sub);
244 vpm[V][LABEL] = TIDL_LayerString[layer_type];
245 vpm[V][COLOR] = GetVertexColor(layer_type);
246 vpm[V][STYLE] = FILLED;
248 }
250 // Add edges based on dataId i.e. there is an edge from layer A -> B
251 // iff dataId is in outData for A and inData for B.
252 EdgePropertyMap<Graph> ep = boost::get(boost::edge_attribute, tidl);
253 for (int i = 0 ; i < net.numLayers; i++)
254 {
255 if (net.TIDLLayers[i].numOutBufs < 0)
256 continue;
258 // Create a string to annotate the edge - num_channels, height, width
259 int32_t out_id = net.TIDLLayers[i].outData[0].dataId;
260 std::string edge_info =
261 std::to_string(net.TIDLLayers[i].outData[0].dimValues[1])+
262 "x" +
263 std::to_string(net.TIDLLayers[i].outData[0].dimValues[2])+
264 "x" +
265 std::to_string(net.TIDLLayers[i].outData[0].dimValues[3]);
267 for (int j = 0; j < net.numLayers; j++)
268 {
269 if (j == i) continue;
271 for (int x = 0; x < net.TIDLLayers[j].numInBufs; x++)
272 if (net.TIDLLayers[j].inData[x].dataId == out_id)
273 {
274 Edge e = add_edge(i, j, tidl).first;
275 ep[e][LABEL] = edge_info;
276 }
278 }
279 }
281 get_property(tidl, graph_name) = "TIDL Network";
282 get_property(tidl, graph_vertex_attribute)[SHAPE] = "Mrecord";
284 // Generate dot file from boost graph
285 std::ofstream dot(dot_file);
286 boost::write_graphviz(dot, tidl);
288 return true;
289 }
292 const char* GetVertexColor(uint32_t layer_type)
293 {
294 static const char* LayerColors[] =
295 {
296 "lightblue",
297 "lightseagreen",
298 "lightpink",
299 "sienna",
300 "lightyellow",
301 "palegoldenrod",
302 "lightsalmon",
303 "lightcyan",
304 "wheat",
305 "olivedrab",
306 "lightskyblue",
307 "beige",
308 "lavender",
309 "linen"
311 };
313 return LayerColors[layer_type % (sizeof(LayerColors)/sizeof(char *))];
314 }