1<!DOCTYPE html>
2<html>
3<head>
4    <title>$spec_name</title>
5    <style>
6        body {
7            font-family: "Roboto", sans-serif;
8            margin: 0;
9            height: 100%;
10            background-color: rgb(61, 65, 77);
11        }
12
13        #main {
14            width: 62%;
15            transition: 0.5s;
16        }
17
18        #main h1 {
19            padding: 20px;
20            color: #eee;
21            font-size: 24px;
22        }
23
24        .subgraph h3 {
25            text-transform: capitalize;
26        }
27
28        .subgraph {
29            padding: 20px;
30            margin: 20px;
31            border-radius: 10px;
32            background-color: #fff;
33        }
34
35        .subgraph table {
36            border-collapse: collapse;
37            border-spacing: 0;
38        }
39
40        .subgraph thead {
41            background-color: rgb(61, 65, 77);
42            color: white;
43            text-transform: capitalize;
44        }
45
46        .subgraph tbody tr:nth-child(odd) {
47            background-color: #f2f2f2;
48        }
49
50        .subgraph tbody tr:hover {
51            background-color: #d8d8d8;
52        }
53
54        .subgraph td {
55            border: 1px solid #ddd;
56            padding: 8px;
57        }
58
59        .subgraph select {
60            font-weight: bold;
61            text-transform: uppercase;
62            font-size: 18px;
63            color: black;
64        }
65
66        .subgraph svg {
67            background: white;
68            border: 1px solid #ccc;
69        }
70
71        .subgraph .edges line {
72            stroke: #333;
73        }
74
75        .subgraph .nodes text {
76            color: black;
77            pointer-events: none;
78            font-family: sans-serif;
79            font-size: 11px;
80        }
81
82        #sidebar {
83            height: 100%;
84            width: 38%;
85            position: fixed;
86            z-index: 1;
87            top: 0;
88            right: 0;
89            background-color: #eee;
90            overflow-x: hidden;
91            transition: 0.5s;
92            border-left: 1px solid #ccc;
93        }
94
95        #sidebar #sidebar-main {
96            padding: 50px;
97        }
98
99        #sidebar h1 {
100            margin-top: 6px;
101            margin-bottom: 24px;
102            font-weight: bold;
103            font-size: 18px;
104            text-transform: uppercase;
105        }
106
107        #sidebar .subtitle {
108            margin-bottom: 6px;
109            border-bottom: 1px solid #ccc;
110            padding-bottom: 4px;
111            font-weight: bold;
112            font-size: 12px;
113            text-transform: uppercase;
114            color: #555;
115        }
116
117        #sidebar .property {
118            display: block;
119            margin-bottom: 16px;
120        }
121
122        #sidebar .property_title {
123            float: left;
124            width: 80px;
125            margin-top: 0;
126            padding-top: 10px;
127            font-weight: bold;
128            font-size: 12px;
129            text-transform: uppercase;
130            color: #555;
131        }
132
133        #sidebar .property_text {
134            margin-top: 8px;
135            margin-left: 100px;
136            border: 1px solid #ccc;
137            border-radius: 2px;
138            padding: 8px;
139            font-size: 14px;
140            background-color: #fff;
141        }
142
143        #sidebar .closebtn {
144            position: absolute;
145            top: 0;
146            right: 25px;
147            font-size: 36px;
148            margin-left: 50px;
149            text-decoration: none;
150            color: #555;
151        }
152
153        .tooltip {
154            color: blue;
155        }
156
157        .tooltip .tooltipcontent {
158            visibility: hidden;
159            color: black;
160            background-color: #eee;
161            margin-top: 18px;
162            padding: 5px;
163            border: 1px solid #ccc;
164            border-radius: 4px;
165            position: absolute;
166            z-index: 1;
167        }
168
169        .tooltip:hover .tooltipcontent {
170            visibility: visible;
171        }
172    </style>
173    <link href="https://fonts.googleapis.com/css?family=Roboto&display=swap" rel="stylesheet" />
174    <script src="https://d3js.org/d3.v4.min.js"></script>
175    <script>
176        graphs = $graph_dump;
177    </script>
178</head>
179
180<body>
181    <div id="main">
182        <h1>$spec_name</h1>
183        <div class="subgraph" id="main-subgraph">
184            <label for="main-selector">Choose a subgraph: </label>
185            <select id="main-selector" onchange="renderSubgraph(this.value)"></select>
186            <div id="main-tables"></div>
187            <h3>Visual Graph</h3>
188            <svg id="subgraph-svg" width="100%" height="720"></svg>
189        </div>
190    </div>
191
192    <div id="sidebar">
193        <div id="sidebar-main">
194        </div>
195    </div>
196
197    <script>
198        // Render the sidebar view of a given node object.
199        // The node must have "name" and "group" fields available.
200        function renderSidebar(node) {
201            var sidebar = document.getElementById("sidebar-main");
202            sidebar.innerHTML = "";
203            if (node == null) return;
204
205            // Sidebar subtitle -- text taken from node.group.
206            var subtitle = document.createElement("p");
207            subtitle.classList.add("subtitle");
208            subtitle.innerHTML = node.group;
209            sidebar.appendChild(subtitle);
210
211            // Sidebar title -- text taken from node.name.
212            var title = document.createElement("h1");
213            title.innerHTML = node.name;
214            sidebar.appendChild(title);
215
216            // List all the other fields in sidebar.
217            var ignoredFields = ["name", "group"];
218            for (var property in node) {
219                if (ignoredFields.includes(property)) continue;
220
221                var propertyTitle = document.createElement("h2");
222                propertyTitle.classList.add("property_title");
223                propertyTitle.innerHTML = property;
224
225                var propertyText = document.createElement("p");
226                propertyText.classList.add("property_text");
227                propertyText.innerHTML = node[property];
228
229                var propertyDiv = document.createElement("div");
230                propertyDiv.classList.add("property");
231                propertyDiv.appendChild(propertyTitle);
232                propertyDiv.appendChild(propertyText);
233                sidebar.appendChild(propertyDiv);
234            }
235        }
236
237        // Render the SVG DAG visualization, from TFLite graph visualizer.
238        // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/tools/visualize.py
239        //
240        // The node coordiates are pre-calculated from the python visualizer.
241        function renderSvg(subgraph) {
242            var data = graphs[subgraph]["subgraph"];
243            var svg = d3.select("#subgraph-svg");
244            svg.selectAll("*").remove();
245            var width = svg.attr("width");
246            var height = svg.attr("height");
247            // Make the graph scrollable.
248            svg = svg.call(d3.zoom().on("zoom", function () {
249                svg.attr("transform", d3.event.transform);
250            })).append("g");
251            var color = d3.scaleOrdinal(d3.schemeDark2);
252            var simulation = d3.forceSimulation()
253                .force("link", d3.forceLink().id(function (d) { return d.id; }))
254                .force("charge", d3.forceManyBody())
255                .force("center", d3.forceCenter(0.5 * width, 0.5 * height));
256            var edge = svg.append("g").attr("class", "edges").selectAll("line")
257                .data(data.edges).enter().append("path").attr("stroke", "black").attr("fill", "none")
258            // Make the node group
259            var node = svg.selectAll(".nodes")
260                .data(data.nodes)
261                .enter().append("g")
262                .attr("x", function (d) { return d.x })
263                .attr("y", function (d) { return d.y })
264                .attr("transform", function (d) {
265                    return "translate( " + d.x + ", " + d.y + ")"
266                })
267                .attr("class", "nodes")
268                .call(d3.drag()
269                    .on("start", function (d) {
270                        if (!d3.event.active) simulation.alphaTarget(1.0).restart();
271                        d.fx = d.x; d.fy = d.y;
272                    })
273                    .on("drag", function (d) {
274                        d.fx = d3.event.x; d.fy = d3.event.y;
275                    })
276                    .on("end", function (d) {
277                        if (!d3.event.active) simulation.alphaTarget(0);
278                        d.fx = d.fy = null;
279                    }));
280            // Within the group, draw a box for the node position and text
281            // on the side.
282            var node_width = 150;
283            var node_height = 30;
284            node.append("rect")
285                .attr("r", "5px")
286                .attr("width", function (d) { return d.group == 1 ? node_width : node_width + 50; })
287                .attr("height", node_height)
288                .attr("rx", function (d) { return d.group == 1 ? 1 : 10; })
289                .attr("stroke", "#000000")
290                .attr("fill", function (d) { return d.group == 1 ? "#dddddd" : "#000000"; })
291                .attr("onclick", function (d) {
292                    return "renderSidebar(graphs." + subgraph + ".details." +
293                        (d.group == 1 ? "operands" : "operations") + "[" +
294                        d.index.toString() + "])";
295                });
296            node.append("text")
297                .text(function (d) { return d.name; })
298                .attr("x", 5)
299                .attr("y", 20)
300                .attr("fill", function (d) { return d.group == 1 ? "#000000" : "#eeeeee"; })
301            // Setup force parameters and update position callback
302            var node = svg.selectAll(".nodes")
303                .data(data.nodes);
304            // Bind the links
305            var name_to_g = {}
306            node.each(function (data, index, nodes) {
307                name_to_g[data.id] = this;
308            });
309            function proc(w, t) {
310                return parseInt(w.getAttribute(t));
311            }
312            edge.attr("d", function (d) {
313                function lerp(t, a, b) {
314                    return (1.0 - t) * a + t * b;
315                }
316                var x1 = proc(name_to_g[d.source], "x") + node_width / 2;
317                var y1 = proc(name_to_g[d.source], "y") + node_height;
318                var x2 = proc(name_to_g[d.target], "x") + node_width / 2;
319                var y2 = proc(name_to_g[d.target], "y");
320                var s = "M " + x1 + " " + y1
321                    + " C " + x1 + " " + lerp(.5, y1, y2)
322                    + " " + x2 + " " + lerp(.5, y1, y2)
323                    + " " + x2 + " " + y2
324                return s;
325            });
326        }
327
328        // Open a new window and present the full text data.
329        function showFullData(data) {
330            window.open().document.write(data);
331        }
332
333        // Renders a single table.
334        function renderTable(title, data, headers) {
335            var parent = document.getElementById("main-tables");
336
337            // Create heading.
338            var heading = document.createElement("h3");
339            heading.innerHTML = title;
340            parent.appendChild(heading);
341
342            // Filter out headers that do not appear in any data element.
343            headers = headers.filter(function (key) {
344                return data.some(function (elem) { return key in elem; });
345            });
346
347            // Render the table headers.
348            var table = document.createElement("table");
349            let header = table.createTHead().insertRow();
350            for (let key of headers) { header.insertCell().innerHTML = key; }
351
352            // Render the table body.
353            // Since the "data" field could be very large, we omit the full content and
354            // append a "View Full" button to the end.
355            var omittableFields = ["data"];
356            let body = table.createTBody();
357            for (const [index, elem] of data.entries()) {
358                let row = body.insertRow();
359                row.id = "details-" + title.toLowerCase() + "-" + index.toString();
360
361                for (let key of headers) {
362                    var cell = row.insertCell();
363                    var data = key in elem ? elem[key] : "-";
364                    if (omittableFields.includes(key) && data.length > 100) {
365                        // If the data exceeds the length limit, only print the first 80 and
366                        // the last 20 characters.
367                        data = data.substring(0, 80) + " ... " +
368                            data.substring(data.length - 20, data.length) + " ";
369                        cell.innerHTML = data;
370
371                        // Append a "View Full" button to the end.
372                        var href = document.createElement("a");
373                        href.innerHTML = "View Full";
374                        href.href = "javascript:void(0)";
375                        href.onclick = function () { showFullData(elem[key]); };
376                        cell.appendChild(href);
377                    } else {
378                        cell.innerHTML = data;
379                    }
380                }
381            }
382            parent.appendChild(table);
383        }
384
385        function renderTables(subgraph) {
386            document.getElementById("main-tables").innerHTML = "";
387            renderTable("Configurations", graphs[subgraph].details.configurations, [
388                "relaxed",
389                "use shared memory",
390                "expect failure"
391            ]);
392            renderTable("Operands", graphs[subgraph].details.operands, [
393                "index",
394                "name",
395                "type",
396                "dimensions",
397                "scale",
398                "zero point",
399                "channel dim",
400                "lifetime",
401                "data"
402            ]);
403            renderTable("Operations", graphs[subgraph].details.operations, [
404                "index",
405                "opcode",
406                "inputs",
407                "outputs"
408            ]);
409        }
410
411        // Re-render all the information related to a subgraph.
412        // Invoked everytime when the main-selector changes.
413        function renderSubgraph(subgraph) {
414            renderTables(subgraph);
415            renderSvg(subgraph);
416            renderSidebar(null);  // Clear sidebar.
417        }
418
419        // Renders the main-selector and the first subgraph choice in the main-selector.
420        // Only invoked once when the page gets loaded the first time.
421        function renderMain() {
422            var selector = document.getElementById("main-selector");
423            var first = true;
424            for (var subgraph in graphs) {
425                var option = document.createElement("option");
426                option.value = subgraph;
427                option.text = subgraph;
428                selector.appendChild(option);
429                if (first) {
430                    first = false;
431                    renderSubgraph(subgraph);
432                }
433            }
434        }
435        renderMain();
436    </script>
437</body>
438</html>
439