Utility to dump TIDL networks to dot graphs
[tidl/tidl-api.git] / utils / src / tinn_utils.cpp
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;
122 bool tinn::util::GenerateDotGraphForNetwork(const std::string& network_binary,
123                                             const std::string& dot_file)
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);
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)
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;
292 const char* GetVertexColor(uint32_t layer_type)
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 *))];