diff --git a/WORKSPACE b/WORKSPACE index 6fb8f8794e5..b6dbc7463f2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -91,7 +91,7 @@ new_git_repository( name = "iron_behaviors", build_file = "bower.BUILD", remote = "https://github.com/polymerelements/iron-behaviors.git", - tag = "v1.0.16", + tag = "v1.0.17", ) new_git_repository( @@ -143,13 +143,6 @@ new_git_repository( tag = "v1.0.8", ) -new_git_repository( - name = "iron_icons", - build_file = "bower.BUILD", - remote = "https://github.com/polymerelements/iron-icons.git", - tag = "v1.1.3", -) - new_git_repository( name = "iron_iconset_svg", build_file = "bower.BUILD", diff --git a/bower.BUILD b/bower.BUILD index 9f4994944fc..cb3309a36e2 100644 --- a/bower.BUILD +++ b/bower.BUILD @@ -142,24 +142,6 @@ filegroup( ], ) -filegroup( - name = "iron_icons", - srcs = [ - "av-icons.html", - "communication-icons.html", - "device-icons.html", - "editor-icons.html", - "hardware-icons.html", - "image-icons.html", - "index.html", - "iron-icons.html", - "maps-icons.html", - "notification-icons.html", - "places-icons.html", - "social-icons.html", - ], -) - filegroup( name = "iron_iconset_svg", srcs = [ diff --git a/tensorflow/tensorboard/TAG b/tensorflow/tensorboard/TAG index a45fd52cc58..7273c0fa8c5 100644 --- a/tensorflow/tensorboard/TAG +++ b/tensorflow/tensorboard/TAG @@ -1 +1 @@ -24 +25 diff --git a/tensorflow/tensorboard/bower.json b/tensorflow/tensorboard/bower.json index 27ca5dc6193..20095d92750 100644 --- a/tensorflow/tensorboard/bower.json +++ b/tensorflow/tensorboard/bower.json @@ -10,7 +10,6 @@ "iron-flex-layout", "iron-form-element-behavior", "iron-icon", - "iron-icons", "iron-iconset-svg", "iron-input", "iron-menu-behavior", @@ -41,7 +40,7 @@ "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#1.1.2", "iron-ajax": "PolymerElements/iron-ajax#1.2.0", "iron-autogrow-textarea": "PolymerElements/iron-autogrow-textarea#1.0.12", - "iron-behaviors": "PolymerElements/iron-behaviors#1.0.16", + "iron-behaviors": "PolymerElements/iron-behaviors#1.0.17", "iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#1.0.4", "iron-collapse": "PolymerElements/iron-collapse#1.0.8", "iron-dropdown": "PolymerElements/iron-dropdown#1.4.0", @@ -49,7 +48,6 @@ "iron-flex-layout": "PolymerElements/iron-flex-layout#1.3.0", "iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#1.0.6", "iron-icon": "PolymerElements/iron-icon#1.0.8", - "iron-icons": "PolymerElements/iron-icons#1.1.3", "iron-iconset-svg": "PolymerElements/iron-iconset-svg#1.0.9", "iron-input": "PolymerElements/iron-input#1.0.10", "iron-list": "PolymerElements/iron-list#1.1.7", @@ -117,7 +115,7 @@ "iron-a11y-keys-behavior": "1.1.2", "iron-ajax": "1.2.0", "iron-autogrow-textarea": "1.0.12", - "iron-behaviors": "1.0.16", + "iron-behaviors": "1.0.17", "iron-checked-element-behavior": "1.0.4", "iron-collapse": "1.0.8", "iron-dropdown": "1.4.0", diff --git a/tensorflow/tensorboard/bower/BUILD b/tensorflow/tensorboard/bower/BUILD index 90d9910205b..4b43b558443 100644 --- a/tensorflow/tensorboard/bower/BUILD +++ b/tensorflow/tensorboard/bower/BUILD @@ -22,7 +22,6 @@ filegroup( "@iron_flex_layout//:iron_flex_layout", "@iron_form_element_behavior//:iron_form_element_behavior", "@iron_icon//:iron_icon", - "@iron_icons//:iron_icons", "@iron_iconset_svg//:iron_iconset_svg", "@iron_input//:iron_input", "@iron_list//:iron_list", diff --git a/tensorflow/tensorboard/dist/tf-tensorboard.html b/tensorflow/tensorboard/dist/tf-tensorboard.html index effefbd572c..97f9fbbc7f1 100644 --- a/tensorflow/tensorboard/dist/tf-tensorboard.html +++ b/tensorflow/tensorboard/dist/tf-tensorboard.html @@ -99,7 +99,7 @@ var TF; var Globals; (function (Globals) { // The names of TensorBoard tabs. - Globals.TABS = ['events', 'images', 'audio', 'graphs', 'distributions']; + Globals.TABS = ['events', 'images', 'audio', 'graphs', 'distributions', 'histograms']; // If true, TensorBoard stores its hash in the URI state. // If false, tab switching in TensorBoard will not update location hash, // because hash updates interfere with wct_tests. @@ -1681,19 +1681,23 @@ var Categorizer; position: relative; } - .card .card-title { + .card .card-title, .card .card-subtitle { flex-grow: 0; flex-shrink: 0; - margin-bottom: 10px; font-size: 14px; text-overflow: ellipsis; overflow: hidden; } + .card .card-subtitle { + font-size: 12px; + } + .card .card-content { flex-grow: 1; flex-shrink: 1; display: flex; + margin-top: 10px; } .card .card-bottom-row { position: absolute; @@ -2361,11 +2365,15 @@ var TF; var url = this.router.histograms(tag, run); p = this.requestManager.request(url); return p.then(map(detupler(createHistogram))).then(function (histos) { + // Get the minimum and maximum values across all histograms so that the + // visualization is aligned for all timesteps. + var min = d3.min(histos, function (d) { return d.min; }); + var max = d3.max(histos, function (d) { return d.max; }); return histos.map(function (histo, i) { return { wall_time: histo.wall_time, step: histo.step, - bins: convertBins(histo) + bins: convertBins(histo, min, max) }; }); }); @@ -2427,13 +2435,73 @@ var TF; }()); Backend_1.Backend = Backend; /** Given a RunToTag, return sorted array of all runs */ - function getRuns(r) { return _.keys(r).sort(); } + function getRuns(r) { + return _.keys(r).sort(compareTagNames); + } Backend_1.getRuns = getRuns; /** Given a RunToTag, return array of all tags (sorted + dedup'd) */ function getTags(r) { - return _.union.apply(null, _.values(r)).sort(); + return _.union.apply(null, _.values(r)).sort(compareTagNames); } Backend_1.getTags = getTags; + /** Compares tag names asciinumerically broken into components. */ + function compareTagNames(a, b) { + var ai = 0; + var bi = 0; + while (true) { + if (ai === a.length) + return bi === b.length ? 0 : -1; + if (bi === b.length) + return 1; + if (isDigit(a[ai]) && isDigit(b[bi])) { + var ais = ai; + var bis = bi; + ai = consumeNumber(a, ai + 1); + bi = consumeNumber(b, bi + 1); + var an = parseFloat(a.slice(ais, ai)); + var bn = parseFloat(b.slice(bis, bi)); + if (an < bn) + return -1; + if (an > bn) + return 1; + continue; + } + if (isBreak(a[ai])) { + if (!isBreak(b[bi])) + return -1; + } + else if (isBreak(b[bi])) { + return 1; + } + else if (a[ai] < b[bi]) { + return -1; + } + else if (a[ai] > b[bi]) { + return 1; + } + ai++; + bi++; + } + } + Backend_1.compareTagNames = compareTagNames; + function consumeNumber(s, i) { + var decimal = false; + for (; i < s.length; i++) { + if (isDigit(s[i])) + continue; + if (!decimal && s[i] === '.') { + decimal = true; + continue; + } + break; + } + return i; + } + function isDigit(c) { return '0' <= c && c <= '9'; } + function isBreak(c) { + // TODO(jart): Remove underscore when people stop using it like a slash. + return c === '/' || c === '_' || isDigit(c); + } /** * Given a RunToTag and an array of runs, return every tag that appears for * at least one run. @@ -2486,28 +2554,53 @@ var TF; * Takes histogram data as stored by tensorboard backend and converts it to * the standard d3 histogram data format to make it more compatible and easier * to visualize. When visualizing histograms, having the left edge and width - * makes things quite a bit easier. + * makes things quite a bit easier. The bins are also converted to have an + * uniform width, what makes the visualization easier to understand. * * @param histogram A histogram from tensorboard backend. + * @param min The leftmost edge. The binning will start on it. + * @param max The rightmost edge. The binning will end on it. + * @param numBins The number of bins of the converted data. The default of 30 + * is a sensible default, using more starts to get artifacts because the event + * data is stored in buckets, and you start being able to see the aliased + * borders between each bucket. * @return A histogram bin. Each bin has an x (left edge), a dx (width), * and a y (count). * * If given rightedges are inclusive, then these left edges (x) are exclusive. */ - function convertBins(histogram) { + function convertBins(histogram, min, max, numBins) { + if (numBins === void 0) { numBins = 30; } if (histogram.bucketRightEdges.length !== histogram.bucketCounts.length) { throw (new Error('Edges and counts are of different lengths.')); } - var previousRightEdge = histogram.min; - return histogram.bucketRightEdges.map(function (rightEdge, i) { - // Use the previous bin's rightEdge as the new leftEdge - var left = previousRightEdge; - // We need to clip the rightEdge because right-most edge can be - // infinite-sized - var right = Math.min(histogram.max, rightEdge); - // Store rightEdgeValue for next iteration - previousRightEdge = rightEdge; - return { x: left, dx: right - left, y: histogram.bucketCounts[i] }; + var binWidth = (max - min) / numBins; + var bucketLeft = min; // Use the min as the starting point for the bins. + var bucketPos = 0; + return d3.range(min, max, binWidth).map(function (binLeft) { + var binRight = binLeft + binWidth; + // Take the count of each existing bucket, multiply it by the proportion + // of overlap with the new bin, then sum and store as the count for the + // new bin. If no overlap, will add to zero, if 100% overlap, will include + // the full count into new bin. + var binY = 0; + while (bucketPos < histogram.bucketRightEdges.length) { + // Clip the right edge because right-most edge can be infinite-sized. + var bucketRight = Math.min(max, histogram.bucketRightEdges[bucketPos]); + var intersect = Math.min(bucketRight, binRight) - Math.max(bucketLeft, binLeft); + var count = (intersect / (bucketRight - bucketLeft)) * + histogram.bucketCounts[bucketPos]; + binY += intersect > 0 ? count : 0; + // If bucketRight is bigger than binRight, than this bin is finished and + // there is data for the next bin, so don't increment bucketPos. + if (bucketRight > binRight) { + break; + } + bucketLeft = Math.max(min, bucketRight); + bucketPos++; + } + ; + return { x: binLeft, dx: binWidth, y: binY }; }); } Backend_1.convertBins = convertBins; @@ -2917,10 +3010,11 @@ var TF; selectedRuns: Array, xType: String, dataProvider: Function, - _initialized: Boolean, + _attached: Boolean, + _makeChartAsyncCallbackId: { type: Number, value: null } }, observers: [ - "_makeChart(tag, dataProvider, xType, colorScale, _initialized)", + "_makeChart(tag, dataProvider, xType, colorScale, _attached)", "_changeRuns(_chart, selectedRuns.*)" ], _changeRuns: function(chart) { @@ -2936,23 +3030,26 @@ var TF; reload: function() { this._chart.reload(); }, - _makeChart: function(tag, dataProvider, xType, colorScale, _initialized) { - if (!_initialized) { - return; + _makeChart: function(tag, dataProvider, xType, colorScale, _attached) { + if (this._makeChartAsyncCallbackId === null) { + this.cancelAsync(this._makeChartAsyncCallbackId); } - if (this._chart) this._chart.destroy(); - var chart = new TF.DistributionChart(tag, dataProvider, xType, colorScale); - var svg = d3.select(this.$.chartsvg); - this.async(function() { + + this._makeChartAsyncCallbackId = this.async(function() { + this._makeChartAsyncCallbackId = null; + if (!_attached) return; + if (this._chart) this._chart.destroy(); + var chart = new TF.DistributionChart(tag, dataProvider, xType, colorScale); + var svg = d3.select(this.$.chartsvg); chart.renderTo(svg); this._chart = chart; }, 350); }, attached: function() { - this._initialized = true; + this._attached = true; }, detached: function() { - this._initialized = false; + this._attached = false; } }); @@ -3071,6 +3168,140 @@ var TF; + + + + + +