diff options
author | Dan Mané <danmane@gmail.com> | 2016-03-11 14:57:33 -0800 |
---|---|---|
committer | TensorFlower Gardener <gardener@tensorflow.org> | 2016-03-11 20:45:08 -0800 |
commit | 65c9124086240616747e26e0aa5ef3412a3be55d (patch) | |
tree | 96efafa6cf7cee517ccdc2f4e02ba94823e48106 | |
parent | dbc57b9068da9b823dbcc3101e599da6f65f658a (diff) |
Release a new TensorBoard version
Change: 117011137
-rw-r--r-- | WORKSPACE | 10 | ||||
-rw-r--r-- | tensorflow/tensorboard/TAG | 2 | ||||
-rw-r--r-- | tensorflow/tensorboard/dist/tf-tensorboard.html | 1158 |
3 files changed, 786 insertions, 384 deletions
@@ -56,7 +56,7 @@ new_git_repository( new_git_repository( name = "iron_a11y_keys_behavior", build_file = "bower.BUILD", - remote = "https://github.com/PolymerElements/iron-a11y-keys-behavior.git", + remote = "https://github.com/polymerelements/iron-a11y-keys-behavior.git", tag = "v1.1.1", ) @@ -77,7 +77,7 @@ new_git_repository( new_git_repository( name = "iron_behaviors", build_file = "bower.BUILD", - remote = "https://github.com/polymerelements/iron-behaviors.git", + remote = "https://github.com/PolymerElements/iron-behaviors.git", tag = "v1.0.13", ) @@ -168,7 +168,7 @@ new_git_repository( new_git_repository( name = "iron_meta", build_file = "bower.BUILD", - remote = "https://github.com/PolymerElements/iron-meta.git", + remote = "https://github.com/polymerelements/iron-meta.git", tag = "v1.1.1", ) @@ -322,7 +322,7 @@ new_git_repository( new_git_repository( name = "paper_ripple", build_file = "bower.BUILD", - remote = "https://github.com/polymerelements/paper-ripple.git", + remote = "https://github.com/PolymerElements/paper-ripple.git", tag = "v1.0.5", ) @@ -371,7 +371,7 @@ new_git_repository( new_git_repository( name = "polymer", build_file = "bower.BUILD", - remote = "https://github.com/polymer/polymer.git", + remote = "https://github.com/Polymer/polymer.git", tag = "v1.3.1", ) diff --git a/tensorflow/tensorboard/TAG b/tensorflow/tensorboard/TAG index b1bd38b62a..8351c19397 100644 --- a/tensorflow/tensorboard/TAG +++ b/tensorflow/tensorboard/TAG @@ -1 +1 @@ -13 +14 diff --git a/tensorflow/tensorboard/dist/tf-tensorboard.html b/tensorflow/tensorboard/dist/tf-tensorboard.html index 3fcd43c7d5..492a5d45ce 100644 --- a/tensorflow/tensorboard/dist/tf-tensorboard.html +++ b/tensorflow/tensorboard/dist/tf-tensorboard.html @@ -16,8 +16,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../../typings/tsd.d.ts" /> -/// <reference path="../plottable/plottable.d.ts" /> var TF; (function (TF) { /* The DataCoordinator generates TF.Datasets for each run/tag combination, @@ -65,11 +63,16 @@ var TF; return dataset; }; return DataCoordinator; - })(); + }()); TF.DataCoordinator = DataCoordinator; })(TF || (TF = {})); </script> - <script>/* Copyright 2015 Google Inc. All Rights Reserved. + <script>var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +/* Copyright 2015 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -83,13 +86,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -}; -/// <reference path="../../typings/tsd.d.ts" /> -/// <reference path="../plottable/plottable.d.ts" /> var TF; (function (TF) { /* An extension of Plottable.Dataset that knows how to load data from a backend. @@ -123,7 +119,7 @@ var TF; }); }; return Dataset; - })(Plottable.Dataset); + }(Plottable.Dataset)); TF.Dataset = Dataset; })(TF || (TF = {})); </script> @@ -770,8 +766,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../../typings/tsd.d.ts" /> -/// <reference path="../plottable/plottable.d.ts" /> var TF; (function (TF) { var Urls; @@ -791,8 +785,18 @@ var TF; function individualImageUrl(query) { return "/data/individualImage?" + query; } - function graphUrl(run) { - return "/data/graph?run=" + encodeURIComponent(run); + function graphUrl(run, limit_attr_size, large_attrs_key) { + var query_params = [["run", run]]; + if (limit_attr_size != null) { + query_params.push(["limit_attr_size", String(limit_attr_size)]); + } + if (large_attrs_key != null) { + query_params.push(["large_attrs_key", large_attrs_key]); + } + var query = query_params.map(function (param) { + return param[0] + "=" + encodeURIComponent(param[1]); + }).join("&"); + return "/data/graph?" + query; } return { runs: function () { return "/data/runs"; }, @@ -806,7 +810,21 @@ var TF; } Urls.productionRouter = productionRouter; ; - function demoRouter(dataDir) { + function demoRouter(dataDir, oldVersion) { + if (oldVersion === void 0) { oldVersion = false; } + if (oldVersion) { + return { + runs: function () { return dataDir + "runs.json"; }, + graph: function (run) { return dataDir + run + "-graph.pbtxt"; }, + scalars: function (tag, run) { + return dataDir + run.split("_")[0] + ".json"; + }, + histograms: function () { return null; }, + compressedHistograms: function () { return null; }, + images: function () { return null; }, + individualImage: function () { return null; } + }; + } /* Retrieves static .json data generated by demo_from_server.py */ function demoRoute(route) { return function (tag, run) { @@ -858,7 +876,7 @@ var TF; }, }; TF.Urls.routes.forEach(function(route) { - /* for each route (other than runs, handled seperately): + /* for each route (other than runs, handled separately): * out`RouteName`: { * type: Function, * readOnly: true, @@ -1254,7 +1272,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../../typings/tsd.d.ts" /> var Categorizer; (function (Categorizer) { /* Canonical TensorFlow ops are namespaced using forward slashes. @@ -1568,11 +1585,16 @@ var Plottable; draw(); }; return DragZoomLayer; - })(Plottable.Components.SelectionBoxLayer); + }(Plottable.Components.SelectionBoxLayer)); Plottable.DragZoomLayer = DragZoomLayer; })(Plottable || (Plottable = {})); </script> - <script>/* Copyright 2015 Google Inc. All Rights Reserved. + <script>var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +/* Copyright 2015 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -1586,13 +1608,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -}; -/// <reference path="../../typings/tsd.d.ts" /> -/// <reference path="../plottable/plottable.d.ts" /> var TF; (function (TF) { var Y_TOOLTIP_FORMATTER_PRECISION = 4; @@ -1710,7 +1725,7 @@ var TF; this.outer.destroy(); }; return BaseChart; - })(); + }()); TF.BaseChart = BaseChart; var LineChart = (function (_super) { __extends(LineChart, _super); @@ -1732,7 +1747,7 @@ var TF; this.plot.datasets(datasets); }; return LineChart; - })(BaseChart); + }(BaseChart)); TF.LineChart = LineChart; var HistogramChart = (function (_super) { __extends(HistogramChart, _super); @@ -1773,7 +1788,7 @@ var TF; return new Plottable.Components.Group([new Plottable.Components.Group(plots), group]); }; return HistogramChart; - })(BaseChart); + }(BaseChart)); TF.HistogramChart = HistogramChart; /* Create a formatter function that will switch between exponential and * regular display depending on the scale of the number being formatted, @@ -2333,19 +2348,19 @@ var TF; <div id="fullContainer" class="container scrollbar"> <div id="topRow" class="container"> <div class="noshrink" id="paddingCell"></div> - <template is="dom-repeat" items="[[_runs]]" sort="_sort" as="run"> + <template is="dom-repeat" items="[[_runs]]" as="run"> <div class="run-name-cell noshrink"> <span>[[run]]</span> </div> </template> </div> <div id="bottomContainer" class="container"> - <template is="dom-repeat" items="[[_tags]]" sort="_sort" as="tag"> + <template is="dom-repeat" items="[[_tags]]" as="tag"> <div class="image-row container noshrink"> <div class="tag-name-cell noshrink"> <span class="tag-name">[[tag]]</span> </div> - <template is="dom-repeat" items="[[_runs]]" sort="_sort" as="run"> + <template is="dom-repeat" items="[[_runs]]" as="run"> <div class="image-cell noshrink"> <template is="dom-if" if="[[_exists(run, tag, runToImages.*)]]"> <tf-image-loader id="loader" run="[[run]]" tag="[[tag]]" images-generator="[[imagesGenerator]]" individual-image-generator="[[individualImageGenerator]]"> @@ -2429,18 +2444,16 @@ var TF; individualImageGenerator: Function, }, _getTags: function(runToImages) { - return _.chain(runToImages.base).values().flatten().union().value(); + var r2i = runToImages.base; + return _.chain(r2i).values().flatten().union().value().sort(); }, _getRuns: function(runToImages) { var r2i = runToImages.base; - return _.keys(r2i).filter(function(x) {return r2i[x].length > 0;}); + return _.keys(r2i).filter((x) => r2i[x].length > 0).sort(); }, _exists: function (run, tag, runToImages) { - runToImages = runToImages.base; - return runToImages[run].indexOf(tag) !== -1; - }, - _sort: function(a, b) { - return a > b; + var r2i = runToImages.base; + return r2i[run].indexOf(tag) !== -1; }, }); </script> @@ -2527,7 +2540,6 @@ Polymer({ progress: { type: Object, notify: true, - readOnly: true // Produces, does not consume. }, datasets: Array, hasStats: { @@ -2554,39 +2566,35 @@ Polymer({ type: String, readOnly: true, notify: true - } + }, + outHierarchyParams: { + type: Object, + readOnly: true, + notify: true + }, }, observers: [ '_selectedDatasetChanged(selectedDataset, datasets)' ], _parseAndConstructHierarchicalGraph: function(dataset, pbTxtContent) { - var self = this; // Reset the progress bar to 0. - self._setProgress({ + this.set('progress', { value: 0, msg: '' }); - var tracker = { - setMessage: function(msg) { - self._setProgress({ - value: self.progress.value, - msg: msg - }); - }, - updateProgress: function(value) { - self._setProgress({ - value: self.progress.value + value, - msg: self.progress.msg - }); - }, - reportError: function(msg) { - self._setProgress({ - value: self.progress.value, - msg: msg, - error: true - }); - }, + var tracker = tf.getTracker(this); + var hierarchyParams = { + verifyTemplate: true, + // If a set of numbered op nodes has at least this number of nodes + // then group them into a series node. + seriesNodeMinSize: 5, + // A map of series node names to series grouping settings, to indicate + // if a series is to be rendered as grouped or ungrouped. + // Starts out empty which allows the renderer to decide which series + // are initially rendered grouped and which aren't. + seriesMap: {}, }; + this._setOutHierarchyParams(hierarchyParams); var statsJson; var dataTracker = tf.getSubtaskTracker(tracker, 30, 'Data'); tf.graph.parser.readAndParseData(dataset, pbTxtContent, dataTracker) @@ -2630,12 +2638,6 @@ Polymer({ tf.graph.joinStatsInfoWithGraph(graph, statsJson); }); } - var hierarchyParams = { - verifyTemplate: true, - // If a set of numbered op nodes has at least this number of nodes - // then group them into a series node. - seriesNodeMinSize: 5, - }; var hierarchyTracker = tf.getSubtaskTracker(tracker, 50, 'Namespace hierarchy'); return tf.graph.hierarchy.build(graph, hierarchyParams, hierarchyTracker); @@ -2646,8 +2648,10 @@ Polymer({ this._setHasStats(statsJson != null); this._setOutGraphHierarchy(graphHierarchy); }.bind(this)) - .catch(function(reason) { - tracker.reportError("Graph visualization failed: " + reason); + .catch(function(e) { + // Generic error catch, for errors that happened outside + // asynchronous tasks. + tracker.reportError("Graph visualization failed: " + e, e); }); }, _selectedDatasetChanged: function(datasetIndex, datasets) { @@ -2692,7 +2696,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../../../typings/tsd.d.ts" /> var tf; (function (tf) { /** @@ -2710,6 +2713,40 @@ var tf; } tf.time = time; /** + * Creates a tracker that sets the progress property of the + * provided polymer component. The provided component must have + * a property called 'progress' that is not read-only. The progress + * property is an object with a numerical 'value' property and a + * string 'msg' property. + */ + function getTracker(polymerComponent) { + return { + setMessage: function (msg) { + polymerComponent.set("progress", { + value: polymerComponent.progress.value, + msg: msg + }); + }, + updateProgress: function (value) { + polymerComponent.set("progress", { + value: polymerComponent.progress.value + value, + msg: polymerComponent.progress.msg + }); + }, + reportError: function (msg, err) { + // Log the stack trace in the console. + console.error(err.stack); + // And send a user-friendly message to the UI. + polymerComponent.set("progress", { + value: polymerComponent.progress.value, + msg: msg, + error: true + }); + }, + }; + } + tf.getTracker = getTracker; + /** * Creates a tracker for a subtask given the parent tracker, the total progress * of the subtask and the subtask message. The parent task should pass a * subtracker to its subtasks. The subtask reports its own progress which @@ -2720,7 +2757,7 @@ var tf; setMessage: function (progressMsg) { // The parent should show a concatenation of its message along with // its subtask tracker message. - parentTracker.setMessage(subtaskMsg + " : " + progressMsg); + parentTracker.setMessage(subtaskMsg + ": " + progressMsg); }, updateProgress: function (incrementValue) { // Update the parent progress relative to the child progress. @@ -2729,10 +2766,10 @@ var tf; parentTracker .updateProgress(incrementValue * impactOnTotalProgress / 100); }, - reportError: function (errorMsg) { + reportError: function (msg, err) { // The parent should show a concatenation of its message along with // its subtask error message. - parentTracker.reportError(subtaskMsg + " : " + errorMsg); + parentTracker.reportError(subtaskMsg + ": " + msg, err); } }; } @@ -2755,7 +2792,9 @@ var tf; resolve(result); } catch (e) { - reject(result); + // Errors that happen inside asynchronous tasks are + // reported to the tracker using a user-friendly message. + tracker.reportError("Failed " + msg, e); } }, ASYNC_TASK_DELAY); }); @@ -2785,8 +2824,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../../../typings/tsd.d.ts" /> -/// <reference path="common.ts" /> var tf; (function (tf) { var graph; @@ -2794,6 +2831,13 @@ var tf; /** Delimiter used in node names to denote namespaces. */ graph_1.NAMESPACE_DELIM = "/"; graph_1.ROOT_NAME = "__root__"; + /** Attribute key used for storing attributes that are too large. */ + graph_1.LARGE_ATTRS_KEY = "_too_large_attrs"; + /** + * Maximum allowed size in bytes, before the attribute is considered large + * and filtered out of the graph. + */ + graph_1.LIMIT_ATTR_SIZE = 1024; // Separator between the source and the destination name of the edge. graph_1.EDGE_KEY_DELIM = "--"; (function (GraphType) { @@ -2825,6 +2869,15 @@ var tf; })(graph_1.InclusionType || (graph_1.InclusionType = {})); var InclusionType = graph_1.InclusionType; ; + /** Indicates if a series is to be grouped in the graph when rendered. */ + (function (SeriesGroupingType) { + SeriesGroupingType[SeriesGroupingType["GROUP"] = 0] = "GROUP"; + SeriesGroupingType[SeriesGroupingType["UNGROUP"] = 1] = "UNGROUP"; + })(graph_1.SeriesGroupingType || (graph_1.SeriesGroupingType = {})); + var SeriesGroupingType = graph_1.SeriesGroupingType; + ; + /** Attribute key reserved for the shapes of the output tensors. */ + var OUTPUT_SHAPES_KEY = "_output_shapes"; /** * A SlimGraph is inspired by graphlib.Graph, but having only the functionality * that we need. @@ -2835,7 +2888,7 @@ var tf; this.edges = []; } return SlimGraph; - })(); + }()); graph_1.SlimGraph = SlimGraph; var EllipsisNodeImpl = (function () { /** @@ -2857,7 +2910,7 @@ var tf; this.name = "... " + numNodes + " more"; }; return EllipsisNodeImpl; - })(); + }()); graph_1.EllipsisNodeImpl = EllipsisNodeImpl; ; /** @@ -2869,17 +2922,18 @@ var tf; * Constructs a new Op node. * * @param rawNode The raw node. - * @param normalizedInputs An array of normalized - * inputs that denote the incoming edges to the current node. Each input - * contains the normalized name of the source node, whether it has a - * number part and whether it is a control dependency. */ - function OpNodeImpl(rawNode, normalizedInputs) { + function OpNodeImpl(rawNode) { this.op = rawNode.op; this.name = rawNode.name; this.device = rawNode.device; this.attr = rawNode.attr; - this.inputs = normalizedInputs; + // An array of normalized inputs that denote the incoming edges to + // the current node. Each input contains the normalized name of the + // source node, whether it has a number part and whether it is a + // control dependency. + this.inputs = normalizeInputs(rawNode.input); + this.outputShapes = extractOutputShapes(rawNode.attr); // additional properties this.type = NodeType.OP; this.isGroupNode = false; @@ -2888,9 +2942,10 @@ var tf; this.outEmbeddings = []; this.parentNode = null; this.include = InclusionType.UNSPECIFIED; + this.owningSeries = null; } return OpNodeImpl; - })(); + }()); ; function createMetanode(name, opt) { if (opt === void 0) { opt = {}; } @@ -2912,11 +2967,11 @@ var tf; nodeStats.nodeName + graph_1.NAMESPACE_DELIM + "(" + nodeStats.nodeName + ")"; if (nodeName in graph.nodes) { // Compute the total bytes used. - var totalBytes = 0; + var totalBytes_1 = 0; if (nodeStats.memory) { _.each(nodeStats.memory, function (alloc) { if (alloc.totalBytes) { - totalBytes += Number(alloc.totalBytes); + totalBytes_1 += Number(alloc.totalBytes); } }); } @@ -2926,7 +2981,7 @@ var tf; return _.map(output.tensorDescription.shape.dim, function (dim) { return Number(dim.size); }); }); } - graph.nodes[nodeName].stats = new NodeStats(totalBytes, Number(nodeStats.allEndRelMicros), outputSize); + graph.nodes[nodeName].stats = new NodeStats(totalBytes_1, Number(nodeStats.allEndRelMicros), outputSize); } }); }); @@ -2955,7 +3010,7 @@ var tf; } }; return NodeStats; - })(); + }()); graph_1.NodeStats = NodeStats; var MetanodeImpl = (function () { /** A label object for meta-nodes in the graph hierarchy */ @@ -3025,7 +3080,7 @@ var tf; return leaves; }; return MetanodeImpl; - })(); + }()); ; function createMetaedge(v, w) { return new MetaedgeImpl(v, w); @@ -3043,8 +3098,9 @@ var tf; this.numRegularEdges = 0; this.numControlEdges = 0; this.numRefEdges = 0; + this.totalSize = 0; } - MetaedgeImpl.prototype.addBaseEdge = function (edge) { + MetaedgeImpl.prototype.addBaseEdge = function (edge, h) { this.baseEdgeList.push(edge); if (edge.isControlDependency) { this.numControlEdges += 1; @@ -3055,9 +3111,39 @@ var tf; if (edge.isReferenceEdge) { this.numRefEdges += 1; } + // Compute the size of the tensor flowing through this + // base edge. + this.totalSize += MetaedgeImpl.computeSizeOfEdge(edge, h); + }; + MetaedgeImpl.computeSizeOfEdge = function (edge, h) { + var opNode = h.node(edge.v); + if (opNode.outputShapes == null) { + // No shape information. Asssume a single number. This gives + // a lower bound for the total size. + return 1; + } + // Sum the sizes of all output tensors. + return _(opNode.outputShapes).map(function (shape) { + // If the shape is unknown, treat it as 1 when computing + // total size. This gives a lower bound for the total size. + if (shape == null) { + return 1; + } + // Multiply all shapes to get the total size of the tensor. + // E.g. The total size of [4, 2, 1] is 4 * 2 * 1. + return _(shape).reduce(function (accumulated, currSize) { + // If this particular dimension is unknown, treat + // it as 1 when computing total size. This gives a lower bound + // for the total size. + if (currSize === -1) { + currSize = 1; + } + return accumulated * currSize; + }, 1); + }).sum(); }; return MetaedgeImpl; - })(); + }()); function createSeriesNode(prefix, suffix, parent, clusterId, name) { return new SeriesNodeImpl(prefix, suffix, parent, clusterId, name); } @@ -3091,7 +3177,49 @@ var tf; this.include = InclusionType.UNSPECIFIED; } return SeriesNodeImpl; - })(); + }()); + /** + * Extracts the shapes of the output tensors from the attr property in the + * node proto. + */ + function extractOutputShapes(attr) { + var result = null; + // We don't know anything about the output tensors. + if (!attr) { + return null; + } + for (var i = 0; i < attr.length; i++) { + var _a = attr[i], key = _a.key, value = _a.value; + if (key === OUTPUT_SHAPES_KEY) { + // Map all output tensors into array of numbers denoting their shape. + var result_1 = value.list.shape.map(function (shape) { + if (shape.unknown_rank) { + // This output tensor is of unknown rank. We don't know if it is a + // scalar, or a tensor, or of what shape it is. + return null; + } + if (shape.dim == null || + (shape.dim.length === 1 && shape.dim[0].size == null)) { + // This output tensor is a scalar. + return []; + } + // This output tensor has a known rank. Map each dimension size + // into a number. + return shape.dim.map(function (dim) { + // Size can be -1 if this particular dimension is unknown. + return dim.size; + }); + }); + // Since we already processed it, remove the entry from the attribute + // list (saves memory). + attr.splice(i, 1); + return result_1; + } + } + // We didn't find OUTPUT_SHAPES_KEY in attributes, so we don't know anything + // about the output tensors. + return result; + } /** * Normalizes the inputs and extracts associated metadata: * 1) Inputs can contain a colon followed by a number at the end @@ -3169,8 +3297,7 @@ var tf; var opNodes = new Array(rawNodes.length); var index = 0; _.each(rawNodes, function (rawNode) { - var normalizedInputs = normalizeInputs(rawNode.input); - var opNode = new OpNodeImpl(rawNode, normalizedInputs); + var opNode = new OpNodeImpl(rawNode); if (isInEmbeddedPred(opNode)) { embeddingNodeNames.push(opNode.name); inEmbedding[opNode.name] = opNode; @@ -3252,9 +3379,6 @@ var tf; }); return graph; }, tracker); - }) - .catch(function (reason) { - throw new Error("Failure creating graph"); }); } graph_1.build = build; @@ -3410,6 +3534,34 @@ var tf; } graph_1.getIncludeNodeButtonString = getIncludeNodeButtonString; ; + /** + * Returns the string for the series node grouping toggle button, dependant + * on the provided current SeriesGroupingType. + */ + function getGroupSeriesNodeButtonString(group) { + if (group === tf.graph.SeriesGroupingType.GROUP) { + return "Ungroup this series of nodes"; + } + else { + return "Group this series of nodes"; + } + } + graph_1.getGroupSeriesNodeButtonString = getGroupSeriesNodeButtonString; + ; + /** + * Toggle the node series grouping option in the provided map, setting it + * to ungroup if the series is not already in the map. + */ + function toggleNodeSeriesGroup(map, name) { + if (!(name in map) || map[name] === tf.graph.SeriesGroupingType.GROUP) { + map[name] = tf.graph.SeriesGroupingType.UNGROUP; + } + else { + map[name] = tf.graph.SeriesGroupingType.GROUP; + } + } + graph_1.toggleNodeSeriesGroup = toggleNodeSeriesGroup; + ; })(graph = tf.graph || (tf.graph = {})); })(tf || (tf = {})); // close module tf.graph </script> @@ -3427,8 +3579,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../../../typings/tsd.d.ts" /> -/// <reference path="common.ts" /> var tf; (function (tf) { var graph; @@ -3512,9 +3662,6 @@ var tf; nodes: nodes, statsJson: statsJson }; - }) - .catch(function (reason) { - throw new Error("Failure parsing graph definition"); }); } parser.readAndParseData = readAndParseData; @@ -3552,7 +3699,10 @@ var tf; "node.attr.value.list.type": true, "node.attr.value.shape.dim": true, "node.attr.value.tensor.string_val": true, - "node.attr.value.tensor.tensor_shape.dim": true + "node.attr.value.tensor.tensor_shape.dim": true, + "node.attr.value.list.shape": true, + "node.attr.value.list.shape.dim": true, + "node.attr.value.list.s": true }; /** * Adds a value, given the attribute name and the host object. If the @@ -3630,8 +3780,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="graph.ts" /> -/// <reference path="template.ts" /> /** * Package for the Graph Hierarchy for TensorFlow graph. */ @@ -3727,7 +3875,7 @@ var tf; } // Copy the BaseEdge from the parent's Metaedge into this // bridgegraph Metaedge. - bridgeMetaedge.addBaseEdge(baseEdge); + bridgeMetaedge.addBaseEdge(baseEdge, _this); }); }) .value(); // force lodash chain execution. @@ -3819,16 +3967,14 @@ var tf; HierarchyImpl.prototype.getOneWayEdges = function (node, inEdges) { var edges = { control: [], regular: [] }; // A node with no parent cannot have any edges. - if (!node.parentNode) { + if (!node.parentNode || !node.parentNode.isGroupNode) { return edges; } - if (node.parentNode.isGroupNode) { - var parentNode = node.parentNode; - var metagraph = parentNode.metagraph; - var bridgegraph = this.getBridgegraph(parentNode.name); - findEdgeTargetsInGraph(metagraph, node, inEdges, edges); - findEdgeTargetsInGraph(bridgegraph, node, inEdges, edges); - } + var parentNode = node.parentNode; + var metagraph = parentNode.metagraph; + var bridgegraph = this.getBridgegraph(parentNode.name); + findEdgeTargetsInGraph(metagraph, node, inEdges, edges); + findEdgeTargetsInGraph(bridgegraph, node, inEdges, edges); return edges; }; /** @@ -3909,7 +4055,7 @@ var tf; return function (templateId) { return templateIndex(templateId); }; }; return HierarchyImpl; - })(); + }()); /** * Internal utility function - given a graph (should be either a metagraph or a * bridgegraph) and a node which is known to be in that graph, determine @@ -3923,21 +4069,23 @@ var tf; * Discovered target names are appended to the targets array. */ function findEdgeTargetsInGraph(graph, node, inbound, targets) { - _.each(graph.edges(), function (e) { - var _a = inbound ? [e.w, e.v] : [e.v, e.w], selfName = _a[0], otherName = _a[1]; - if (selfName === node.name) { - if (node.isGroupNode) { - var targetList = graph.edge(e).numRegularEdges - ? targets.regular : targets.control; - targetList.push(otherName); - } - else { - _.each(graph.edge(e).baseEdgeList, function (baseEdge) { - var targetList = baseEdge.isControlDependency - ? targets.control : targets.regular; - targetList.push(inbound ? baseEdge.v : baseEdge.w); - }); - } + var edges = inbound ? graph.inEdges(node.name) : graph.outEdges(node.name); + _.each(edges, function (e) { + var otherName = inbound ? e.v : e.w; + var metaedge = graph.edge(e); + if (node.isGroupNode && metaedge.baseEdgeList.length > 1) { + var targetList = metaedge.numRegularEdges + ? targets.regular : targets.control; + targetList.push(otherName); + } + else { + // Enumerate all the base edges if the node is an OpNode, or the + // metaedge has only 1 edge in it. + _.each(metaedge.baseEdgeList, function (baseEdge) { + var targetList = baseEdge.isControlDependency + ? targets.control : targets.regular; + targetList.push(inbound ? baseEdge.v : baseEdge.w); + }); } }); } @@ -3962,7 +4110,7 @@ var tf; .then(function () { return tf.runAsyncTask("Detect series", 20, function () { if (params.seriesNodeMinSize > 0) { - groupSeries(h.root, h, seriesNames, params.seriesNodeMinSize); + groupSeries(h.root, h, seriesNames, params.seriesNodeMinSize, params.seriesMap); } }, tracker); }) @@ -3978,8 +4126,6 @@ var tf; }) .then(function () { return h; - }).catch(function (reason) { - throw new Error("Failure creating graph hierarchy"); }); } hierarchy_1.build = build; @@ -4091,7 +4237,7 @@ var tf; !baseEdge.isControlDependency) { sharedAncestorNode.hasNonControlEdges = true; } - metaedge.addBaseEdge(baseEdge); + metaedge.addBaseEdge(baseEdge, h); }); } ; @@ -4103,17 +4249,21 @@ var tf; * * @param metanode * @param hierarchy + * @param seriesNames Map of node names to their series they are contained in. + * This should be provided empty and is populated by this method. * @param threshold If the series has this many nodes or more, then group them * into a series. + * @param map Map of series names to their series grouping type, if one has + * been set. * @return A dictionary from node name to series node name that contains the * node. */ - function groupSeries(metanode, hierarchy, seriesNames, threshold) { + function groupSeries(metanode, hierarchy, seriesNames, threshold, map) { var metagraph = metanode.metagraph; _.each(metagraph.nodes(), function (n) { var child = metagraph.node(n); if (child.type === tf.graph.NodeType.META) { - groupSeries(child, hierarchy, seriesNames, threshold); + groupSeries(child, hierarchy, seriesNames, threshold, map); } }); var clusters = clusterNodes(metagraph); @@ -4122,7 +4272,21 @@ var tf; // metagraph. _.each(seriesDict, function (seriesNode, seriesName) { var nodeMemberNames = seriesNode.metagraph.nodes(); - if (nodeMemberNames.length < threshold) { + _.each(nodeMemberNames, function (n) { + var child = metagraph.node(n); + if (!child.owningSeries) { + child.owningSeries = seriesName; + } + }); + // If the series contains less than the threshold number of nodes and + // this series has not been adding to the series map, then set this + // series to be shown ungrouped in the map. + if (nodeMemberNames.length < threshold && !(seriesNode.name in map)) { + map[seriesNode.name] = tf.graph.SeriesGroupingType.UNGROUP; + } + // If the series is in the map as ungrouped then do not group the series. + if (seriesNode.name in map + && map[seriesNode.name] === tf.graph.SeriesGroupingType.UNGROUP) { return; } hierarchy.setNode(seriesName, seriesNode); // add to the index @@ -4202,9 +4366,6 @@ var tf; } else { prefix = isGroup ? leaf.substr(0, leaf.length - 1) : leaf; - if (prefix.charAt(prefix.length - 1) !== "_") { - prefix += "_"; - } id = 0; suffix = isGroup ? "*" : ""; } @@ -4252,19 +4413,24 @@ var tf; function addSeriesToDict(seriesNodes, seriesDict, clusterId, metagraph) { if (seriesNodes.length > 1) { var curSeriesName = graph_1.getSeriesNodeName(seriesNodes[0].prefix, seriesNodes[0].suffix, seriesNodes[0].parent, seriesNodes[0].clusterId, seriesNodes[seriesNodes.length - 1].clusterId); - var curSeriesNode = graph_1.createSeriesNode(seriesNodes[0].prefix, seriesNodes[0].suffix, seriesNodes[0].parent, clusterId, curSeriesName); + var curSeriesNode_1 = graph_1.createSeriesNode(seriesNodes[0].prefix, seriesNodes[0].suffix, seriesNodes[0].parent, clusterId, curSeriesName); _.each(seriesNodes, function (node) { - curSeriesNode.ids.push(node.clusterId); - curSeriesNode.metagraph.setNode(node.name, metagraph.node(node.name)); + curSeriesNode_1.ids.push(node.clusterId); + curSeriesNode_1.metagraph.setNode(node.name, metagraph.node(node.name)); }); - seriesDict[curSeriesName] = curSeriesNode; + seriesDict[curSeriesName] = curSeriesNode_1; } } })(hierarchy = graph_1.hierarchy || (graph_1.hierarchy = {})); })(graph = tf.graph || (tf.graph = {})); })(tf || (tf = {})); // close module tf.graph.hierarchy </script> -<script>/* Copyright 2015 Google Inc. All Rights Reserved. +<script>var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +/* Copyright 2015 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -4278,13 +4444,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -}; -/// <reference path="graph.ts" /> -/// <reference path="hierarchy.ts" /> /** * Package for the Render Hierarchy for TensorFlow graph. */ @@ -4346,6 +4505,14 @@ var tf; DEFAULT_FILL: "white", DEFAULT_STROKE: "#b2b2b2" }; + /** The minimum stroke width of an edge. */ + var MIN_EDGE_WIDTH = 0.75; + /** The maximum stroke width of an edge. */ + var MAX_EDGE_WIDTH = 12; + /** The exponent used in the power scale for edge thickness. */ + var EDGE_WIDTH_SCALE_EXPONENT = 0.3; + /** The domain (min and max value) for the edge width. */ + var DOMAIN_EDGE_WIDTH_SCALE = [1, 5E6]; /** * Stores the rendering information, such as x and y coordinates, * for each node in the graph. @@ -4357,6 +4524,11 @@ var tf; this.deviceColorMap = d3.scale.ordinal() .domain(hierarchy.devices) .range(_.map(d3.range(hierarchy.devices.length), render.MetanodeColors.DEVICE_PALETTE)); + this.edgeWidthScale = d3.scale.pow() + .exponent(EDGE_WIDTH_SCALE_EXPONENT) + .domain(DOMAIN_EDGE_WIDTH_SCALE) + .range([MIN_EDGE_WIDTH, MAX_EDGE_WIDTH]) + .clamp(true); var topLevelGraph = hierarchy.root.metagraph; // Find the maximum and minimum memory usage. var memoryExtent = d3.extent(topLevelGraph.nodes(), function (nodeName, index) { @@ -4396,6 +4568,12 @@ var tf; return this.index[nodeName]; }; /** + * Get the underlying node in the hierarchical graph by its name. + */ + RenderGraphInfo.prototype.getNodeByName = function (nodeName) { + return this.hierarchy.node(nodeName); + }; + /** * Get a previously created RenderNodeInfo for the specified node name, * or create one if it hasn't been created yet. */ @@ -4409,6 +4587,12 @@ var tf; return this.index[nodeName]; } var node = this.hierarchy.node(nodeName); + // Exit early if the node does not exist in the hierarchy. This can happen + // when a graph is reloaded while the infocard points to a node not visible + // at the top-level. + if (!node) { + return null; + } var renderInfo = node.isGroupNode ? new RenderGroupNodeInfo(node) : new RenderNodeInfo(node); @@ -4424,11 +4608,11 @@ var tf; var pairs = _.pairs(node.deviceHistogram); if (pairs.length > 0) { // Compute the total # of devices. - var numDevices = _.sum(pairs, _.last); + var numDevices_1 = _.sum(pairs, _.last); renderInfo.deviceColors = _.map(pairs, function (pair) { return ({ color: _this.deviceColorMap(pair[0]), // Normalize to a proportion of total # of devices. - proportion: pair[1] / numDevices + proportion: pair[1] / numDevices_1 }); }); } } @@ -4661,7 +4845,7 @@ var tf; // (which ignores control edges) and seeing that Z comes AFTER A. // // The property of being backwards is independent of whether the edge - // is inbound or outbound. In the preceeding example, if we were building + // is inbound or outbound. In the preceding example, if we were building // the subhierarchy for Z, we'd find bridge edge Z/Y=>A, walk to its // topmost adjoining metaedge Z=>A and discover that it's backwards. var backwards = false; @@ -4752,7 +4936,7 @@ var tf; // one edge in the bridgegraph from Z->A/C. // // At this point, we've added a container bridge node IN to house all - // incoming bridge nodes. We'v alse added a bridge node Z' (with parent IN) + // incoming bridge nodes. We've also added a bridge node Z' (with parent IN) // to A, and a bridge edge from Z'->C. // // +----------------------+ @@ -4860,7 +5044,7 @@ var tf; }); }; return RenderGraphInfo; - })(); + }()); render.RenderGraphInfo = RenderGraphInfo; /** * A class for rendering annotation object which contains label @@ -4899,7 +5083,7 @@ var tf; this.points = []; } return Annotation; - })(); + }()); render.Annotation = Annotation; ; (function (AnnotationType) { @@ -4942,7 +5126,7 @@ var tf; this.list.push(new Annotation(ellipsisNode, new RenderNodeInfo(ellipsisNode), null, AnnotationType.ELLIPSIS, annotation.isIn)); }; return AnnotationList; - })(); + }()); render.AnnotationList = AnnotationList; /** * Contains rendering information about a node in the hierarchical graph. @@ -4981,7 +5165,7 @@ var tf; return !this.isInExtract && !this.isOutExtract; }; return RenderNodeInfo; - })(); + }()); render.RenderNodeInfo = RenderNodeInfo; /** * Contains rendering information about a Metaedge from the underlying @@ -4995,7 +5179,7 @@ var tf; this.weight = 1; } return RenderMetaedgeInfo; - })(); + }()); render.RenderMetaedgeInfo = RenderMetaedgeInfo; function addInAnnotation(node, predecessor, predecessorRenderInfo, edge, type, params) { var annotation = new Annotation(predecessor, predecessorRenderInfo, edge, type, true); @@ -5034,7 +5218,7 @@ var tf; this.isolatedOutExtract = []; } return RenderGroupNodeInfo; - })(RenderNodeInfo); + }(RenderNodeInfo)); render.RenderGroupNodeInfo = RenderGroupNodeInfo; function setGroupNodeDepth(renderInfo, depth) { if (renderInfo.coreGraph) { @@ -5146,7 +5330,7 @@ var tf; } return false; } - /** Move nodes that are speficied to be excluded out of the core graph. */ + /** Move nodes that are specified to be excluded out of the core graph. */ function extractSpecifiedNodes(renderNode, params) { var graph = renderNode.coreGraph; _.each(graph.nodes(), function (n) { @@ -5380,8 +5564,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="graph.ts" /> -/// <reference path="hierarchy.ts" /> var tf; (function (tf) { var graph; @@ -5646,10 +5828,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../graph.ts" /> -/// <reference path="edge.ts" /> -/// <reference path="node.ts" /> -/// <reference path="../layout.ts" /> var tf; (function (tf) { var graph; @@ -5874,10 +6052,10 @@ var tf; * * @param container D3 selection of the parent. * @param renderNode render node of a metanode or series node. - * @param sceneBehavior Parent scene module. + * @param sceneElement <tf-graph-scene> polymer element. * @param sceneClass class attribute of the scene (default="scene"). */ - function buildGroup(container, renderNode, sceneBehavior, sceneClass) { + function buildGroup(container, renderNode, sceneElement, sceneClass) { sceneClass = sceneClass || scene.Class.Scene.GROUP; var isNewSceneGroup = selectChild(container, "g", sceneClass).empty(); var sceneGroup = selectOrCreateChild(container, "g", sceneClass); @@ -5897,13 +6075,13 @@ var tf; coreNodes.reverse(); } // Create the layer of edges for this scene (paths). - scene.edge.buildGroup(coreGroup, renderNode.coreGraph, sceneBehavior); + scene.edge.buildGroup(coreGroup, renderNode.coreGraph, sceneElement); // Create the layer of nodes for this scene (ellipses, rects etc). - scene.node.buildGroup(coreGroup, coreNodes, sceneBehavior); + scene.node.buildGroup(coreGroup, coreNodes, sceneElement); // In-extract if (renderNode.isolatedInExtract.length > 0) { var inExtractGroup = selectOrCreateChild(sceneGroup, "g", scene.Class.Scene.INEXTRACT); - scene.node.buildGroup(inExtractGroup, renderNode.isolatedInExtract, sceneBehavior); + scene.node.buildGroup(inExtractGroup, renderNode.isolatedInExtract, sceneElement); } else { selectChild(sceneGroup, "g", scene.Class.Scene.INEXTRACT).remove(); @@ -5911,7 +6089,7 @@ var tf; // Out-extract if (renderNode.isolatedOutExtract.length > 0) { var outExtractGroup = selectOrCreateChild(sceneGroup, "g", scene.Class.Scene.OUTEXTRACT); - scene.node.buildGroup(outExtractGroup, renderNode.isolatedOutExtract, sceneBehavior); + scene.node.buildGroup(outExtractGroup, renderNode.isolatedOutExtract, sceneElement); } else { selectChild(sceneGroup, "g", scene.Class.Scene.OUTEXTRACT).remove(); @@ -5958,9 +6136,9 @@ var tf; } ; /** Adds a click listener to a group that fires a graph-select event */ - function addGraphClickListener(graphGroup, sceneBehavior) { + function addGraphClickListener(graphGroup, sceneElement) { d3.select(graphGroup).on("click", function () { - sceneBehavior.fire("graph-select"); + sceneElement.fire("graph-select"); }); } scene.addGraphClickListener = addGraphClickListener; @@ -6061,11 +6239,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../graph.ts" /> -/// <reference path="../render.ts" /> -/// <reference path="scene.ts" /> -/// <reference path="edge.ts" /> -/// <reference path="contextmenu.ts" /> var tf; (function (tf) { var graph; @@ -6092,10 +6265,10 @@ var tf; * @param container selection of the container. * @param annotationData node.{in|out}Annotations * @param d node to build group for. - * @param sceneBehavior polymer scene element. + * @param sceneElement <tf-graph-scene> polymer element. * @return selection of appended objects */ - function buildGroup(container, annotationData, d, sceneBehavior) { + function buildGroup(container, annotationData, d, sceneElement) { // Select all children and join with data. var annotationGroups = container.selectAll(function () { // using d3's selector function @@ -6110,7 +6283,7 @@ var tf; .each(function (a) { var aGroup = d3.select(this); // Add annotation to the index in the scene - sceneBehavior.addAnnotationGroup(a, d, aGroup); + sceneElement.addAnnotationGroup(a, d, aGroup); // Append annotation edge var edgeType = scene.Class.Annotation.EDGE; var metaedge = a.renderMetaedgeInfo && a.renderMetaedgeInfo.metaedge; @@ -6121,10 +6294,10 @@ var tf; if (metaedge && metaedge.numRefEdges) { edgeType += " " + scene.Class.Edge.REF_LINE; } - scene.edge.appendEdge(aGroup, a, sceneBehavior, edgeType); - if (a.annotationType !== tf.graph.render.AnnotationType.ELLIPSIS) { + scene.edge.appendEdge(aGroup, a, sceneElement, edgeType); + if (a.annotationType !== graph.render.AnnotationType.ELLIPSIS) { addAnnotationLabelFromNode(aGroup, a); - buildShape(aGroup, a, sceneBehavior); + buildShape(aGroup, a); } else { addAnnotationLabel(aGroup, a.node.name, a, scene.Class.Annotation.ELLIPSIS); @@ -6138,16 +6311,16 @@ var tf; }) .each(function (a) { var aGroup = d3.select(this); - update(aGroup, d, a, sceneBehavior); - if (a.annotationType !== tf.graph.render.AnnotationType.ELLIPSIS) { - addInteraction(aGroup, d, a, sceneBehavior); + update(aGroup, d, a, sceneElement); + if (a.annotationType !== graph.render.AnnotationType.ELLIPSIS) { + addInteraction(aGroup, d, a, sceneElement); } }); annotationGroups.exit() .each(function (a) { var aGroup = d3.select(this); // Remove annotation from the index in the scene - sceneBehavior.removeAnnotationGroup(a, d, aGroup); + sceneElement.removeAnnotationGroup(a, d, aGroup); }) .remove(); return annotationGroups; @@ -6158,11 +6331,11 @@ var tf; * Maps an annotation enum to a class name used in css rules. */ function annotationToClassName(annotationType) { - return (tf.graph.render.AnnotationType[annotationType] || "") + return (graph.render.AnnotationType[annotationType] || "") .toLowerCase() || null; } - function buildShape(aGroup, a, sceneBehavior) { - if (a.annotationType === tf.graph.render.AnnotationType.SUMMARY) { + function buildShape(aGroup, a) { + if (a.annotationType === graph.render.AnnotationType.SUMMARY) { var summary = scene.selectOrCreateChild(aGroup, "use"); summary.attr({ "class": "summary", @@ -6195,16 +6368,16 @@ var tf; .text(label) .append("title").text(titleText); } - function addInteraction(selection, d, annotation, sceneBehavior) { + function addInteraction(selection, d, annotation, sceneElement) { selection .on("mouseover", function (a) { - sceneBehavior.fire("annotation-highlight", { + sceneElement.fire("annotation-highlight", { name: a.node.name, hostName: d.node.name }); }) .on("mouseout", function (a) { - sceneBehavior.fire("annotation-unhighlight", { + sceneElement.fire("annotation-unhighlight", { name: a.node.name, hostName: d.node.name }); @@ -6213,14 +6386,14 @@ var tf; // Stop this event"s propagation so that it isn't also considered a // graph-select. d3.event.stopPropagation(); - sceneBehavior.fire("annotation-select", { + sceneElement.fire("annotation-select", { name: a.node.name, hostName: d.node.name }); }); - if (annotation.annotationType !== tf.graph.render.AnnotationType.SUMMARY && - annotation.annotationType !== tf.graph.render.AnnotationType.CONSTANT) { - selection.on("contextmenu", tf.graph.scene.contextmenu.getMenu(tf.graph.scene.node.getContextMenu(annotation.node, sceneBehavior))); + if (annotation.annotationType !== graph.render.AnnotationType.SUMMARY && + annotation.annotationType !== graph.render.AnnotationType.CONSTANT) { + selection.on("contextmenu", scene.contextmenu.getMenu(scene.node.getContextMenu(annotation.node, sceneElement))); } } ; @@ -6230,18 +6403,18 @@ var tf; * @param aGroup selection of a "g.annotation" element. * @param d Host node data. * @param a annotation node data. - * @param scene Polymer scene element. + * @param scene <tf-graph-scene> polymer element. */ - function update(aGroup, d, a, sceneBehavior) { + function update(aGroup, d, a, sceneElement) { var cx = graph.layout.computeCXPositionOfNodeShape(d); // Annotations that point to embedded nodes (constants,summary) // don't have a render information attached so we don't stylize these. // Also we don't stylize ellipsis annotations (the string "... and X more"). if (a.renderNodeInfo && - a.annotationType !== tf.graph.render.AnnotationType.ELLIPSIS) { - scene.node.stylize(aGroup, a.renderNodeInfo, sceneBehavior, scene.Class.Annotation.NODE); + a.annotationType !== graph.render.AnnotationType.ELLIPSIS) { + scene.node.stylize(aGroup, a.renderNodeInfo, sceneElement, scene.Class.Annotation.NODE); } - if (a.annotationType === tf.graph.render.AnnotationType.SUMMARY) { + if (a.annotationType === graph.render.AnnotationType.SUMMARY) { // Update the width of the annotation to give space for the image. a.width += 10; } @@ -6251,7 +6424,7 @@ var tf; y: d.y + a.dy }); // Some annotations (such as summary) are represented using a 12x12 image tag. - // Purposely ommited units (e.g. pixels) since the images are vector graphics. + // Purposely omitted units (e.g. pixels) since the images are vector graphics. // If there is an image, we adjust the location of the image to be vertically // centered with the node and horizontally centered between the arrow and the // text label. @@ -6292,9 +6465,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../graph.ts" /> -/// <reference path="../render.ts" /> -/// <reference path="scene.ts" /> var tf; (function (tf) { var graph; @@ -6303,8 +6473,10 @@ var tf; (function (scene) { var edge; (function (edge) { + /** Delimiter between dimensions when showing sizes of tensors. */ + var TENSOR_SHAPE_DELIM = "Ă—"; function getEdgeKey(edgeObj) { - return edgeObj.v + tf.graph.EDGE_KEY_DELIM + edgeObj.w; + return edgeObj.v + graph_1.EDGE_KEY_DELIM + edgeObj.w; } edge.getEdgeKey = getEdgeKey; /** @@ -6323,12 +6495,12 @@ var tf; * * @param sceneGroup container * @param graph - * @param sceneBehavior Parent scene module. + * @param sceneElement <tf-graph-scene> polymer element. * @return selection of the created nodeGroups */ - function buildGroup(sceneGroup, graph, sceneBehavior) { + function buildGroup(sceneGroup, graph, sceneElement) { var edges = []; - var edgeData = _.reduce(graph.edges(), function (edges, edgeObj) { + edges = _.reduce(graph.edges(), function (edges, edgeObj) { var edgeLabel = graph.edge(edgeObj); edges.push({ v: edgeObj.v, @@ -6345,8 +6517,7 @@ var tf; // See https://github.com/mbostock/d3/releases/tag/v2.0.0 // (It's not listed in the d3 wiki.) return this.childNodes; - }) - .data(edgeData, getEdgeKey); + }).data(edges, getEdgeKey); // Make edges a group to support rendering multiple lines for metaedge edgeGroups.enter() .append("g") @@ -6356,28 +6527,62 @@ var tf; var edgeGroup = d3.select(this); d.label.edgeGroup = edgeGroup; // index node group for quick highlighting - sceneBehavior._edgeGroupIndex[getEdgeKey(d)] = edgeGroup; + sceneElement._edgeGroupIndex[getEdgeKey(d)] = edgeGroup; // If any edges are reference edges, add the reference edge class. var extraEdgeClass = d.label.metaedge && d.label.metaedge.numRefEdges ? scene.Class.Edge.REF_LINE + " " + scene.Class.Edge.LINE : undefined; // Add line during enter because we're assuming that type of line // normally does not change. - appendEdge(edgeGroup, d, scene, extraEdgeClass); + appendEdge(edgeGroup, d, sceneElement, extraEdgeClass); }); edgeGroups.each(position); edgeGroups.each(function (d) { - stylize(d3.select(this), d, sceneBehavior); + stylize(d3.select(this), d, sceneElement); }); edgeGroups.exit() .each(function (d) { - delete sceneBehavior._edgeGroupIndex[getEdgeKey(d)]; + delete sceneElement._edgeGroupIndex[getEdgeKey(d)]; }) .remove(); return edgeGroups; } edge.buildGroup = buildGroup; ; + function getShapeLabelFromNode(node, renderInfo) { + if (node.outputShapes == null || node.outputShapes.length === 0) { + return null; + } + // TODO(smilkov): Figure out exactly which output tensor this + // edge is from. + var shape = node.outputShapes[0]; + if (shape == null) { + return null; + } + if (shape.length === 0) { + return "scalar"; + } + return shape.map(function (size) { + return size === -1 ? "?" : size; + }).join(TENSOR_SHAPE_DELIM); + } + edge.getShapeLabelFromNode = getShapeLabelFromNode; + /** + * Creates the label for the given metaedge. If the metaedge consists + * of only 1 tensor, and it's shape is known, the label will contain that + * shape. Otherwise, the label will say the number of tensors in the metaedge. + */ + function getLabelForEdge(metaedge, renderInfo) { + var isMultiEdge = metaedge.baseEdgeList.length > 1; + if (isMultiEdge) { + return metaedge.baseEdgeList.length + " tensors"; + } + else { + var node_1 = renderInfo.getNodeByName(metaedge.baseEdgeList[0].v); + return getShapeLabelFromNode(node_1, renderInfo); + } + } + edge.getLabelForEdge = getLabelForEdge; /** * For a given d3 selection and data object, create a path to represent the * edge described in d.label. @@ -6386,13 +6591,43 @@ var tf; * will sometimes be undefined, for example for some Annotation edges for which * there is no underlying Metaedge in the hierarchical graph. */ - function appendEdge(edgeGroup, d, sceneBehavior, edgeClass) { + function appendEdge(edgeGroup, d, sceneElement, edgeClass) { + var size = 1; + if (d.label != null && d.label.metaedge != null) { + // There is an underlying Metaedge. + size = d.label.metaedge.totalSize; + } edgeClass = edgeClass || scene.Class.Edge.LINE; // set default type if (d.label && d.label.structural) { edgeClass += " " + scene.Class.Edge.STRUCTURAL; } + // Give the path a unique id, which will be used to link + // the textPath (edge label) to this path. + var pathId = "path_" + getEdgeKey(d); + var strokeWidth = sceneElement.renderHierarchy.edgeWidthScale(size); edgeGroup.append("path") - .attr("class", edgeClass); + .attr({ + "id": pathId, + "class": edgeClass, + }).style({ + "stroke-width": strokeWidth + "px" + }); + if (d.label == null || d.label.metaedge == null) { + // There is no associated metaedge, thus no text. + // This happens for annotation edges. + return; + } + var labelForEdge = getLabelForEdge(d.label.metaedge, sceneElement.renderHierarchy); + if (labelForEdge == null) { + // We have no information to show on this edge. + return; + } + edgeGroup.append("text").append("textPath").attr({ + "xlink:href": "#" + pathId, + "startOffset": "50%", + "text-anchor": "middle", + "dominant-baseline": "central" + }).text(labelForEdge); } edge.appendEdge = appendEdge; ; @@ -6406,8 +6641,9 @@ var tf; function getEdgePathInterpolator(d, i, a) { var renderMetaedgeInfo = d.label; var adjoiningMetaedge = renderMetaedgeInfo.adjoiningMetaedge; + var points = renderMetaedgeInfo.points; if (!adjoiningMetaedge) { - return d3.interpolate(a, edge.interpolate(renderMetaedgeInfo.points)); + return d3.interpolate(a, edge.interpolate(points)); } var renderPath = this; // Get the adjoining path that matches the adjoining metaedge. @@ -6424,7 +6660,6 @@ var tf; .matrixTransform(renderPath.getCTM().inverse()); // Update the relevant point in the renderMetaedgeInfo's points list, then // re-interpolate the path. - var points = renderMetaedgeInfo.points; var index = inbound ? 0 : points.length - 1; points[index].x = adjoiningPoint.x; points[index].y = adjoiningPoint.y; @@ -6434,10 +6669,8 @@ var tf; } function position(d) { d3.select(this).select("path." + scene.Class.Edge.LINE) - .each(function (d) { - var path = d3.select(this); - path.transition().attrTween("d", getEdgePathInterpolator); - }); + .transition() + .attrTween("d", getEdgePathInterpolator); } ; /** @@ -6472,10 +6705,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../graph.ts" /> -/// <reference path="scene.ts" /> -/// <reference path="annotation.ts" /> -/// <reference path="contextmenu.ts" /> var tf; (function (tf) { var graph; @@ -6525,10 +6754,10 @@ var tf; * * @param sceneGroup selection of the container * @param nodeData array of render node information to map - * @param sceneBehavior parent scene module + * @param sceneElement <tf-graph-scene> polymer element * @return selection of the created nodeGroups */ - function buildGroup(sceneGroup, nodeData, sceneBehavior) { + function buildGroup(sceneGroup, nodeData, sceneElement) { var container = scene.selectOrCreateChild(sceneGroup, "g", scene.Class.Node.CONTAINER); // Select all children and join with data. // (Note that all children of g.nodes are g.node) @@ -6549,7 +6778,7 @@ var tf; .each(function (d) { var nodeGroup = d3.select(this); // index node group for quick stylizing - sceneBehavior.addNodeGroup(d.node.name, nodeGroup); + sceneElement.addNodeGroup(d.node.name, nodeGroup); }); // UPDATE nodeGroups @@ -6558,46 +6787,46 @@ var tf; }) .each(function (d) { var nodeGroup = d3.select(this); - // add g.in-annotations (always add -- to keep layer order consistent.) + // Add g.in-annotations (always add -- to keep layer order consistent.) var inAnnotationBox = scene.selectOrCreateChild(nodeGroup, "g", scene.Class.Annotation.INBOX); - scene.annotation.buildGroup(inAnnotationBox, d.inAnnotations, d, sceneBehavior); - // add g.out-annotations (always add -- to keep layer order consistent.) + scene.annotation.buildGroup(inAnnotationBox, d.inAnnotations, d, sceneElement); + // Add g.out-annotations (always add -- to keep layer order consistent.) var outAnnotationBox = scene.selectOrCreateChild(nodeGroup, "g", scene.Class.Annotation.OUTBOX); - scene.annotation.buildGroup(outAnnotationBox, d.outAnnotations, d, sceneBehavior); - // label - var label = labelBuild(nodeGroup, d, sceneBehavior); - // Do not add interaction to metanode labels as they live inside the - // metanode shape which already has the same interactions. - addInteraction(label, d, sceneBehavior, d.node.type === graph.NodeType.META); - // build .shape below label - var shape = buildShape(nodeGroup, d, scene.Class.Node.SHAPE, label.node()); + scene.annotation.buildGroup(outAnnotationBox, d.outAnnotations, d, sceneElement); + // Build .shape first (background of the node). + var shape = buildShape(nodeGroup, d, scene.Class.Node.SHAPE); if (d.node.isGroupNode) { - addButton(shape, d, sceneBehavior); + addButton(shape, d, sceneElement); } - addInteraction(shape, d, sceneBehavior); - // build subscene on the top - subsceneBuild(nodeGroup, d, sceneBehavior); - stylize(nodeGroup, d, sceneBehavior); - position(nodeGroup, d, sceneBehavior); + addInteraction(shape, d, sceneElement); + // Build subscene on the top. + subsceneBuild(nodeGroup, d, sceneElement); + // Build label last. Should be on top of everything else. + var label = labelBuild(nodeGroup, d, sceneElement); + // Do not add interaction to metanode labels as they live inside the + // metanode shape which already has the same interactions. + addInteraction(label, d, sceneElement, d.node.type === graph.NodeType.META); + stylize(nodeGroup, d, sceneElement); + position(nodeGroup, d); }); // EXIT nodeGroups.exit() .each(function (d) { // remove all indices on remove - sceneBehavior.removeNodeGroup(d.node.name); + sceneElement.removeNodeGroup(d.node.name); var nodeGroup = d3.select(this); if (d.inAnnotations.list.length > 0) { nodeGroup.select("." + scene.Class.Annotation.INBOX) .selectAll("." + scene.Class.Annotation.GROUP) .each(function (a) { - sceneBehavior.removeAnnotationGroup(a, d); + sceneElement.removeAnnotationGroup(a, d); }); } if (d.outAnnotations.list.length > 0) { nodeGroup.select("." + scene.Class.Annotation.OUTBOX) .selectAll("." + scene.Class.Annotation.GROUP) .each(function (a) { - sceneBehavior.removeAnnotationGroup(a, d); + sceneElement.removeAnnotationGroup(a, d); }); } }) @@ -6612,16 +6841,16 @@ var tf; * * @param nodeGroup selection of the container * @param renderNodeInfo the render information for the node. - * @param sceneBehavior parent scene module + * @param sceneElement <tf-graph-scene> polymer element. * @return Selection of the subscene group, or null if node group does not have * a subscene. Op nodes, bridge nodes and unexpanded group nodes will * not have a subscene. */ - function subsceneBuild(nodeGroup, renderNodeInfo, sceneBehavior) { + function subsceneBuild(nodeGroup, renderNodeInfo, sceneElement) { if (renderNodeInfo.node.isGroupNode) { if (renderNodeInfo.expanded) { // Recursively build the subscene. - return scene.buildGroup(nodeGroup, renderNodeInfo, sceneBehavior, scene.Class.Subscene.GROUP); + return scene.buildGroup(nodeGroup, renderNodeInfo, sceneElement, scene.Class.Subscene.GROUP); } // Clean out existing subscene if the node is not expanded. scene.selectChild(nodeGroup, "g", scene.Class.Subscene.GROUP).remove(); @@ -6644,9 +6873,9 @@ var tf; * * @param selection The group node selection. * @param d Info about the node being rendered. - * @param sceneBehavior parent scene module. + * @param sceneElement <tf-graph-scene> polymer element. */ - function addButton(selection, d, sceneBehavior) { + function addButton(selection, d, sceneElement) { var group = scene.selectOrCreateChild(selection, "g", scene.Class.Node.BUTTON_CONTAINER); scene.selectOrCreateChild(group, "circle", scene.Class.Node.BUTTON_CIRCLE); scene.selectOrCreateChild(group, "path", scene.Class.Node.EXPAND_BUTTON).attr("d", "M0,-2.2 V2.2 M-2.2,0 H2.2"); @@ -6655,7 +6884,7 @@ var tf; // Stop this event's propagation so that it isn't also considered a // node-select. d3.event.stopPropagation(); - sceneBehavior.fire("node-toggle-expand", { name: d.node.name }); + sceneElement.fire("node-toggle-expand", { name: d.node.name }); }); scene.positionButton(group, d); } @@ -6668,39 +6897,39 @@ var tf; * don't need interaction as their surrounding shape has interaction, and if * given interaction would cause conflicts with the expand/collapse button. */ - function addInteraction(selection, d, sceneBehavior, disableInteraction) { + function addInteraction(selection, d, sceneElement, disableInteraction) { if (disableInteraction) { selection.attr("pointer-events", "none"); return; } - var contextMenuFunction = tf.graph.scene.contextmenu.getMenu(getContextMenu(d.node, sceneBehavior)); + var contextMenuFunction = scene.contextmenu.getMenu(getContextMenu(d.node, sceneElement)); selection.on("dblclick", function (d) { - sceneBehavior.fire("node-toggle-expand", { name: d.node.name }); + sceneElement.fire("node-toggle-expand", { name: d.node.name }); }) .on("mouseover", function (d) { // don't send mouseover over expanded group, // otherwise it is causing too much glitches - if (sceneBehavior.isNodeExpanded(d)) { + if (sceneElement.isNodeExpanded(d)) { return; } - sceneBehavior.fire("node-highlight", { name: d.node.name }); + sceneElement.fire("node-highlight", { name: d.node.name }); }) .on("mouseout", function (d) { // don't send mouseover over expanded group, // otherwise it is causing too much glitches - if (sceneBehavior.isNodeExpanded(d)) { + if (sceneElement.isNodeExpanded(d)) { return; } - sceneBehavior.fire("node-unhighlight", { name: d.node.name }); + sceneElement.fire("node-unhighlight", { name: d.node.name }); }) .on("click", function (d) { // Stop this event's propagation so that it isn't also considered // a graph-select. d3.event.stopPropagation(); - sceneBehavior.fire("node-select", { name: d.node.name }); + sceneElement.fire("node-select", { name: d.node.name }); }) .on("contextmenu", function (d, i) { - sceneBehavior.fire("node-select", { name: d.node.name }); + sceneElement.fire("node-select", { name: d.node.name }); contextMenuFunction.call(d, i); }); } @@ -6708,37 +6937,101 @@ var tf; /** * Returns the d3 context menu specification for the provided node. */ - function getContextMenu(node, sceneBehavior) { - return [{ + function getContextMenu(node, sceneElement) { + var menu = [{ title: function (d) { - return tf.graph.getIncludeNodeButtonString(node.include); + return graph.getIncludeNodeButtonString(node.include); }, action: function (elm, d, i) { - sceneBehavior.fire("node-toggle-extract", { name: node.name }); + sceneElement.fire("node-toggle-extract", { name: node.name }); } }]; + if (canBeInSeries(node)) { + menu.push({ + title: function (d) { + return getGroupSettingLabel(node); + }, + action: function (elm, d, i) { + sceneElement.fire("node-toggle-seriesgroup", { name: getSeriesName(node) }); + } + }); + } + return menu; } node_1.getContextMenu = getContextMenu; + /** Returns if a node can be part of a grouped series */ + function canBeInSeries(node) { + return getSeriesName(node) !== null; + } + node_1.canBeInSeries = canBeInSeries; + /** + * Returns the name of the possible grouped series containing this node. + * Returns null if the node cannot be part of a grouped series of nodes. + */ + function getSeriesName(node) { + if (!node) { + return null; + } + if (node.type === graph.NodeType.SERIES) { + return node.name; + } + if (node.type === graph.NodeType.OP) { + var op = node; + return op.owningSeries; + } + return null; + } + node_1.getSeriesName = getSeriesName; + /** + * Returns the SeriesNode that represents the series that the provided node + * is contained in (or itself if the provided node is itself a SeriesNode). + * Returns null if the node is not rendered as part of a series. + */ + function getContainingSeries(node) { + var s = null; + if (!node) { + return null; + } + else if (node.type === graph.NodeType.SERIES) { + s = node; + } + else if (node.parentNode && node.parentNode.type === graph.NodeType.SERIES) { + s = node.parentNode; + } + return s; + } + /** + * Returns the label for a button to toggle the group setting of the provided + * node. + */ + function getGroupSettingLabel(node) { + return tf.graph.getGroupSeriesNodeButtonString(getContainingSeries(node) !== null ? tf.graph.SeriesGroupingType.GROUP : + tf.graph.SeriesGroupingType.UNGROUP); + } + node_1.getGroupSettingLabel = getGroupSettingLabel; /** * Append svg text for label and assign data. * @param nodeGroup * @param renderNodeInfo The render node information for the label. - * @param sceneBehavior parent scene module. + * @param sceneElement <tf-graph-scene> polymer element. */ - function labelBuild(nodeGroup, renderNodeInfo, sceneBehavior) { + function labelBuild(nodeGroup, renderNodeInfo, sceneElement) { var namePath = renderNodeInfo.node.name.split("/"); var text = namePath[namePath.length - 1]; // Truncate long labels for unexpanded Metanodes. var useFontScale = renderNodeInfo.node.type === graph.NodeType.META && !renderNodeInfo.expanded; var label = scene.selectOrCreateChild(nodeGroup, "text", scene.Class.Node.LABEL); + // Make sure the label is visually on top among its siblings. + var labelNode = label.node(); + labelNode.parentNode.appendChild(labelNode); label.attr("dy", ".35em") .attr("text-anchor", "middle"); if (useFontScale) { - if (text.length > sceneBehavior.maxMetanodeLabelLength) { - text = text.substr(0, sceneBehavior.maxMetanodeLabelLength - 2) + "..."; + if (text.length > sceneElement.maxMetanodeLabelLength) { + text = text.substr(0, sceneElement.maxMetanodeLabelLength - 2) + "..."; } - var scale = getLabelFontScale(sceneBehavior); + var scale = getLabelFontScale(sceneElement); label.attr("font-size", scale(text.length) + "px"); } label.text(text); @@ -6750,13 +7043,13 @@ var tf; * initialized once by getLabelFontScale. */ var fontScale = null; - function getLabelFontScale(sceneBehavior) { + function getLabelFontScale(sceneElement) { if (!fontScale) { fontScale = d3.scale.linear() - .domain([sceneBehavior.maxMetanodeLabelLengthLargeFont, - sceneBehavior.maxMetanodeLabelLength]) - .range([sceneBehavior.maxMetanodeLabelLengthFontSize, - sceneBehavior.minMetanodeLabelLengthFontSize]).clamp(true); + .domain([sceneElement.maxMetanodeLabelLengthLargeFont, + sceneElement.maxMetanodeLabelLength]) + .range([sceneElement.maxMetanodeLabelLengthFontSize, + sceneElement.minMetanodeLabelLengthFontSize]).clamp(true); } return fontScale; } @@ -6776,12 +7069,11 @@ var tf; * @param nodeGroup * @param d Render node information. * @param nodeClass class for the element. - * @param before Reference DOM node for insertion. * @return Selection of the shape. */ - function buildShape(nodeGroup, d, nodeClass, before) { + function buildShape(nodeGroup, d, nodeClass) { // Create a group to house the underlying visual elements. - var shapeGroup = scene.selectOrCreateChild(nodeGroup, "g", nodeClass, before); + var shapeGroup = scene.selectOrCreateChild(nodeGroup, "g", nodeClass); // TODO(jimbo): DOM structure should be templated in HTML somewhere, not JS. switch (d.node.type) { case graph.NodeType.OP: @@ -6834,7 +7126,7 @@ var tf; node_1.nodeClass = nodeClass; ; /** Modify node and its subscene and its label's positional attributes */ - function position(nodeGroup, d, sceneBehavior) { + function position(nodeGroup, d) { var shapeGroup = scene.selectChild(nodeGroup, "g", scene.Class.Node.SHAPE); var cx = graph.layout.computeCXPositionOfNodeShape(d); switch (d.node.type) { @@ -6902,16 +7194,16 @@ var tf; * option. */ function getFillForNode(templateIndex, colorBy, renderInfo, isExpanded) { - var colorParams = tf.graph.render.MetanodeColors; + var colorParams = graph.render.MetanodeColors; switch (colorBy) { case ColorBy.STRUCTURE: - if (renderInfo.node.type === tf.graph.NodeType.META) { + if (renderInfo.node.type === graph.NodeType.META) { var tid = renderInfo.node.templateId; return tid === null ? colorParams.UNKNOWN : colorParams.STRUCTURE_PALETTE(templateIndex(tid), isExpanded); } - else if (renderInfo.node.type === tf.graph.NodeType.SERIES) { + else if (renderInfo.node.type === graph.NodeType.SERIES) { // If expanded, we're showing the background rect, which we want to // appear gray. Otherwise we're showing a stack of ellipses which we // want to show white. @@ -6933,23 +7225,23 @@ var tf; var id = renderInfo.node.name; var escapedId = tf.escapeQuerySelector(id); var gradientDefs = d3.select("svg#svg defs #linearGradients"); - var linearGradient = gradientDefs.select("linearGradient#" + escapedId); + var linearGradient_1 = gradientDefs.select("linearGradient#" + escapedId); // If the linear gradient is not there yet, create it. - if (linearGradient.size() === 0) { - linearGradient = gradientDefs.append("linearGradient").attr("id", id); + if (linearGradient_1.size() === 0) { + linearGradient_1 = gradientDefs.append("linearGradient").attr("id", id); // Re-create the stops of the linear gradient. - linearGradient.selectAll("*").remove(); - var cumulativeProportion = 0; + linearGradient_1.selectAll("*").remove(); + var cumulativeProportion_1 = 0; // For each device, create a stop using the proportion of that device. _.each(renderInfo.deviceColors, function (d) { var color = d.color; - linearGradient.append("stop") - .attr("offset", cumulativeProportion) + linearGradient_1.append("stop") + .attr("offset", cumulativeProportion_1) .attr("stop-color", color); - linearGradient.append("stop") - .attr("offset", cumulativeProportion + d.proportion) + linearGradient_1.append("stop") + .attr("offset", cumulativeProportion_1 + d.proportion) .attr("stop-color", color); - cumulativeProportion += d.proportion; + cumulativeProportion_1 += d.proportion; }); } return isExpanded ? colorParams.EXPANDED_COLOR : "url(#" + escapedId + ")"; @@ -6970,10 +7262,10 @@ var tf; * Modify node style by toggling class and assign attributes (only for things * that can't be done in css). */ - function stylize(nodeGroup, renderInfo, sceneBehavior, nodeClass) { + function stylize(nodeGroup, renderInfo, sceneElement, nodeClass) { nodeClass = nodeClass || scene.Class.Node.SHAPE; - var isHighlighted = sceneBehavior.isNodeHighlighted(renderInfo.node.name); - var isSelected = sceneBehavior.isNodeSelected(renderInfo.node.name); + var isHighlighted = sceneElement.isNodeHighlighted(renderInfo.node.name); + var isSelected = sceneElement.isNodeSelected(renderInfo.node.name); var isExtract = renderInfo.isInExtract || renderInfo.isOutExtract; var isExpanded = renderInfo.expanded; nodeGroup.classed("highlighted", isHighlighted); @@ -6983,7 +7275,7 @@ var tf; // Main node always exists here and it will be reached before subscene, // so d3 selection is fine here. var node = nodeGroup.select("." + nodeClass + " ." + scene.Class.Node.COLOR_TARGET); - var fillColor = getFillForNode(sceneBehavior.templateIndex, ColorBy[sceneBehavior.colorBy.toUpperCase()], renderInfo, isExpanded); + var fillColor = getFillForNode(sceneElement.templateIndex, ColorBy[sceneElement.colorBy.toUpperCase()], renderInfo, isExpanded); node.style("fill", fillColor); // Choose outline to be darker version of node color if the node is a single // color and is not selected. @@ -6997,7 +7289,7 @@ var tf; function getStrokeForFill(fill) { // If node is colored by a gradient, then use a dark gray outline. return fill.substring(0, 3) === "url" ? - tf.graph.render.MetanodeColors.GRADIENT_OUTLINE : + graph.render.MetanodeColors.GRADIENT_OUTLINE : d3.rgb(fill).darker().toString(); } node_1.getStrokeForFill = getStrokeForFill; @@ -7087,8 +7379,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="graph.ts" /> -/// <reference path="render.ts" /> var tf; (function (tf) { var graph; @@ -8186,7 +8476,6 @@ var tf; ::content .edge > path.edgeline { fill: none; - marker-end: url("#arrowhead"); stroke: #bbb; stroke-linecap: round; stroke-width: 0.75; @@ -8196,6 +8485,12 @@ var tf; marker-start: url("#ref-arrowhead"); } +/* Labels showing tensor shapes on edges */ +::content .edge > text { + font-size: 3.5px; + fill: #666; +} + ::content #arrowhead { fill: #bbb; } @@ -8259,8 +8554,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -/// <reference path="../../../../typings/tsd.d.ts" /> -/// <reference path="../common.ts" /> var tf; (function (tf) { var scene; @@ -8449,7 +8742,8 @@ var tf; downloadContext.clearRect(0, 0, _this.downloadCanvas.width, _this.downloadCanvas.height); downloadContext.drawImage(image, 0, 0, _this.downloadCanvas.width, _this.downloadCanvas.height); }; - image.src = "data:image/svg+xml;base64," + btoa(svgXml); + var blob = new Blob([svgXml], { type: "image/svg+xml;charset=utf-8" }); + image.src = URL.createObjectURL(blob); }; /** * Handles changes in zooming/panning. Should be called from the main svg @@ -8497,7 +8791,7 @@ var tf; } }; return Minimap; - })(); + }()); scene.Minimap = Minimap; })(scene = tf.scene || (tf.scene = {})); })(tf || (tf = {})); // close module tf.scene @@ -9184,6 +9478,12 @@ Polymer({ notify: true, observer: '_graphChanged' }, + basicGraph: Object, + hierarchyParams: Object, + progress: { + type: Object, + notify: true, + }, title: String, selectedNode: { type: String, @@ -9219,7 +9519,7 @@ Polymer({ _allowGraphSelect: { type: Boolean, value: true - } + }, }, observers: [ '_buildRenderHierarchy(graphHierarchy, _graphParams)' @@ -9278,6 +9578,7 @@ Polymer({ 'node-highlight': '_nodeHighlighted', 'node-unhighlight': '_nodeUnhighlighted', 'node-toggle-extract': '_nodeToggleExtract', + 'node-toggle-seriesgroup': '_nodeToggleSeriesGroup', // Annotations @@ -9327,6 +9628,10 @@ Polymer({ this.set('highlightedNode', null); }, _nodeToggleExpand: function(event) { + // Immediately select the node that is about to be expanded. + this._nodeSelected(event); + + // Compute the sub-hierarchy scene. var nodeName = event.detail.name; var renderNode = this.renderHierarchy.getRenderNodeByName(nodeName); // Op nodes are not expandable. @@ -9335,9 +9640,13 @@ Polymer({ } this.renderHierarchy.buildSubhierarchy(nodeName); renderNode.expanded = !renderNode.expanded; - this.querySelector('#scene').setNodeExpanded(renderNode); - // Also select the expanded node. - this._nodeSelected(event); + + // Expand the node with some delay so that the user can immediately see + // the visual effect of selecting that node, before the expansion is + // done. + this.async(function() { + this.querySelector('#scene').setNodeExpanded(renderNode); + }, 75); }, _nodeToggleExtract: function(event) { // Toggle the include setting of the specified node appropriately. @@ -9356,6 +9665,25 @@ Polymer({ // Rebuild the render hierarchy. this._buildRenderHierarchy(this.graphHierarchy, this._graphParams); }, + _nodeToggleSeriesGroup: function(event) { + // Toggle the group setting of the specified node appropriately. + var nodeName = event.detail.name; + tf.graph.toggleNodeSeriesGroup(this.hierarchyParams.seriesMap, nodeName); + + // Rebuild the render hierarchy with the updated series grouping map. + this.set('progress', { + value: 0, + msg: '' + }); + var tracker = tf.getTracker(this); + var hierarchyTracker = tf.getSubtaskTracker(tracker, 100, + 'Namespace hierarchy'); + tf.graph.hierarchy.build(this.basicGraph, this.hierarchyParams, hierarchyTracker) + .then(function(graphHierarchy) { + this.set('graphHierarchy', graphHierarchy); + this._buildRenderHierarchy(this.graphHierarchy, this._graphParams); + }.bind(this)); + }, not: function(x) { return !x; } @@ -9429,7 +9757,7 @@ Polymer({ /** * String indicating the type of coloring to use for this node, used - * only for deterimining the fill. + * only for determining the fill. */ colorBy: { type: Object, @@ -9437,7 +9765,7 @@ Polymer({ }, /** - * Function used by structural coloring algorithim to determine which + * Function used by structural coloring algorithm to determine which * color to use based on the template ID of the node. Optional. */ templateIndex: { @@ -9600,6 +9928,7 @@ Polymer({ font-size: 11pt; font-weight: 400; position: relative; + display: inline-block; } #list-item:hover { @@ -9611,7 +9940,6 @@ Polymer({ } #list-item span { - display: block; margin-left: 40px; } @@ -9619,6 +9947,13 @@ Polymer({ color: #999; } + #list-item span.edge-label { + float: right; + font-size: 10px; + margin-left: 3px; + margin-right: 5px; + } + .node-icon { position: absolute; top: 1px; @@ -9629,6 +9964,7 @@ Polymer({ <div id="list-item" on-mouseover="_nodeListener" on-mouseout="_nodeListener" on-click="_nodeListener"> <tf-graph-icon class="node-icon" height="12" color-by="[[colorBy]]" color-by-params="[[colorByParams]]" node="[[itemNode]]" render-info="[[itemRenderInfo]]" template-index="[[templateIndex]]"></tf-graph-icon> <span title$="[[name]]">[[name]]</span> + <span class="edge-label">[[edgeLabel]]</span> </div> </template> @@ -9648,6 +9984,8 @@ Polymer({ * @type {tf.graph.Node} */ itemNode: Object, + /** The edge label associated with this item. */ + edgeLabel: String, /** * The render node information for the item node. Used by the graph * icon in determining fill color. @@ -9660,7 +9998,7 @@ Polymer({ }, colorBy: String, colorByParams: Object, - templateIndex: Function, + templateIndex: Function }, _itemTypeChanged: function() { @@ -9679,7 +10017,6 @@ Polymer({ type: this.itemType }); } - }); })(); </script> @@ -9793,7 +10130,7 @@ Polymer({ <div> <paper-icon-button icon="{{_getToggleIcon(_expanded)}}" on-click="_toggleExpanded" class="toggle-button"> </paper-icon-button> - <div class="node-name">[[_getNodeName(nodeName)]]</div> + <div class="node-name" id="nodetitle"></div> </div> <div secondary=""> <tf-graph-icon class="node-icon" node="[[_node]]" render-info="[[_getRenderInfo(nodeName, renderHierarchy)]]" color-by="[[colorBy]]" template-index="[[_templateIndex]]"></tf-graph-icon> @@ -9840,7 +10177,7 @@ Polymer({ (<span>[[_totalPredecessors]]</span>) <iron-list class="sub-list" id="inputsList" items="[[_predecessors.regular]]"> <template> - <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]"> + <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" edge-label="[[_getPredEdgeLabel(item)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]"> </tf-node-list-item> </template> </iron-list> @@ -9870,7 +10207,7 @@ Polymer({ (<span>[[_totalSuccessors]]</span>) <iron-list class="sub-list" id="outputsList" items="[[_successors.regular]]"> <template> - <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="successor" color-by="[[colorBy]]" template-index="[[_templateIndex]]"> + <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" edge-label="[[_getSuccEdgeLabel(item)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="successor" color-by="[[colorBy]]" template-index="[[_templateIndex]]"> </tf-node-list-item> </template> </iron-list> @@ -9899,6 +10236,13 @@ Polymer({ <span>[[_auxButtonText]]</span> </paper-button> </div> + <template is="dom-if" if="{{_isInSeries(_node)}}"> + <div class="toggle-include-group"> + <paper-button raised="" class="toggle-include" on-click="_toggleGroup"> + <span>[[_groupButtonText]]</span> + </paper-button> + </div> + </template> </div> </template> </iron-collapse> @@ -9969,7 +10313,8 @@ Polymer({ type: Boolean, value: false }, - _auxButtonText: String + _auxButtonText: String, + _groupButtonText: String }, expandNode: function() { this.fire('_node.expand', this.node); @@ -9980,20 +10325,60 @@ Polymer({ _getNode: function(nodeName, graphHierarchy) { return graphHierarchy.node(nodeName); }, - _getNodeName: function(nodeName) { - // Insert a zero-width whitespace character before each slash so that + _getPrintableHTMLNodeName: function(nodeName) { + // Insert an optional line break before each slash so that // long node names wrap cleanly at path boundaries. - return (nodeName || '').replace(/\//g, '\u200B/'); + return (nodeName || '').replace(/\//g, '<wbr>/'); + }, + _getPredEdgeLabel: function(sourceName) { + return this._getEdgeLabel(sourceName, this.nodeName); + }, + _getSuccEdgeLabel: function(destName) { + return this._getEdgeLabel(this.nodeName, destName); + }, + _getEdgeLabel: function(sourceName, destName) { + if (!this._node) { + // The user clicked outside, thus no node is selected and + // the info card should be hidden. + return; + } + var parent = this._node.parentNode; + var sourceNode = this.graphHierarchy.node(sourceName); + if (!sourceNode.isGroupNode) { + // Show the tensor shape directly. + return tf.graph.scene.edge.getShapeLabelFromNode(sourceNode); + } + sourceName = this.renderHierarchy.getNearestVisibleAncestor(sourceName); + destName = this.renderHierarchy.getNearestVisibleAncestor(destName); + var metaedge = parent.metagraph.edge(sourceName, destName) || + parent.bridgegraph.edge(sourceName, destName); + return tf.graph.scene.edge.getLabelForEdge(metaedge, + this.renderHierarchy); }, _getRenderInfo: function(nodeName, renderHierarchy) { return this.renderHierarchy.getOrCreateRenderNodeByName(nodeName); }, _getAttributes: function(node) { this.async(this._resizeList.bind(this, "#attributesList")); - return node && node.attr ? node.attr.map(function(entry) { - return {key: entry.key, value: JSON.stringify(entry.value)}; - }) : []; - + if (!node || !node.attr) { + return []; + } + var attrs = []; + _.each(node.attr, function(entry) { + // Unpack the "too large" attributes into separate attributes + // in the info card, with values "too large to show". + if (entry.key === tf.graph.LARGE_ATTRS_KEY) { + attrs = attrs.concat(entry.value.list.s.map(function(key) { + return {key: key, value: "Too large to show..."}; + })); + } else { + attrs.push({ + key: entry.key, + value: JSON.stringify(entry.value) + }); + } + }); + return attrs; }, _getDevice: function(node) { return node ? node.device : null; @@ -10030,6 +10415,14 @@ Polymer({ _resetState: function() { this._openedControlPred = false; this._openedControlSucc = false; + + this.set("_groupButtonText", + tf.graph.scene.node.getGroupSettingLabel(this._node)); + + if (this._node) { + Polymer.dom(this.$.nodetitle).innerHTML = + this._getPrintableHTMLNodeName(this._node.name); + } }, _resizeList: function(selector) { var list = document.querySelector(selector); @@ -10046,6 +10439,14 @@ Polymer({ _nodeIncludeStateChanged: function(include, oldInclude) { this.set("_auxButtonText", tf.graph.getIncludeNodeButtonString(include)); + }, + _toggleGroup: function() { + var graphElem = document.querySelector("#graph"); + var seriesName = tf.graph.scene.node.getSeriesName(this._node); + graphElem.fire("node-toggle-seriesgroup", { name: seriesName }); + }, + _isInSeries: function(node) { + return tf.graph.scene.node.canBeInSeries(node); } }); })(); @@ -10222,7 +10623,7 @@ paper-progress { </template> <div class$="[[_getContainerClass(progress)]]"> <div id="main"> - <tf-graph id="graph" graph-hierarchy="[[graphHierarchy]]" render-hierarchy="{{_renderHierarchy}}" selected-node="{{_selectedNode}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="{{colorByParams}}" graph-name="[[graphName]]" progress="[[progress]]"></tf-graph> + <tf-graph id="graph" graph-hierarchy="{{graphHierarchy}}" basic-graph="[[graph]]" hierarchy-params="[[hierarchyParams]]" render-hierarchy="{{_renderHierarchy}}" selected-node="{{_selectedNode}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="{{colorByParams}}" graph-name="[[graphName]]" progress="{{progress}}"></tf-graph> </div> <div id="info"> <tf-graph-info id="graph-info" title="selected" graph-hierarchy="[[graphHierarchy]]" render-hierarchy="[[_renderHierarchy]]" graph="[[graph]]" selected-node="{{_selectedNode}}" selected-node-include="{{_selectedNodeInclude}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="[[colorByParams]]"></tf-graph-info> @@ -10806,10 +11207,10 @@ function convertToHumanReadable(value, units, unitIndex) { <tf-dashboard-layout> <div class="sidebar"> <tf-graph-controls id="controls" color-by-params="[[_colorByParams]]" has-stats="[[_hasStats]]" color-by="{{_colorBy}}" ,="" datasets="[[_datasets]]" selected-dataset="{{_selectedDataset}}" selected-file="{{_selectedFile}}"></tf-graph-controls> - <tf-graph-loader id="loader" datasets="[[_datasets]]" ,="" selected-dataset="[[_selectedDataset]]" selected-file="[[_selectedFile]]" out-graph-hierarchy="{{_graphHierarchy}}" out-graph="{{_graph}}" out-graph-name="{{_graphName}}" has-stats="{{_hasStats}}" progress="{{_progress}}"></tf-graph-loader> + <tf-graph-loader id="loader" datasets="[[_datasets]]" ,="" selected-dataset="[[_selectedDataset]]" selected-file="[[_selectedFile]]" out-graph-hierarchy="{{_graphHierarchy}}" out-graph="{{_graph}}" out-graph-name="{{_graphName}}" has-stats="{{_hasStats}}" progress="{{_progress}}" out-hierarchy-params="{{_hierarchyParams}}"></tf-graph-loader> </div> <div class="center"> - <tf-graph-board id="graphboard" graph-hierarchy="[[_graphHierarchy]]" graph="[[_graph]]" has-stats="[[_hasStats]]" graph-name="[[_graphName]]" progress="[[_progress]]" color-by="[[_colorBy]]" color-by-params="{{_colorByParams}}"> + <tf-graph-board id="graphboard" graph-hierarchy="[[_graphHierarchy]]" graph="[[_graph]]" has-stats="[[_hasStats]]" graph-name="[[_graphName]]" progress="[[_progress]]" color-by="[[_colorBy]]" color-by-params="{{_colorByParams}}" hierarchy-params="[[_hierarchyParams]]"> </tf-graph-board> </div> </tf-dashboard-layout> @@ -10849,7 +11250,8 @@ Polymer({ return _.map(runsWithGraph, function(runName) { return { name: runName, - path: graphUrlGen(runName) + path: graphUrlGen(runName, tf.graph.LIMIT_ATTR_SIZE, + tf.graph.LARGE_ATTRS_KEY) }; }); }, @@ -10865,11 +11267,11 @@ Polymer({ <paper-toolbar id="toolbar"> <div id="toolbar-content"> <div class="toolbar-title">TensorBoard</div> - <paper-tabs selected="0" noink="" class="tabs" id="tabs"> - <paper-tab data-mode="events" on-click="changeMode">Events</paper-tab> - <paper-tab data-mode="images" on-click="changeMode">Images</paper-tab> - <paper-tab data-mode="graphs" on-click="changeMode">Graph</paper-tab> - <paper-tab data-mode="histograms" on-click="changeMode">Histograms</paper-tab> + <paper-tabs selected="{{modeIndex}}" noink="" class="tabs" id="tabs"> + <paper-tab data-mode="events">Events</paper-tab> + <paper-tab data-mode="images">Images</paper-tab> + <paper-tab data-mode="graphs">Graph</paper-tab> + <paper-tab data-mode="histograms">Histograms</paper-tab> </paper-tabs> </div> </paper-toolbar> @@ -10944,14 +11346,24 @@ Polymer({ type: Object, value: TF.Urls.productionRouter(), }, + // Which tab is selected (events, graph, images etc). mode: { type: String, - value: "events", + computed: '_getModeFromIndex(modeIndex)' }, + // If true, tab switching in TensorBoard will not update + // location hash. Hash update interferes with selenium tests. + noHash: { + type: Boolean, + value: false + } }, - changeMode: function(ev) { - var mode = ev.target.parentElement.getAttribute('data-mode'); - this._changeMode(mode, true); + _getModeFromIndex: function(modeIndex) { + var mode = this.tabs[modeIndex]; + if (!this.noHash) { + window.location.hash = mode; + } + return mode; }, eventDashboard: function(mode) { return mode === "events"; @@ -10965,36 +11377,26 @@ Polymer({ histogramDashboard: function(mode) { return mode === "histograms"; }, - loadPreviousMode: function() { - this._changeMode(this._getMode(), false); - }, ready: function() { - this._changeMode(this._getMode(), true); - - var self = this; - window.addEventListener('hashchange', function(){ - self.loadPreviousMode(); + this.tabs = [].slice.call(this.querySelectorAll('paper-tab')).map(function(a) { + return a.dataset.mode; }); + this._getModeFromHash(); + window.addEventListener('hashchange', function() { + this._getModeFromHash(); + }.bind(this)); }, - _changeMode: function(mode, isNewState) { - this.mode = mode; - - // Change the selected tab - this.$.tabs.selected = this._tabs().indexOf(mode); - - if (isNewState){ - window.location.hash = mode; - } - }, - _getMode: function() { + _getModeFromHash: function() { // Return the mode as it is stored in the hash. - // If no mode can be found, default to the first tab. - var hash = window.location.hash; - return hash.length > 0 ? hash.slice(1, hash.length) : this._tabs()[0]; - }, - _tabs: function() { - var elts = Array.prototype.slice.call(this.querySelectorAll('paper-tab')); - return elts.map(function(elt){ return elt.getAttribute('data-mode')}); + var tabName = window.location.hash.trim().slice(1); + var modeIndex = this.tabs.indexOf(tabName); + if (modeIndex == -1 && this.modeIndex == null) { + // Selecting the first tab as default. + this.set('modeIndex', 0); + } + if (modeIndex != -1 && modeIndex != this.modeIndex) { + this.set('modeIndex', modeIndex); + } }, }); </script> |