in tensorboard/plugins/graph/tf_graph_controls/tf-graph-controls.ts [111:1501]
class TfGraphControls extends LegacyElementMixin(
DarkModeMixin(PolymerElement)
) {
static readonly template = html`
<style>
:host {
color: #555;
display: flex;
flex-direction: column;
font-size: 12px;
width: 100%;
--tb-graph-controls-title-color: #000;
--tb-graph-controls-legend-text-color: #000;
--tb-graph-controls-text-color: #555;
--tb-graph-controls-title-font-size: 14px;
--tb-graph-controls-subtitle-font-size: 14px;
--paper-input-container-shared-input-style_-_font-size: 14px;
--paper-font-subhead_-_font-size: 14px;
}
:host(.dark-mode) {
--tb-graph-controls-title-color: #fff;
--tb-graph-controls-legend-text-color: #f3f3f3;
--tb-graph-controls-text-color: #eee;
}
paper-dropdown-menu {
--paper-dropdown-menu-input: {
padding: 0;
color: gray;
}
--iron-icon-width: 15px;
--iron-icon-height: 15px;
--primary-text-color: gray;
--paper-item-min-height: 30px;
}
paper-button[raised].keyboard-focus {
font-weight: normal;
}
.run-dropdown {
--paper-input-container: {
padding: 5px 0 5px 5px;
}
}
table {
border-collapse: collapse;
border-spacing: 0;
}
table tr {
height: 20px;
}
table td {
padding: 0;
margin: 0;
}
.allcontrols {
padding: 0 20px 20px;
flex-grow: 1;
overflow-y: auto;
}
.legend-holder {
background: var(--secondary-background-color);
box-sizing: border-box;
color: var(--tb-graph-controls-text-color);
width: 100%;
}
.legend-toolbar {
appearance: none;
background-color: inherit;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
border-right: none;
border-left: none;
cursor: pointer;
color: var(--tb-graph-controls-legend-text-color);
font: inherit;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.legend-toolbar,
.legend-content {
padding: 8px 20px;
}
.toggle-legend-button {
max-height: 20px;
max-width: 20px;
padding: 0;
}
.toggle-legend-text {
font-size: var(--tb-graph-controls-subtitle-font-size);
}
paper-radio-button {
display: block;
padding: 5px;
}
svg.icon,
tf-graph-icon {
width: 60px;
height: 18px;
}
.domainValues {
margin-bottom: 10px;
width: 165px;
}
.domainStart {
float: left;
}
.domainEnd {
float: right;
}
.colorBox {
width: 20px;
}
.image-icon {
width: 24px;
height: 24px;
}
.help-icon {
height: 15px;
margin: 0;
padding: 0;
}
.gray {
color: #666;
}
.title {
font-size: var(--tb-graph-controls-title-font-size);
margin: 8px 5px 8px 0;
color: var(--tb-graph-controls-title-color);
}
.title small {
font-weight: normal;
}
.deviceList,
.xlaClusterList {
max-height: 200px;
overflow-y: auto;
}
#file {
padding: 8px 0;
}
.color-legend-row {
align-items: center;
clear: both;
display: flex;
height: 20px;
margin-top: 5px;
}
.color-legend-row .label,
.color-legend-row svg,
.color-legend-row tf-graph-icon {
flex: 0 0 40px;
margin-right: 20px;
}
.devices-checkbox input {
text-align: left;
vertical-align: middle;
}
.control-holder .icon-button {
font-size: var(--tb-graph-controls-subtitle-font-size);
margin: 0 -5px;
padding: 5px;
display: flex;
justify-content: flex-start;
color: var(--tb-graph-controls-text-color);
}
.button-text {
padding-left: 20px;
text-transform: none;
}
.upload-button {
width: 165px;
height: 25px;
text-transform: none;
margin-top: 4px;
}
.button-icon {
width: 26px;
height: 26px;
color: var(--paper-orange-500);
}
.hidden-input {
display: none;
}
.allcontrols .control-holder {
clear: both;
display: flex;
justify-content: space-between;
}
.allcontrols .control-holder.control-options {
padding: 0 0 15px 15px;
flex-direction: column;
}
.allcontrols .control-holder paper-toggle-button {
margin-bottom: 5px;
}
span.counter {
font-size: var(--tb-graph-controls-subtitle-font-size);
color: gray;
margin-left: 4px;
}
.runs-row .title,
.tags-row .title {
display: flex;
align-items: baseline;
}
.runs-row paper-item,
.tags-row paper-item {
--paper-item: {
white-space: nowrap;
}
}
table.control-holder {
border: 0;
border-collapse: collapse;
}
table.tf-graph-controls td.input-element-table-data {
padding: 0 0 0 20px;
}
.spacer {
flex-grow: 1;
}
.color-text {
overflow: hidden;
}
.color-text.gradient-container {
margin: 0 5px;
}
/** Override inline styles that suppress pointer events for disabled buttons. Otherwise, the */
/* tooltips do not appear. */
paper-radio-group paper-radio-button {
pointer-events: auto !important;
}
.legend-clarifier {
color: #266236;
cursor: help;
display: inline-block;
text-decoration: underline;
}
.legend-clarifier paper-tooltip {
width: 150px;
}
/** Otherwise, polymer UI controls appear atop node search. */
tf-graph-node-search {
z-index: 1;
width: 100%;
}
paper-dropdown-menu {
flex-grow: 1;
}
</style>
<div class="allcontrols">
<div class="control-holder">
<tf-graph-node-search
selected-node="{{selectedNode}}"
render-hierarchy="[[renderHierarchy]]"
></tf-graph-node-search>
</div>
<div class="control-holder">
<paper-button class="icon-button" on-tap="_fit" alt="Fit to screen">
<iron-icon icon="aspect-ratio" class="button-icon"></iron-icon>
<span class="button-text">Fit to screen</span>
</paper-button>
</div>
<div class="control-holder">
<paper-button
class="icon-button"
on-click="download"
alt="Download PNG"
>
<iron-icon icon="file-download" class="button-icon"></iron-icon>
<span class="button-text">Download PNG</span>
</paper-button>
</div>
<template is="dom-if" if="[[showUploadButton]]">
<div class="control-holder">
<paper-button
class="icon-button"
on-click="_getFile"
alt="Upload file"
title="Upload a pbtxt file to view a graph from the local filesystem"
>
<iron-icon icon="file-upload" class="button-icon"></iron-icon>
<span class="button-text">Upload file</span>
</paper-button>
<div class="hidden-input">
<input
type="file"
id="file"
name="file"
on-change="_updateFileInput"
accept=".pbtxt"
/>
</div>
</div>
</template>
<div class="control-holder runs-row">
<div class="title">
Run <span class="counter">([[datasets.length]])</span>
</div>
<paper-dropdown-menu
no-label-float
no-animations
noink
horizontal-align="left"
class="run-dropdown"
>
<paper-listbox
class="dropdown-content"
selected="{{_selectedRunIndex}}"
slot="dropdown-content"
>
<template is="dom-repeat" items="[[datasets]]">
<paper-item>[[item.name]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
<template is="dom-if" if="[[showSessionRunsDropdown]]">
<div class="control-holder tags-row">
<div class="title">
Tag
<span class="counter"
>([[_numTags(datasets, _selectedRunIndex)]])</span
>
</div>
<paper-dropdown-menu
no-label-float
no-animations
horizontal-align="left"
noink
class="run-dropdown"
>
<paper-listbox
class="dropdown-content"
selected="{{_selectedTagIndex}}"
slot="dropdown-content"
>
<template
is="dom-repeat"
items="[[_getTags(datasets, _selectedRunIndex)]]"
>
<paper-item>[[item.displayName]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</div>
</template>
<div class="title">Graph type</div>
<div class="control-holder control-options">
<paper-radio-group
selected="{{_selectedGraphType}}"
on-paper-radio-group-changed="_onGraphTypeChangedByUserGesture"
>
<!-- Note that the name has to match that of tf_graph_common.SelectionType. -->
<paper-radio-button
name="op_graph"
disabled="[[_getSelectionOpGraphDisabled(datasets, _selectedRunIndex, _selectedTagIndex)]]"
>Op graph</paper-radio-button
>
<paper-radio-button
name="conceptual_graph"
disabled="[[_getSelectionConceptualGraphDisabled(datasets, _selectedRunIndex, _selectedTagIndex)]]"
>Conceptual graph</paper-radio-button
>
<paper-radio-button
name="profile"
disabled="[[_getSelectionProfileDisabled(datasets, _selectedRunIndex, _selectedTagIndex)]]"
>Profile</paper-radio-button
>
</paper-radio-group>
</div>
<div class="title">Node options</div>
<div class="control-holder control-options">
<paper-toggle-button
checked="{{traceInputs}}"
on-change="_onTraceInputsChangedByUserGesture"
>
Trace inputs
</paper-toggle-button>
<paper-toggle-button checked="{{autoExtractNodes}}">
Auto-extract high-degree nodes
</paper-toggle-button>
</div>
<template is="dom-if" if="[[healthPillsFeatureEnabled]]">
<div class="control-holder">
<paper-toggle-button checked="{{healthPillsToggledOn}}"
>Show health pills</paper-toggle-button
>
</div>
</template>
<div class="title">Color by</div>
<div class="control-holder control-options">
<paper-radio-group
selected="{{colorBy}}"
on-paper-radio-group-changed="_onColorByChangedByUserGesture"
>
<paper-radio-button name="[[ColorBy.NONE]]">None</paper-radio-button>
<paper-radio-button name="[[ColorBy.STRUCTURE]]"
>Structure</paper-radio-button
>
<paper-radio-button name="[[ColorBy.DEVICE]]"
>Device</paper-radio-button
>
<paper-radio-button
id="xla-cluster-radio-button"
name="[[ColorBy.XLA_CLUSTER]]"
disabled="[[!_xlaClustersProvided(renderHierarchy)]]"
>
XLA cluster
</paper-radio-button>
<paper-tooltip
animation-delay="0"
for="xla-cluster-radio-button"
position="right"
offset="0"
>
Coloring by XLA cluster is only enabled if at least 1 op specifies
an XLA cluster.
</paper-tooltip>
<paper-radio-button
id="compute-time-radio-button"
name="[[ColorBy.COMPUTE_TIME]]"
disabled="[[!stats]]"
>
Compute time
</paper-radio-button>
<paper-tooltip
animation-delay="0"
for="compute-time-radio-button"
position="right"
offset="0"
>
Coloring by compute time is only enabled if the RunMetadata proto is
passed to the FileWriter when a specific session is run.
</paper-tooltip>
<paper-radio-button
id="memory-radio-button"
name="[[ColorBy.MEMORY]]"
disabled="[[!stats]]"
>
Memory
</paper-radio-button>
<paper-tooltip
animation-delay="0"
for="memory-radio-button"
position="right"
offset="0"
>
Coloring by memory is only enabled if the RunMetadata proto is
passed to the FileWriter when a specific session is run.
</paper-tooltip>
<paper-radio-button
id="tpu-compatibility-radio-button"
name="[[ColorBy.OP_COMPATIBILITY]]"
>
TPU compatibility
</paper-radio-button>
<paper-tooltip
animation-delay="0"
for="tpu-compatibility-radio-button"
position="right"
offset="0"
>
Coloring by whether an operation is compatible for the TPU device.
</paper-tooltip>
</paper-radio-group>
<span class="spacer"></span>
</div>
</div>
<div class="legend-holder">
<button class="legend-toolbar" on-click="_toggleLegendOpen">
<span class="toggle-legend-text">Legend</span>
<iron-icon
icon="[[_getToggleLegendIcon(_legendOpened)]]"
class="toggle-legend-button"
>
</iron-icon>
</button>
<iron-collapse opened="[[_legendOpened]]" class="legend-content">
<!-- Color-mode-specific legend items -->
<div>
<template is="dom-if" if="[[_isGradientColoring(stats, colorBy)]]">
<svg width="140" height="20" class="color-text gradient-container">
<defs>
<linearGradient
id="linearGradient"
x1="0%"
y1="0%"
x2="100%"
y2="0%"
>
<stop
class="start"
offset="0%"
stop-color$="[[_currentGradientParams.startColor]]"
></stop>
<stop
class="end"
offset="100%"
stop-color$="[[_currentGradientParams.endColor]]"
></stop>
</linearGradient>
</defs>
<rect
x="0"
y="0"
width="135"
height="20"
fill="url(#linearGradient)"
stroke="black"
></rect>
</svg>
<div class="domainValues color-text">
<div class="domainStart">[[_currentGradientParams.minValue]]</div>
<div class="domainEnd">[[_currentGradientParams.maxValue]]</div>
</div>
<br style="clear: both" />
<div>Devices included in stats:</div>
<div class="deviceList">
<template is="dom-repeat" items="[[_currentDevices]]">
<div class="color-legend-row devices-checkbox">
<span
><input
type="checkbox"
value$="[[item.device]]"
checked$="[[item.used]]"
on-click="_deviceCheckboxClicked"
/></span>
<span>[[item.suffix]]</span>
<template is="dom-if" if="[[item.ignoredMsg]]">
<paper-icon-button
icon="help"
class="help-icon"
></paper-icon-button>
<paper-tooltip
position="right"
offset="0"
animation-delay="0"
>[[item.ignoredMsg]]</paper-tooltip
>
</template>
</div>
</template>
</div>
</template>
<template is="dom-if" if="[[_equals(colorBy, 'structure')]]">
<div class="color-text">
<div class="color-legend-row">
<span class="label"> colors </span>
<span class="color-legend-value">same substructure</span>
</div>
<div class="color-legend-row">
<tf-graph-icon
type="META"
height="16"
fill-override="#eee"
stroke-override="#a6a6a6"
></tf-graph-icon>
<span class="color-legend-value">unique substructure</span>
</div>
</div>
</template>
<template is="dom-if" if="[[_equals(colorBy, 'device')]]">
<div>
<template is="dom-repeat" items="[[_currentDeviceParams]]">
<div class="color-legend-row">
<tf-graph-icon
type="META"
height="16"
fill-override="[[item.color]]"
stroke-override="#a6a6a6"
></tf-graph-icon>
<span class="color-legend-value">[[item.device]]</span>
</div>
</template>
<div class="color-legend-row">
<tf-graph-icon
type="META"
height="16"
fill-override="#eee"
stroke-override="#a6a6a6"
></tf-graph-icon>
<span class="color-legend-value">unknown device</span>
</div>
</div>
</template>
<template is="dom-if" if="[[_equals(colorBy, 'xla_cluster')]]">
<div>
<template is="dom-repeat" items="[[_currentXlaClusterParams]]">
<div class="color-legend-row">
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="#unfilled-rect"
x="0"
y="0"
style="fill:[[item.color]]"
></use>
</svg>
<span class="color-legend-value">[[item.xla_cluster]]</span>
</div>
</template>
<div class="color-legend-row">
<svg>
<use
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="#grey-rect"
x="0"
y="0"
></use>
</svg>
<span class="color-legend-value">unknown XLA cluster</span>
</div>
</div>
</template>
<template is="dom-if" if="[[_equals(colorBy, 'op_compatibility')]]">
<div class="color-text">
<div class="color-legend-row">
<tf-graph-icon
type="OP"
height="16"
fill-override="#0f9d58"
stroke-override="#ccc"
></tf-graph-icon>
<span class="color-legend-value">Valid Op</span>
</div>
<div class="color-legend-row">
<tf-graph-icon
type="OP"
height="16"
fill-override="#db4437"
stroke-override="#ccc"
></tf-graph-icon>
<span class="color-legend-value">Invalid Op</span>
</div>
</div>
</template>
<template is="dom-if" if="[[_statsNotNull(stats)]]">
<div class="color-legend-row">
<tf-graph-icon type="META" height="16" faded></tf-graph-icon>
<span class="color-legend-value">unused substructure</span>
</div>
</template>
</div>
<!-- Common legend items -->
<div>
<table>
<tbody>
<tr>
<td></td>
<td>(* = expandable)</td>
</tr>
<tr>
<td>
<tf-graph-icon
type="META"
height="16"
fill-override="#d9d9d9"
stroke-override="#ccc"
></tf-graph-icon>
</td>
<td>
Namespace<span class="gray">*</span>
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Encapsulates a set of nodes. Namespace is hierarchical and
based on scope.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<tf-graph-icon type="OP" height="16"></tf-graph-icon>
</td>
<td>
OpNode
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Node that performs an operation. These nodes cannot
expand.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<tf-graph-icon type="SERIES" height="16"></tf-graph-icon>
</td>
<td>
Unconnected series<span class="gray">*</span>
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Sequence of numbered nodes that are not connected to each
other.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<tf-graph-icon
type="SERIES"
height="16"
vertical
></tf-graph-icon>
</td>
<td>
Connected series<span class="gray">*</span>
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Sequence of numbered nodes that are connected to each
other.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<svg class="icon">
<circle
fill="white"
stroke="#848484"
cx="10"
cy="10"
r="5"
></circle>
</svg>
</td>
<td>
Constant
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Node that outputs a constant value.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<tf-graph-icon type="SUMMARY" height="20"></tf-graph-icon>
</td>
<td>
Summary
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Node that collects data for visualization within
TensorBoard.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<svg
class="icon"
height="15px"
preserveAspectRatio="xMinYMid meet"
viewBox="0 0 15 15"
>
<defs>
<marker
id="dataflow-arrowhead-legend"
fill="#bbb"
markerWidth="10"
markerHeight="10"
refX="9"
refY="5"
orient="auto-start-reverse"
>
<path d="M 0,0 L 10,5 L 0,10 C 3,7 3,3 0,0"></path>
</marker>
</defs>
<path
marker-end="url(#dataflow-arrowhead-legend)"
stroke="#bbb"
d="M2 9 l 29 0"
stroke-linecap="round"
></path>
</svg>
</td>
<td>
Dataflow edge
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Edge showing the data flow between operations. Edges flow
upwards unless arrowheads specify otherwise.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<svg
class="icon"
height="15px"
preserveAspectRatio="xMinYMid meet"
viewBox="0 0 15 15"
>
<path
stroke="#bbb"
d="M2 9 l 29 0"
stroke-linecap="round"
stroke-dasharray="2, 2"
></path>
</svg>
</td>
<td>
Control dependency edge
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Edge showing the control dependency between operations.
</paper-tooltip>
</div>
</td>
</tr>
<tr>
<td>
<svg
class="icon"
height="15px"
preserveAspectRatio="xMinYMid meet"
viewBox="0 0 15 15"
>
<defs>
<marker
id="reference-arrowhead-legend"
fill="#FFB74D"
markerWidth="10"
markerHeight="10"
refX="9"
refY="5"
orient="auto-start-reverse"
>
<path d="M 0,0 L 10,5 L 0,10 C 3,7 3,3 0,0"></path>
</marker>
</defs>
<path
marker-end="url(#reference-arrowhead-legend)"
stroke="#FFB74D"
d="M2 9 l 29 0"
stroke-linecap="round"
></path>
</svg>
</td>
<td>
Reference edge
<div class="legend-clarifier">
<span>?</span>
<paper-tooltip
animation-delay="0"
position="right"
offset="0"
>
Edge showing that the outgoing operation node can mutate
the incoming tensor.
</paper-tooltip>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</iron-collapse>
</div>
`;
// Expose values for use in template.
ColorBy = ColorBy;
// Public API.
/**
* @type {?tf_graph_proto.StepStats}
*/
@property({
type: Object,
observer: '_statsChanged',
})
stats: object = null;
/**
* @type {?Object<string, boolean>}
*/
@property({
type: Object,
notify: true,
})
devicesForStats: object = null;
@property({
type: String,
notify: true,
})
colorBy: ColorBy = ColorBy.STRUCTURE;
@property({
type: Object,
notify: true,
})
colorByParams: ColorByParams;
@property({
type: Array,
observer: '_datasetsChanged',
})
datasets: any = [];
/**
* @type {tf_graph_render.RenderGraphInfo}
*/
@property({
type: Object,
})
renderHierarchy: tf_graph_render.RenderGraphInfo;
/**
* @type {!Selection}
*/
@property({
type: Object,
notify: true,
readOnly: true,
computed:
'_computeSelection(datasets, _selectedRunIndex, _selectedTagIndex, _selectedGraphType)',
})
selection: object;
@property({
type: Object,
notify: true,
})
selectedFile: object;
@property({
type: Number,
observer: '_selectedRunIndexChanged',
})
_selectedRunIndex: number = 0;
@property({
type: Boolean,
notify: true,
})
traceInputs: boolean = false;
@property({
type: Boolean,
notify: true,
})
autoExtractNodes: boolean = true;
@property({
type: Number,
observer: '_selectedTagIndexChanged',
})
_selectedTagIndex: number = 0;
/**
* @type {tf_graph_common.SelectionType}
*/
@property({
type: String,
})
_selectedGraphType: string = tf_graph_common.SelectionType.OP_GRAPH;
@property({
type: String,
notify: true,
})
selectedNode: string;
@property({
type: Boolean,
})
showSessionRunsDropdown: boolean = true;
@property({
type: Boolean,
})
showUploadButton: boolean = true;
// This stores whether the feature for showing health pills is enabled in the first place.
@property({type: Boolean})
healthPillsFeatureEnabled: boolean;
// This stores whether to show health pills. Only relevant if healthPillsFeatureEnabled. The
// user can toggle this value.
@property({
type: Boolean,
notify: true,
})
healthPillsToggledOn: boolean;
@property({
type: Boolean,
})
_legendOpened: boolean = true;
_downloadFilename = 'graph.png';
_onGraphTypeChangedByUserGesture() {
tf_graph_util.notifyDebugEvent({
actionId: tb_debug.GraphDebugEventId.GRAPH_TYPE_CHANGED,
eventLabel: this._selectedGraphType,
});
}
_onColorByChangedByUserGesture() {
tf_graph_util.notifyDebugEvent({
actionId: tb_debug.GraphDebugEventId.NODE_COLOR_MODE_CHANGED,
eventLabel: this.colorBy,
});
}
_onTraceInputsChangedByUserGesture() {
tf_graph_util.notifyDebugEvent({
actionId: tb_debug.GraphDebugEventId.TRACE_INPUT_MODE_TOGGLED,
});
}
_xlaClustersProvided(
renderHierarchy: tf_graph_render.RenderGraphInfo | null
) {
return (
renderHierarchy &&
renderHierarchy.hierarchy &&
renderHierarchy.hierarchy.xlaClusters.length > 0
);
}
_statsChanged(stats: tf_graph_proto.StepStats) {
if (stats == null) {
return;
}
var devicesForStats = {};
var devices = _.each(stats.dev_stats, function (d) {
// Only considered included devices.
var include = _.some(DEVICE_NAMES_INCLUDE, function (rule) {
return rule.regex.test(d.device);
});
// Exclude device names that are ignored by default.
var exclude = _.some(DEVICE_STATS_DEFAULT_OFF, function (rule) {
return rule.regex.test(d.device);
});
if (include && !exclude) {
devicesForStats[d.device] = true;
}
});
this.set('devicesForStats', devicesForStats);
}
@computed('devicesForStats')
get _currentDevices(): unknown[] {
var devicesForStats = this.devicesForStats;
const stats = this.stats as tf_graph_proto.StepStats | null;
const devStats: tf_graph_proto.DevStat[] = stats ? stats.dev_stats : [];
const allDevices = devStats.map((d) => d.device);
const devices = allDevices.filter((deviceName) => {
return DEVICE_NAMES_INCLUDE.some((rule) => {
return rule.regex.test(deviceName);
});
});
// Devices names can be long so we remove the longest common prefix
// before showing the devices in a list.
const suffixes = tf_graph_util.removeCommonPrefix(devices);
if (suffixes.length == 1) {
const found = suffixes[0].match(DEVICE_NAME_REGEX);
if (found) {
suffixes[0] = found[1];
}
}
return devices.map((device, i) => {
let ignoredMsg = null;
// TODO(stephanwlee): this should probably bail on the first match or
// do something useful with multiple rule.msgs.
DEVICE_STATS_DEFAULT_OFF.forEach((rule) => {
if (rule.regex.test(device)) {
ignoredMsg = rule.msg;
}
});
return {
device: device,
suffix: suffixes[i],
used: devicesForStats[device],
ignoredMsg: ignoredMsg,
};
});
}
_deviceCheckboxClicked(event: Event) {
// Update the device map.
const input = event.target as HTMLInputElement;
const devicesForStats: DeviceForStats = Object.assign(
{},
this.devicesForStats
);
const device = input.value;
if (input.checked) {
devicesForStats[device] = true;
} else {
delete devicesForStats[device];
}
this.set('devicesForStats', devicesForStats);
}
_numTags(datasets: Dataset, _selectedRunIndex: number) {
return this._getTags(datasets, _selectedRunIndex).length;
}
_getTags(datasets: Dataset, _selectedRunIndex: number) {
if (!datasets || !datasets[_selectedRunIndex]) {
return [];
}
return datasets[_selectedRunIndex].tags;
}
_fit() {
this.fire('fit-tap');
}
_isGradientColoring(stats: tf_graph_proto.StepStats, colorBy: ColorBy) {
return GRADIENT_COMPATIBLE_COLOR_BY.has(colorBy) && stats != null;
}
_equals(a: any, b: any) {
return a === b;
}
@computed('colorByParams')
get _currentDeviceParams(): unknown[] {
var colorByParams = this.colorByParams;
const deviceParams = colorByParams.device.filter((param) => {
return DEVICE_NAMES_INCLUDE.some((rule) => {
return rule.regex.test(param.device);
});
});
// Remove common prefix and merge back corresponding color. If
// there is only one device then remove everything up to "/device:".
const suffixes = tf_graph_util.removeCommonPrefix(
deviceParams.map((d) => d.device)
);
if (suffixes.length == 1) {
var found = suffixes[0].match(DEVICE_NAME_REGEX);
if (found) {
suffixes[0] = found[1];
}
}
return deviceParams.map((d, i) => {
return {device: suffixes[i], color: d.color};
});
}
@computed('colorByParams')
get _currentXlaClusterParams(): unknown[] {
var colorByParams = this.colorByParams;
return colorByParams.xla_cluster;
}
@computed('colorByParams', 'colorBy')
get _currentGradientParams(): object {
var colorByParams = this.colorByParams;
var colorBy = this.colorBy;
if (!this._isGradientColoring(this.stats as any, colorBy)) {
return;
}
const params: ColorParams = colorByParams[colorBy];
let minValue = params.minValue;
let maxValue = params.maxValue;
if (colorBy === ColorBy.MEMORY) {
minValue = tf_graph_util.convertUnitsToHumanReadable(
minValue,
tf_graph_util.MEMORY_UNITS
);
maxValue = tf_graph_util.convertUnitsToHumanReadable(
maxValue,
tf_graph_util.MEMORY_UNITS
);
} else if (colorBy === ColorBy.COMPUTE_TIME) {
minValue = tf_graph_util.convertUnitsToHumanReadable(
minValue,
tf_graph_util.TIME_UNITS
);
maxValue = tf_graph_util.convertUnitsToHumanReadable(
maxValue,
tf_graph_util.TIME_UNITS
);
}
return {
minValue,
maxValue,
startColor: params.startColor,
endColor: params.endColor,
};
}
download() {
this.fire('download-image-requested', this._downloadFilename);
}
_updateFileInput(e: Event) {
const file = (e.target as HTMLInputElement).files[0];
if (!file) return;
// Strip off everything before the last "/" and strip off the file
// extension in order to get the name of the PNG for the graph.
let filePath = file.name;
const dotIndex = filePath.lastIndexOf('.');
if (dotIndex >= 0) {
filePath = filePath.substring(0, dotIndex);
}
const lastSlashIndex = filePath.lastIndexOf('/');
if (lastSlashIndex >= 0) {
filePath = filePath.substring(lastSlashIndex + 1);
}
this._setDownloadFilename(filePath);
this.set('selectedFile', e);
tf_graph_util.notifyDebugEvent({
actionId: tb_debug.GraphDebugEventId.UPLOADED_GRAPH_FROM_FILESYSTEM,
});
}
_datasetsChanged(newDatasets: Dataset, oldDatasets: Dataset) {
if (oldDatasets != null) {
// Select the first dataset by default.
this._selectedRunIndex = 0;
}
this._setDownloadFilename(this.datasets[this._selectedRunIndex]?.name);
}
_computeSelection(
datasets: Dataset,
_selectedRunIndex: number,
_selectedTagIndex: number,
_selectedGraphType: tf_graph_common.SelectionType
) {
if (
!datasets[_selectedRunIndex] ||
!datasets[_selectedRunIndex].tags[_selectedTagIndex]
) {
return null;
}
return {
run: datasets[_selectedRunIndex].name,
tag: datasets[_selectedRunIndex].tags[_selectedTagIndex].tag,
type: _selectedGraphType,
};
}
_selectedRunIndexChanged(runIndex: number) {
if (!this.datasets) return;
// Reset the states when user pick a different run.
this.colorBy = ColorBy.STRUCTURE;
this._selectedTagIndex = 0;
this._selectedGraphType = this._getDefaultSelectionType();
this.traceInputs = false; // Set trace input to off-state.
this._setDownloadFilename(this.datasets[runIndex]?.name);
}
_selectedTagIndexChanged(): void {
this._selectedGraphType = this._getDefaultSelectionType();
}
_getDefaultSelectionType(): tf_graph_common.SelectionType {
const {datasets, _selectedRunIndex: run, _selectedTagIndex: tag} = this;
if (
!datasets ||
!datasets[run] ||
!(datasets[run] as any).tags[tag] ||
(datasets[run] as any).tags[tag].opGraph
) {
return tf_graph_common.SelectionType.OP_GRAPH;
}
const datasetForRun = datasets[run] as any;
if (datasetForRun.tags[tag].profile) {
return tf_graph_common.SelectionType.PROFILE;
}
if (datasetForRun.tags[tag].conceptualGraph) {
return tf_graph_common.SelectionType.CONCEPTUAL_GRAPH;
}
return tf_graph_common.SelectionType.OP_GRAPH;
}
_getFile() {
(this.$$('#file') as HTMLElement).click();
}
_setDownloadFilename(name?: string) {
this._downloadFilename = (name || 'graph') + '.png';
}
_statsNotNull(stats: tf_graph_proto.StepStats) {
return stats !== null;
}
_toggleLegendOpen(): void {
this.set('_legendOpened', !this._legendOpened);
}
_getToggleLegendIcon(legendOpened: boolean): string {
// This seems counter-intuitive, but actually makes sense because the
// expand-more button points downwards, and the expand-less button points
// upwards. For most collapsibles, this works because the collapsibles
// expand in the downwards direction. This collapsible expands upwards
// though, so we reverse the icons.
return legendOpened ? 'expand-more' : 'expand-less';
}
_getSelectionOpGraphDisabled(
datasets: Dataset,
_selectedRunIndex: number,
_selectedTagIndex: number
) {
return (
!datasets[_selectedRunIndex] ||
!datasets[_selectedRunIndex].tags[_selectedTagIndex] ||
!datasets[_selectedRunIndex].tags[_selectedTagIndex].opGraph
);
}
_getSelectionProfileDisabled(
datasets: Dataset,
_selectedRunIndex: number,
_selectedTagIndex: number
) {
return (
!datasets[_selectedRunIndex] ||
!datasets[_selectedRunIndex].tags[_selectedTagIndex] ||
!datasets[_selectedRunIndex].tags[_selectedTagIndex].profile
);
}
_getSelectionConceptualGraphDisabled(
datasets: Dataset,
_selectedRunIndex: number,
_selectedTagIndex: number
) {
return (
!datasets[_selectedRunIndex] ||
!datasets[_selectedRunIndex].tags[_selectedTagIndex] ||
!datasets[_selectedRunIndex].tags[_selectedTagIndex].conceptualGraph
);
}
}