aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/tensorboard/components/tf_graph_common/lib/scene/scene.ts
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/tensorboard/components/tf_graph_common/lib/scene/scene.ts')
-rw-r--r--tensorflow/tensorboard/components/tf_graph_common/lib/scene/scene.ts280
1 files changed, 188 insertions, 92 deletions
diff --git a/tensorflow/tensorboard/components/tf_graph_common/lib/scene/scene.ts b/tensorflow/tensorboard/components/tf_graph_common/lib/scene/scene.ts
index f8ad90a968..518d89e82a 100644
--- a/tensorflow/tensorboard/components/tf_graph_common/lib/scene/scene.ts
+++ b/tensorflow/tensorboard/components/tf_graph_common/lib/scene/scene.ts
@@ -13,102 +13,121 @@ See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
module tf.graph.scene {
+ const svgNamespace = 'http://www.w3.org/2000/svg';
-/** Enums element class of objects in the scene */
-export let Class = {
- Node: {
- // <g> element that contains nodes.
- CONTAINER: 'nodes',
- // <g> element that contains detail about a node.
- GROUP: 'node',
- // <g> element that contains visual elements (like rect, ellipse).
- SHAPE: 'nodeshape',
- // <*> element(s) under SHAPE that should receive color updates.
- COLOR_TARGET: 'nodecolortarget',
- // <text> element showing the node's label.
- LABEL: 'nodelabel',
- // <g> element that contains all visuals for the expand/collapse
- // button for expandable group nodes.
- BUTTON_CONTAINER: 'buttoncontainer',
- // <circle> element that surrounds expand/collapse buttons.
- BUTTON_CIRCLE: 'buttoncircle',
- // <path> element of the expand button.
- EXPAND_BUTTON: 'expandbutton',
- // <path> element of the collapse button.
- COLLAPSE_BUTTON: 'collapsebutton'
- },
- Edge: {
- CONTAINER: 'edges',
- GROUP: 'edge',
- LINE: 'edgeline',
- REF_LINE: 'refline',
- STRUCTURAL: 'structural'
- },
- Annotation: {
- OUTBOX: 'out-annotations',
- INBOX: 'in-annotations',
- GROUP: 'annotation',
- NODE: 'annotation-node',
- EDGE: 'annotation-edge',
- CONTROL_EDGE: 'annotation-control-edge',
- LABEL: 'annotation-label',
- ELLIPSIS: 'annotation-ellipsis'
- },
- Scene: {
- GROUP: 'scene',
- CORE: 'core',
- INEXTRACT: 'in-extract',
- OUTEXTRACT: 'out-extract'
- },
- Subscene: {GROUP: 'subscene'},
- OPNODE: 'op',
- METANODE: 'meta',
- SERIESNODE: 'series',
- BRIDGENODE: 'bridge',
- ELLIPSISNODE: 'ellipsis'
-};
+ /** Enums element class of objects in the scene */
+ export let Class = {
+ Node: {
+ // <g> element that contains nodes.
+ CONTAINER: 'nodes',
+ // <g> element that contains detail about a node.
+ GROUP: 'node',
+ // <g> element that contains visual elements (like rect, ellipse).
+ SHAPE: 'nodeshape',
+ // <*> element(s) under SHAPE that should receive color updates.
+ COLOR_TARGET: 'nodecolortarget',
+ // <text> element showing the node's label.
+ LABEL: 'nodelabel',
+ // <g> element that contains all visuals for the expand/collapse
+ // button for expandable group nodes.
+ BUTTON_CONTAINER: 'buttoncontainer',
+ // <circle> element that surrounds expand/collapse buttons.
+ BUTTON_CIRCLE: 'buttoncircle',
+ // <path> element of the expand button.
+ EXPAND_BUTTON: 'expandbutton',
+ // <path> element of the collapse button.
+ COLLAPSE_BUTTON: 'collapsebutton'
+ },
+ Edge: {
+ CONTAINER: 'edges',
+ GROUP: 'edge',
+ LINE: 'edgeline',
+ REF_LINE: 'refline',
+ STRUCTURAL: 'structural'
+ },
+ Annotation: {
+ OUTBOX: 'out-annotations',
+ INBOX: 'in-annotations',
+ GROUP: 'annotation',
+ NODE: 'annotation-node',
+ EDGE: 'annotation-edge',
+ CONTROL_EDGE: 'annotation-control-edge',
+ LABEL: 'annotation-label',
+ ELLIPSIS: 'annotation-ellipsis'
+ },
+ Scene: {
+ GROUP: 'scene',
+ CORE: 'core',
+ INEXTRACT: 'in-extract',
+ OUTEXTRACT: 'out-extract'
+ },
+ Subscene: {GROUP: 'subscene'},
+ OPNODE: 'op',
+ METANODE: 'meta',
+ SERIESNODE: 'series',
+ BRIDGENODE: 'bridge',
+ ELLIPSISNODE: 'ellipsis'
+ };
-/**
- * Helper method for fitting the graph in the svg view.
- *
- * @param svg The main svg.
- * @param zoomG The svg group used for panning and zooming.
- * @param d3zoom The zoom behavior.
- * @param callback Called when the fitting is done.
- */
-export function fit(svg, zoomG, d3zoom, callback) {
- let svgRect = svg.getBoundingClientRect();
- let sceneSize = null;
- try {
- sceneSize = zoomG.getBBox();
- if (sceneSize.width === 0) {
- // There is no scene anymore. We have been detached from the dom.
+ /**
+ * A health pill encapsulates an overview of tensor element values. The value
+ * field is a list of 12 numbers that shed light on the status of the tensor.
+ * Visualized in health pills are the 3rd through 8th (inclusive) numbers of
+ * health pill values. Those 6 numbers are counts of tensor elements that fall
+ * under -Inf, negative, 0, positive, +Inf, NaN (in that order).
+ *
+ * Please keep this interface consistent with HealthPillDatum within
+ * backend.ts.
+ */
+ export interface HealthPill {
+ node_name: string;
+ output_slot: number;
+ value: number[];
+ wall_time: number;
+ step: number;
+ }
+ ;
+
+ /**
+ * Helper method for fitting the graph in the svg view.
+ *
+ * @param svg The main svg.
+ * @param zoomG The svg group used for panning and zooming.
+ * @param d3zoom The zoom behavior.
+ * @param callback Called when the fitting is done.
+ */
+ export function fit(svg, zoomG, d3zoom, callback) {
+ let svgRect = svg.getBoundingClientRect();
+ let sceneSize = null;
+ try {
+ sceneSize = zoomG.getBBox();
+ if (sceneSize.width === 0) {
+ // There is no scene anymore. We have been detached from the dom.
+ return;
+ }
+ } catch (e) {
+ // Firefox produced NS_ERROR_FAILURE if we have been
+ // detached from the dom.
return;
}
- } catch (e) {
- // Firefox produced NS_ERROR_FAILURE if we have been
- // detached from the dom.
- return;
- }
- let scale = 0.9 * Math.min(
- svgRect.width / sceneSize.width,
- svgRect.height / sceneSize.height,
- 2
- );
- let params = layout.PARAMS.graph;
- let zoomEvent =
- d3zoom.scale(scale)
- .on('zoomend.fitted',
- () => {
- // Remove the listener for the zoomend event,
- // so we don't get called at the end of regular zoom events,
- // just those that fit the graph to screen.
- d3zoom.on('zoomend.fitted', null);
- callback();
- })
- .translate([params.padding.paddingLeft, params.padding.paddingTop])
- .event;
- d3.select(zoomG).transition().duration(500).call(zoomEvent);
+ let scale = 0.9 *
+ Math.min(
+ svgRect.width / sceneSize.width, svgRect.height / sceneSize.height,
+ 2);
+ let params = layout.PARAMS.graph;
+ let zoomEvent =
+ d3zoom.scale(scale)
+ .on('zoomend.fitted',
+ () => {
+ // Remove the listener for the zoomend event,
+ // so we don't get called at the end of regular zoom events,
+ // just those that fit the graph to screen.
+ d3zoom.on('zoomend.fitted', null);
+ callback();
+ })
+ .translate([params.padding.paddingLeft, params.padding.paddingTop])
+ .event;
+ d3.select(zoomG).transition().duration(500).call(zoomEvent);
};
/**
@@ -450,4 +469,81 @@ export function positionEllipse(ellipse, cx: number, cy: number,
});
};
+/**
+ * Renders a health pill for an op atop a node.
+ */
+function _addHealthPill(
+ nodeGroupElement: SVGElement, healthPills: HealthPill[],
+ nodeInfo: render.RenderGroupNodeInfo, colors: string[]) {
+ // Check if text already exists at location.
+ d3.select(nodeGroupElement.parentNode)
+ .selectAll('.health-pill-group')
+ .remove();
+
+ if (!nodeInfo || !healthPills || !healthPills.length) {
+ return;
+ }
+
+ let lastHealthPillData = healthPills[healthPills.length - 1].value;
+
+ // For now, we only visualize the 6 values that summarize counts of tensor
+ // elements of various categories: -Inf, negative, 0, positive, Inf, and NaN.
+ let lastHealthPillOverview = lastHealthPillData.slice(2, 8);
+
+ let healthPillWidth = 60;
+ let healthPillHeight = 10;
+ let healthPill = document.createElementNS(svgNamespace, 'svg');
+ healthPill.classList.add('health-pill-group');
+ healthPill.setAttribute('width', String(healthPillWidth));
+ healthPill.setAttribute('height', String(healthPillHeight));
+
+ let totalCount = lastHealthPillData[1];
+ // Create 1 rectangle for each category.
+ let totalCountSoFar = 0;
+ let totalWidthDividedByTotalCount = healthPillWidth / totalCount;
+ for (let i = 0; i < lastHealthPillOverview.length; i++) {
+ if (!lastHealthPillOverview[i]) {
+ // Do not render empty rectangles.
+ continue;
+ }
+ let rect = document.createElementNS(svgNamespace, 'rect');
+ rect.setAttribute('height', String(healthPillHeight));
+ rect.setAttribute(
+ 'width',
+ String(totalWidthDividedByTotalCount * lastHealthPillOverview[i]));
+ rect.setAttribute(
+ 'x', String(totalWidthDividedByTotalCount * totalCountSoFar));
+ rect.setAttribute('fill', colors[i]);
+ totalCountSoFar += lastHealthPillOverview[i];
+ Polymer.dom(healthPill).appendChild(rect);
+ }
+
+ // Center this health pill just right above the node for the op.
+ healthPill.setAttribute(
+ 'x', String(nodeInfo.x - healthPillWidth + nodeInfo.width / 2));
+ healthPill.setAttribute(
+ 'y',
+ String(nodeInfo.y - healthPillHeight - nodeInfo.coreBox.height / 2 - 2));
+ Polymer.dom(nodeGroupElement.parentNode).appendChild(healthPill);
+}
+
+/**
+ * Adds health pills (which visualize tensor summaries) to a graph group.
+ * @param svgRoot The root SVG element of the graph to add heath pills to.
+ * @param nodeNamesToHealthPills An object mapping node names to health pills.
+ * @param colors A list of colors to use.
+ */
+export function addHealthPills(
+ svgRoot: SVGElement, nodeNamesToHealthPills: {[key: string]: HealthPill[]},
+ colors: string[]) {
+ let svgRootSelection = d3.select(svgRoot);
+
+ svgRootSelection.selectAll('g.nodeshape')
+ .each(function(nodeInfo: render.RenderGroupNodeInfo) {
+ // The element is the first item of a d3 selection.
+ _addHealthPill(
+ this, nodeNamesToHealthPills[nodeInfo.node.name], nodeInfo, colors);
+ });
+};
+
} // close module