diff --git a/WORKSPACE b/WORKSPACE index 60b69a9c1a7..981ac77ea46 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -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 b1bd38b62a0..8351c19397f 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 3fcd43c7d54..492a5d45cee 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. ==============================================================================*/ -/// -/// 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 = {})); - @@ -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. ==============================================================================*/ -/// -/// 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. ==============================================================================*/ -/// 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 = {})); - @@ -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. ==============================================================================*/ -/// var tf; (function (tf) { /** @@ -2709,6 +2712,40 @@ var tf; return result; } 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 @@ -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. ==============================================================================*/ -/// -/// 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 @@ -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. ==============================================================================*/ -/// -/// 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. ==============================================================================*/ -/// -/// /** * 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 - @@ -9793,7 +10130,7 @@ Polymer({
-
[[_getNodeName(nodeName)]]
+
@@ -9840,7 +10177,7 @@ Polymer({ ([[_totalPredecessors]]) @@ -9870,7 +10207,7 @@ Polymer({ ([[_totalSuccessors]]) @@ -9899,6 +10236,13 @@ Polymer({ [[_auxButtonText]]
+ @@ -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, '/'); + }, + _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 {
- +
@@ -10806,10 +11207,10 @@ function convertToHumanReadable(value, units, unitIndex) {
- +
@@ -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({
TensorBoard
- - Events - Images - Graph - Histograms + + Events + Images + Graph + Histograms
@@ -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); + } }, });