aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Justine Tunney <jart@google.com>2017-05-17 16:13:33 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2017-05-17 16:17:17 -0700
commit52b4d471ebde27b5065a4f6195049a679d12d97e (patch)
tree1c8eb2723802e520ceb880db63a00f0de8f2e7a3
parent9d9aa78c105a2414deed6ced31fb7699f08e55bb (diff)
Delete TensorBoard Gulp build
This CL uses web_library to build all components in the forked d3v4 packages. The tensorboard command now uses the newly vulcanized binary. This leaves the CMake build for TensorBoard in a partially broken state. We will need to update it to obtain tensorboard/index.html from a web server. PiperOrigin-RevId: 156369259
-rw-r--r--WORKSPACE500
-rw-r--r--bower.BUILD645
-rw-r--r--tensorflow/BUILD4
-rw-r--r--tensorflow/contrib/cmake/CMakeLists.txt1
-rw-r--r--tensorflow/contrib/cmake/external/tensorboard.cmake148
-rwxr-xr-xtensorflow/contrib/cmake/tf_python.cmake25
-rw-r--r--tensorflow/tensorboard/.bowerrc3
-rw-r--r--tensorflow/tensorboard/.gitignore27
-rw-r--r--tensorflow/tensorboard/BUILD50
-rw-r--r--tensorflow/tensorboard/DEVELOPMENT.md123
-rw-r--r--tensorflow/tensorboard/app/BUILD19
-rw-r--r--tensorflow/tensorboard/app/analytics.js16
-rw-r--r--tensorflow/tensorboard/backend/BUILD2
-rw-r--r--tensorflow/tensorboard/backend/application.py64
-rw-r--r--tensorflow/tensorboard/backend/application_test.py5
-rw-r--r--tensorflow/tensorboard/bower.json187
-rw-r--r--tensorflow/tensorboard/bower/BUILD76
-rw-r--r--tensorflow/tensorboard/components/BUILD39
-rw-r--r--tensorflow/tensorboard/components/analytics.html (renamed from tensorflow/tensorboard/components/tf_imports_google/graphlib.html)2
-rw-r--r--tensorflow/tensorboard/components/index.html39
-rw-r--r--tensorflow/tensorboard/components/tensorboard.html (renamed from tensorflow/tensorboard/components/tf_tensorboard_d3v4/dist.html)15
-rw-r--r--tensorflow/tensorboard/components/tf_audio_dashboard/BUILD3
-rw-r--r--tensorflow/tensorboard/components/tf_backend/BUILD12
-rw-r--r--tensorflow/tensorboard/components/tf_backend_d3v4/BUILD1
-rw-r--r--tensorflow/tensorboard/components/tf_color_scale/BUILD14
-rw-r--r--tensorflow/tensorboard/components/tf_dashboard_common/BUILD12
-rw-r--r--tensorflow/tensorboard/components/tf_distribution_dashboard/BUILD4
-rw-r--r--tensorflow/tensorboard/components/tf_graph_common/BUILD14
-rw-r--r--tensorflow/tensorboard/components/tf_histogram_dashboard/BUILD2
-rw-r--r--tensorflow/tensorboard/components/tf_imports/BUILD4
-rw-r--r--tensorflow/tensorboard/components/tf_imports_d3v4/BUILD10
-rw-r--r--tensorflow/tensorboard/components/tf_imports_google/README.md3
-rw-r--r--tensorflow/tensorboard/components/tf_imports_google/d3.html18
-rw-r--r--tensorflow/tensorboard/components/tf_imports_google/dagre.html18
-rw-r--r--tensorflow/tensorboard/components/tf_imports_google/plottable.html19
-rw-r--r--tensorflow/tensorboard/components/tf_scalar_dashboard/BUILD3
-rw-r--r--tensorflow/tensorboard/components/tf_storage/BUILD12
-rw-r--r--tensorflow/tensorboard/components/tf_tensorboard_d3v4/BUILD43
-rw-r--r--tensorflow/tensorboard/components/tf_tensorboard_d3v4/demo.html15
-rw-r--r--tensorflow/tensorboard/components/tf_tensorboard_d3v4/style.html (renamed from tensorflow/tensorboard/components/tf_imports_google/lodash.html)12
-rw-r--r--tensorflow/tensorboard/components/tf_tensorboard_d3v4/tf-tensorboard.html3
-rw-r--r--tensorflow/tensorboard/components/vz_distribution_chart/BUILD12
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap/BUILD29
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap/demo/index.html3
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap/vz-heatmap.html6
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap_d3v4/BUILD41
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap_d3v4/demo/index.html161
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap_d3v4/index.html (renamed from tensorflow/tensorboard/dist/bazel-html-imports.html)18
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html360
-rw-r--r--tensorflow/tensorboard/components/vz_histogram_timeseries/BUILD2
-rw-r--r--tensorflow/tensorboard/components/vz_line_chart/BUILD14
-rw-r--r--tensorflow/tensorboard/components/vz_sorting/BUILD1
-rw-r--r--tensorflow/tensorboard/components/vz_sorting/test/BUILD10
-rw-r--r--tensorflow/tensorboard/dist/index.html32
-rw-r--r--tensorflow/tensorboard/dist/tf-tensorboard.html27143
-rw-r--r--tensorflow/tensorboard/gulp_tasks/bower.js23
-rw-r--r--tensorflow/tensorboard/gulp_tasks/compile.js95
-rw-r--r--tensorflow/tensorboard/gulp_tasks/test.js28
-rw-r--r--tensorflow/tensorboard/gulp_tasks/util.js39
-rw-r--r--tensorflow/tensorboard/gulp_tasks/vulcanize.js83
-rw-r--r--tensorflow/tensorboard/gulpfile.js91
-rw-r--r--tensorflow/tensorboard/java/org/tensorflow/tensorboard/vulcanize/BUILD1
-rw-r--r--tensorflow/tensorboard/lib/BUILD22
-rw-r--r--tensorflow/tensorboard/lib/css/global.css21
-rw-r--r--tensorflow/tensorboard/package.json39
-rw-r--r--tensorflow/tensorboard/tsconfig.json16
-rw-r--r--tensorflow/tensorboard/wct.conf.json7
-rw-r--r--tensorflow/tools/pip_package/BUILD8
-rw-r--r--tensorflow/tools/pip_package/setup.py5
-rw-r--r--tensorflow/workspace.bzl24
70 files changed, 858 insertions, 29688 deletions
diff --git a/WORKSPACE b/WORKSPACE
index b2d6fb542b..edf655f6a7 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -20,7 +20,7 @@ load("//tensorflow:workspace.bzl", "tf_workspace")
#android_sdk_repository(
# name = "androidsdk",
# api_level = 23,
-# # Ensure that you have the build_tools_version below installed in the
+# # Ensure that you have the build_tools_version below installed in the
# # SDK manager as it updates periodically.
# build_tools_version = "25.0.2",
# # Replace with path to Android SDK on your system
@@ -31,7 +31,7 @@ load("//tensorflow:workspace.bzl", "tf_workspace")
#android_ndk_repository(
# name="androidndk",
# path="<PATH_TO_NDK>",
-# # This needs to be 14 or higher to compile TensorFlow.
+# # This needs to be 14 or higher to compile TensorFlow.
# # Note that the NDK version is not the API level.
# api_level=14)
@@ -39,485 +39,31 @@ load("//tensorflow:workspace.bzl", "tf_workspace")
tf_workspace()
new_http_archive(
- name = "inception5h",
- build_file = "models.BUILD",
- url = "https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip",
- sha256 = "d13569f6a98159de37e92e9c8ec4dae8f674fbf475f69fe6199b514f756d4364"
-)
-
-new_http_archive(
- name = "mobile_multibox",
- build_file = "models.BUILD",
- url = "https://storage.googleapis.com/download.tensorflow.org/models/mobile_multibox_v1a.zip",
- sha256 = "859edcddf84dddb974c36c36cfc1f74555148e9c9213dedacf1d6b613ad52b96"
-)
-
-new_http_archive(
- name = "stylize",
- build_file = "models.BUILD",
- url = "https://storage.googleapis.com/download.tensorflow.org/models/stylize_v1.zip",
- sha256 = "3d374a730aef330424a356a8d4f04d8a54277c425e274ecb7d9c83aa912c6bfa"
-)
-
-# TENSORBOARD_BOWER_AUTOGENERATED_BELOW_THIS_LINE_DO_NOT_EDIT
-
-new_http_archive(
- name = "d3",
- build_file = "bower.BUILD",
- url = "https://github.com/mbostock-bower/d3-bower/archive/v3.5.15.tar.gz",
- strip_prefix = "d3-bower-3.5.15",
-)
-
-new_http_archive(
- name = "dagre",
- build_file = "bower.BUILD",
- url = "https://github.com/cpettitt/dagre/archive/v0.7.4.tar.gz",
- strip_prefix = "dagre-0.7.4",
-)
-
-new_http_archive(
- name = "es6_promise",
- build_file = "bower.BUILD",
- url = "https://github.com/components/es6-promise/archive/v2.1.0.tar.gz",
- strip_prefix = "es6-promise-2.1.0",
-)
-
-new_http_archive(
- name = "font_roboto",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/font-roboto/archive/v1.0.1.tar.gz",
- strip_prefix = "font-roboto-1.0.1",
-)
-
-new_http_archive(
- name = "graphlib",
- build_file = "bower.BUILD",
- url = "https://github.com/cpettitt/graphlib/archive/v1.0.7.tar.gz",
- strip_prefix = "graphlib-1.0.7",
-)
-
-new_http_archive(
- name = "iron_a11y_announcer",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-a11y-announcer/archive/v1.0.5.tar.gz",
- strip_prefix = "iron-a11y-announcer-1.0.5",
-)
-
-new_http_archive(
- name = "iron_a11y_keys_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-a11y-keys-behavior/archive/v1.1.8.tar.gz",
- strip_prefix = "iron-a11y-keys-behavior-1.1.8",
-)
-
-new_http_archive(
- name = "iron_ajax",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-ajax/archive/v1.2.0.tar.gz",
- strip_prefix = "iron-ajax-1.2.0",
-)
-
-new_http_archive(
- name = "iron_autogrow_textarea",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-autogrow-textarea/archive/v1.0.12.tar.gz",
- strip_prefix = "iron-autogrow-textarea-1.0.12",
-)
-
-new_http_archive(
- name = "iron_behaviors",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-behaviors/archive/v1.0.17.tar.gz",
- strip_prefix = "iron-behaviors-1.0.17",
-)
-
-new_http_archive(
- name = "iron_checked_element_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-checked-element-behavior/archive/v1.0.4.tar.gz",
- strip_prefix = "iron-checked-element-behavior-1.0.4",
-)
-
-new_http_archive(
- name = "iron_collapse",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-collapse/archive/v1.0.8.tar.gz",
- strip_prefix = "iron-collapse-1.0.8",
-)
-
-new_http_archive(
- name = "iron_dropdown",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-dropdown/archive/v1.4.0.tar.gz",
- strip_prefix = "iron-dropdown-1.4.0",
-)
-
-new_http_archive(
- name = "iron_fit_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-fit-behavior/archive/v1.2.5.tar.gz",
- strip_prefix = "iron-fit-behavior-1.2.5",
-)
-
-new_http_archive(
- name = "iron_flex_layout",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-flex-layout/archive/v1.3.0.tar.gz",
- strip_prefix = "iron-flex-layout-1.3.0",
-)
-
-new_http_archive(
- name = "iron_form_element_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-form-element-behavior/archive/v1.0.6.tar.gz",
- strip_prefix = "iron-form-element-behavior-1.0.6",
-)
-
-new_http_archive(
- name = "iron_icon",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-icon/archive/v1.0.11.tar.gz",
- strip_prefix = "iron-icon-1.0.11",
-)
-
-new_http_archive(
- name = "iron_icons",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-icons/archive/v1.1.3.tar.gz",
- strip_prefix = "iron-icons-1.1.3",
-)
-
-new_http_archive(
- name = "iron_iconset_svg",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-iconset-svg/archive/v1.1.0.tar.gz",
- strip_prefix = "iron-iconset-svg-1.1.0",
-)
-
-new_http_archive(
- name = "iron_input",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-input/archive/1.0.10.tar.gz",
- strip_prefix = "iron-input-1.0.10",
-)
-
-new_http_archive(
- name = "iron_list",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-list/archive/v1.3.9.tar.gz",
- strip_prefix = "iron-list-1.3.9",
-)
-
-new_http_archive(
- name = "iron_menu_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-menu-behavior/archive/v1.1.10.tar.gz",
- strip_prefix = "iron-menu-behavior-1.1.10",
-)
-
-new_http_archive(
- name = "iron_meta",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-meta/archive/v1.1.1.tar.gz",
- strip_prefix = "iron-meta-1.1.1",
-)
-
-new_http_archive(
- name = "iron_overlay_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-overlay-behavior/archive/v1.10.1.tar.gz",
- strip_prefix = "iron-overlay-behavior-1.10.1",
-)
-
-new_http_archive(
- name = "iron_range_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-range-behavior/archive/v1.0.4.tar.gz",
- strip_prefix = "iron-range-behavior-1.0.4",
-)
-
-new_http_archive(
- name = "iron_resizable_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-resizable-behavior/archive/v1.0.3.tar.gz",
- strip_prefix = "iron-resizable-behavior-1.0.3",
-)
-
-new_http_archive(
- name = "iron_scroll_target_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-scroll-target-behavior/archive/v1.0.3.tar.gz",
- strip_prefix = "iron-scroll-target-behavior-1.0.3",
-)
-
-new_http_archive(
- name = "iron_selector",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-selector/archive/v1.5.2.tar.gz",
- strip_prefix = "iron-selector-1.5.2",
-)
-
-new_http_archive(
- name = "iron_validatable_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/iron-validatable-behavior/archive/v1.1.1.tar.gz",
- strip_prefix = "iron-validatable-behavior-1.1.1",
-)
-
-new_http_archive(
- name = "lodash",
- build_file = "bower.BUILD",
- url = "https://github.com/lodash/lodash/archive/3.8.0.tar.gz",
- strip_prefix = "lodash-3.8.0",
-)
-
-new_http_archive(
- name = "neon_animation",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/neon-animation/archive/v1.2.2.tar.gz",
- strip_prefix = "neon-animation-1.2.2",
-)
-
-http_file(
- name = "numericjs_numeric_min_js",
- url = "https://cdnjs.cloudflare.com/ajax/libs/numeric/1.2.6/numeric.min.js",
-)
-
-new_http_archive(
- name = "paper_behaviors",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-behaviors/archive/v1.0.12.tar.gz",
- strip_prefix = "paper-behaviors-1.0.12",
-)
-
-new_http_archive(
- name = "paper_button",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-button/archive/v1.0.11.tar.gz",
- strip_prefix = "paper-button-1.0.11",
-)
-
-new_http_archive(
- name = "paper_checkbox",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-checkbox/archive/v1.4.0.tar.gz",
- strip_prefix = "paper-checkbox-1.4.0",
-)
-
-new_http_archive(
- name = "paper_dialog",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-dialog/archive/v1.0.4.tar.gz",
- strip_prefix = "paper-dialog-1.0.4",
-)
-
-new_http_archive(
- name = "paper_dialog_behavior",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-dialog-behavior/archive/v1.2.5.tar.gz",
- strip_prefix = "paper-dialog-behavior-1.2.5",
-)
-
-new_http_archive(
- name = "paper_dialog_scrollable",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-dialog-scrollable/archive/1.1.5.tar.gz",
- strip_prefix = "paper-dialog-scrollable-1.1.5",
-)
-
-new_http_archive(
- name = "paper_dropdown_menu",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-dropdown-menu/archive/v1.4.0.tar.gz",
- strip_prefix = "paper-dropdown-menu-1.4.0",
-)
-
-new_http_archive(
- name = "paper_header_panel",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-header-panel/archive/v1.1.4.tar.gz",
- strip_prefix = "paper-header-panel-1.1.4",
-)
-
-new_http_archive(
- name = "paper_icon_button",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-icon-button/archive/v1.1.3.tar.gz",
- strip_prefix = "paper-icon-button-1.1.3",
-)
-
-new_http_archive(
- name = "paper_input",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-input/archive/v1.1.18.tar.gz",
- strip_prefix = "paper-input-1.1.18",
-)
-
-new_http_archive(
- name = "paper_item",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-item/archive/v1.1.4.tar.gz",
- strip_prefix = "paper-item-1.1.4",
-)
-
-new_http_archive(
- name = "paper_listbox",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-listbox/archive/v1.1.2.tar.gz",
- strip_prefix = "paper-listbox-1.1.2",
-)
-
-new_http_archive(
- name = "paper_material",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-material/archive/v1.0.6.tar.gz",
- strip_prefix = "paper-material-1.0.6",
-)
-
-new_http_archive(
- name = "paper_menu",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-menu/archive/v1.2.2.tar.gz",
- strip_prefix = "paper-menu-1.2.2",
-)
-
-new_http_archive(
- name = "paper_menu_button",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-menu-button/archive/v1.5.1.tar.gz",
- strip_prefix = "paper-menu-button-1.5.1",
-)
-
-new_http_archive(
- name = "paper_progress",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-progress/archive/v1.0.9.tar.gz",
- strip_prefix = "paper-progress-1.0.9",
-)
-
-new_http_archive(
- name = "paper_radio_button",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-radio-button/archive/v1.1.2.tar.gz",
- strip_prefix = "paper-radio-button-1.1.2",
-)
-
-new_http_archive(
- name = "paper_radio_group",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-radio-group/archive/v1.0.9.tar.gz",
- strip_prefix = "paper-radio-group-1.0.9",
-)
-
-new_http_archive(
- name = "paper_ripple",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-ripple/archive/v1.0.5.tar.gz",
- strip_prefix = "paper-ripple-1.0.5",
-)
-
-new_http_archive(
- name = "paper_slider",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-slider/archive/v1.0.10.tar.gz",
- strip_prefix = "paper-slider-1.0.10",
-)
-
-new_http_archive(
- name = "paper_spinner",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-spinner/archive/v1.1.1.tar.gz",
- strip_prefix = "paper-spinner-1.1.1",
-)
-
-new_http_archive(
- name = "paper_styles",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-styles/archive/v1.1.4.tar.gz",
- strip_prefix = "paper-styles-1.1.4",
-)
-
-new_http_archive(
- name = "paper_tabs",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-tabs/archive/v1.7.0.tar.gz",
- strip_prefix = "paper-tabs-1.7.0",
-)
-
-new_http_archive(
- name = "paper_toast",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-toast/archive/v1.3.0.tar.gz",
- strip_prefix = "paper-toast-1.3.0",
-)
-
-new_http_archive(
- name = "paper_toggle_button",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-toggle-button/archive/v1.2.0.tar.gz",
- strip_prefix = "paper-toggle-button-1.2.0",
-)
-
-new_http_archive(
- name = "paper_toolbar",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-toolbar/archive/v1.1.4.tar.gz",
- strip_prefix = "paper-toolbar-1.1.4",
-)
-
-new_http_archive(
- name = "paper_tooltip",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerelements/paper-tooltip/archive/v1.1.2.tar.gz",
- strip_prefix = "paper-tooltip-1.1.2",
-)
-
-new_http_archive(
- name = "plottable",
- build_file = "bower.BUILD",
- url = "https://github.com/palantir/plottable/archive/v1.16.1.tar.gz",
- strip_prefix = "plottable-1.16.1",
-)
-
-new_http_archive(
- name = "polymer",
- build_file = "bower.BUILD",
- url = "https://github.com/polymer/polymer/archive/v1.7.0.tar.gz",
- strip_prefix = "polymer-1.7.0",
-)
-
-new_http_archive(
- name = "promise_polyfill",
- build_file = "bower.BUILD",
- url = "https://github.com/polymerlabs/promise-polyfill/archive/v1.0.0.tar.gz",
- strip_prefix = "promise-polyfill-1.0.0",
-)
-
-http_file(
- name = "three_js_three_min_js",
- url = "https://raw.githubusercontent.com/mrdoob/three.js/r77/build/three.min.js",
-)
-
-http_file(
- name = "three_js_orbitcontrols_js",
- url = "https://raw.githubusercontent.com/mrdoob/three.js/r77/examples/js/controls/OrbitControls.js",
+ name = "inception5h",
+ build_file = "models.BUILD",
+ sha256 = "d13569f6a98159de37e92e9c8ec4dae8f674fbf475f69fe6199b514f756d4364",
+ urls = [
+ "http://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip",
+ "http://download.tensorflow.org/models/inception5h.zip",
+ ],
)
new_http_archive(
- name = "web_animations_js",
- build_file = "bower.BUILD",
- url = "https://github.com/web-animations/web-animations-js/archive/2.2.1.tar.gz",
- strip_prefix = "web-animations-js-2.2.1",
+ name = "mobile_multibox",
+ build_file = "models.BUILD",
+ sha256 = "859edcddf84dddb974c36c36cfc1f74555148e9c9213dedacf1d6b613ad52b96",
+ urls = [
+ "http://storage.googleapis.com/download.tensorflow.org/models/mobile_multibox_v1a.zip",
+ "http://download.tensorflow.org/models/mobile_multibox_v1a.zip",
+ ],
)
new_http_archive(
- name = "webcomponentsjs",
- build_file = "bower.BUILD",
- url = "https://github.com/webcomponents/webcomponentsjs/archive/v0.7.22.tar.gz",
- strip_prefix = "webcomponentsjs-0.7.22",
-)
-
-http_file(
- name = "weblas_weblas_js",
- url = "https://raw.githubusercontent.com/waylonflinn/weblas/v0.9.0/dist/weblas.js",
+ name = "stylize",
+ build_file = "models.BUILD",
+ sha256 = "3d374a730aef330424a356a8d4f04d8a54277c425e274ecb7d9c83aa912c6bfa",
+ urls = [
+ "http://storage.googleapis.com/download.tensorflow.org/models/stylize_v1.zip",
+ "http://download.tensorflow.org/models/stylize_v1.zip",
+ ],
)
diff --git a/bower.BUILD b/bower.BUILD
deleted file mode 100644
index eabd1d6450..0000000000
--- a/bower.BUILD
+++ /dev/null
@@ -1,645 +0,0 @@
-# AUTOGENERATED FILE by tensorboard_bower_dependency_sync.py
-
-package(default_visibility = ["//visibility:public"])
-
-filegroup(
- name = "d3",
- srcs = [
- "d3.js",
- "d3.min.js",
- "package.js",
- ],
-)
-
-filegroup(
- name = "dagre",
- srcs = [
- "dist/dagre.core.js",
- "dist/dagre.core.min.js",
- ],
-)
-
-filegroup(
- name = "es6_promise",
- srcs = [
- "promise.js",
- "promise.min.js",
- ],
-)
-
-filegroup(
- name = "font_roboto",
- srcs = ["roboto.html"],
-)
-
-filegroup(
- name = "graphlib",
- srcs = [
- "dist/graphlib.core.js",
- "dist/graphlib.core.min.js",
- ],
-)
-
-filegroup(
- name = "iron_a11y_announcer",
- srcs = [
- "index.html",
- "iron-a11y-announcer.html",
- ],
-)
-
-filegroup(
- name = "iron_a11y_keys_behavior",
- srcs = [
- "index.html",
- "iron-a11y-keys-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_ajax",
- srcs = [
- "index.html",
- "iron-ajax.html",
- "iron-request.html",
- ],
-)
-
-filegroup(
- name = "iron_autogrow_textarea",
- srcs = [
- "index.html",
- "iron-autogrow-textarea.html",
- ],
-)
-
-filegroup(
- name = "iron_behaviors",
- srcs = [
- "index.html",
- "iron-button-state.html",
- "iron-control-state.html",
- ],
-)
-
-filegroup(
- name = "iron_checked_element_behavior",
- srcs = [
- "index.html",
- "iron-checked-element-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_collapse",
- srcs = [
- "index.html",
- "iron-collapse.html",
- ],
-)
-
-filegroup(
- name = "iron_dropdown",
- srcs = [
- "index.html",
- "iron-dropdown.html",
- "iron-dropdown-scroll-manager.html",
- ],
-)
-
-filegroup(
- name = "iron_fit_behavior",
- srcs = [
- "index.html",
- "iron-fit-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_flex_layout",
- srcs = [
- "classes/iron-flex-layout.html",
- "classes/iron-shadow-flex-layout.html",
- "index.html",
- "iron-flex-layout.html",
- "iron-flex-layout-classes.html",
- ],
-)
-
-filegroup(
- name = "iron_form_element_behavior",
- srcs = [
- "index.html",
- "iron-form-element-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_icon",
- srcs = [
- "index.html",
- "iron-icon.html",
- ],
-)
-
-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 = [
- "index.html",
- "iron-iconset-svg.html",
- ],
-)
-
-filegroup(
- name = "iron_input",
- srcs = [
- "index.html",
- "iron-input.html",
- ],
-)
-
-filegroup(
- name = "iron_list",
- srcs = [
- "index.html",
- "iron-list.html",
- "test/smoke/avg-worst-case.html",
- "test/smoke/dummy-data.html",
- "test/smoke/index.html",
- "test/smoke/physical-count.html",
- ],
-)
-
-filegroup(
- name = "iron_menu_behavior",
- srcs = [
- "index.html",
- "iron-menu-behavior.html",
- "iron-menubar-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_meta",
- srcs = [
- "index.html",
- "iron-meta.html",
- ],
-)
-
-filegroup(
- name = "iron_overlay_behavior",
- srcs = [
- "index.html",
- "iron-focusables-helper.html",
- "iron-overlay-backdrop.html",
- "iron-overlay-behavior.html",
- "iron-overlay-manager.html",
- ],
-)
-
-filegroup(
- name = "iron_range_behavior",
- srcs = [
- "index.html",
- "iron-range-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_resizable_behavior",
- srcs = [
- "demo/src/x-app.html",
- "index.html",
- "iron-resizable-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_scroll_target_behavior",
- srcs = [
- "index.html",
- "iron-scroll-target-behavior.html",
- ],
-)
-
-filegroup(
- name = "iron_selector",
- srcs = [
- "index.html",
- "iron-multi-selectable.html",
- "iron-selectable.html",
- "iron-selection.html",
- "iron-selector.html",
- ],
-)
-
-filegroup(
- name = "iron_validatable_behavior",
- srcs = [
- "index.html",
- "iron-validatable-behavior.html",
- ],
-)
-
-filegroup(
- name = "lodash",
- srcs = [
- "lodash.js",
- "lodash.min.js",
- ],
-)
-
-filegroup(
- name = "neon_animation",
- srcs = [
- "animations/cascaded-animation.html",
- "animations/fade-in-animation.html",
- "animations/fade-out-animation.html",
- "animations/hero-animation.html",
- "animations/opaque-animation.html",
- "animations/reverse-ripple-animation.html",
- "animations/ripple-animation.html",
- "animations/scale-down-animation.html",
- "animations/scale-up-animation.html",
- "animations/slide-down-animation.html",
- "animations/slide-from-bottom-animation.html",
- "animations/slide-from-left-animation.html",
- "animations/slide-from-right-animation.html",
- "animations/slide-from-top-animation.html",
- "animations/slide-left-animation.html",
- "animations/slide-right-animation.html",
- "animations/slide-up-animation.html",
- "animations/transform-animation.html",
- "demo/card/index.html",
- "demo/card/x-card.html",
- "demo/card/x-cards-list.html",
- "demo/declarative/index.html",
- "demo/doc/index.html",
- "demo/doc/my-animatable.html",
- "demo/doc/my-dialog.html",
- "demo/dropdown/animated-dropdown.html",
- "demo/dropdown/index.html",
- "demo/grid/animated-grid.html",
- "demo/grid/fullsize-page-with-card.html",
- "demo/grid/index.html",
- "demo/list/full-view.html",
- "demo/list/index.html",
- "demo/list/list-demo.html",
- "demo/list/list-view.html",
- "demo/load/animated-grid.html",
- "demo/load/full-page.html",
- "demo/load/index.html",
- "demo/reprojection/animated-grid.html",
- "demo/reprojection/fullsize-page-with-card.html",
- "demo/reprojection/index.html",
- "demo/reprojection/reprojected-pages.html",
- "demo/tiles/circles-page.html",
- "demo/tiles/index.html",
- "demo/tiles/squares-page.html",
- "index.html",
- "neon-animatable.html",
- "neon-animatable-behavior.html",
- "neon-animated-pages.html",
- "neon-animation.html",
- "neon-animation-behavior.html",
- "neon-animation-runner-behavior.html",
- "neon-animations.html",
- "neon-shared-element-animatable-behavior.html",
- "neon-shared-element-animation-behavior.html",
- "web-animations.html",
- ],
-)
-
-filegroup(
- name = "paper_behaviors",
- srcs = [
- "index.html",
- "paper-button-behavior.html",
- "paper-checked-element-behavior.html",
- "paper-inky-focus-behavior.html",
- "paper-ripple-behavior.html",
- ],
-)
-
-filegroup(
- name = "paper_button",
- srcs = [
- "index.html",
- "paper-button.html",
- ],
-)
-
-filegroup(
- name = "paper_checkbox",
- srcs = [
- "index.html",
- "paper-checkbox.html",
- ],
-)
-
-filegroup(
- name = "paper_dialog",
- srcs = [
- "index.html",
- "paper-dialog.html",
- ],
-)
-
-filegroup(
- name = "paper_dialog_behavior",
- srcs = [
- "index.html",
- "paper-dialog-behavior.html",
- "paper-dialog-common.css",
- "paper-dialog-shared-styles.html",
- ],
-)
-
-filegroup(
- name = "paper_dialog_scrollable",
- srcs = [
- "index.html",
- "paper-dialog-scrollable.html",
- ],
-)
-
-filegroup(
- name = "paper_dropdown_menu",
- srcs = [
- "index.html",
- "paper-dropdown-menu.html",
- "paper-dropdown-menu-icons.html",
- "paper-dropdown-menu-light.html",
- "paper-dropdown-menu-shared-styles.html",
- ],
-)
-
-filegroup(
- name = "paper_header_panel",
- srcs = [
- "index.html",
- "paper-header-panel.html",
- ],
-)
-
-filegroup(
- name = "paper_icon_button",
- srcs = [
- "index.html",
- "paper-icon-button.html",
- "paper-icon-button-light.html",
- ],
-)
-
-filegroup(
- name = "paper_input",
- srcs = [
- "all-imports.html",
- "index.html",
- "paper-input.html",
- "paper-input-addon-behavior.html",
- "paper-input-behavior.html",
- "paper-input-char-counter.html",
- "paper-input-container.html",
- "paper-input-error.html",
- "paper-textarea.html",
- ],
-)
-
-filegroup(
- name = "paper_item",
- srcs = [
- "all-imports.html",
- "index.html",
- "paper-icon-item.html",
- "paper-item.html",
- "paper-item-behavior.html",
- "paper-item-body.html",
- "paper-item-shared-styles.html",
- ],
-)
-
-filegroup(
- name = "paper_listbox",
- srcs = [
- "index.html",
- "paper-listbox.html",
- ],
-)
-
-filegroup(
- name = "paper_material",
- srcs = [
- "index.html",
- "paper-material.html",
- "paper-material-shared-styles.html",
- ],
-)
-
-filegroup(
- name = "paper_menu",
- srcs = [
- "index.html",
- "paper-menu.html",
- "paper-menu-shared-styles.html",
- "paper-submenu.html",
- ],
-)
-
-filegroup(
- name = "paper_menu_button",
- srcs = [
- "index.html",
- "paper-menu-button.html",
- "paper-menu-button-animations.html",
- ],
-)
-
-filegroup(
- name = "paper_progress",
- srcs = [
- "index.html",
- "paper-progress.html",
- ],
-)
-
-filegroup(
- name = "paper_radio_button",
- srcs = [
- "index.html",
- "paper-radio-button.html",
- ],
-)
-
-filegroup(
- name = "paper_radio_group",
- srcs = [
- "index.html",
- "paper-radio-group.html",
- ],
-)
-
-filegroup(
- name = "paper_ripple",
- srcs = [
- "index.html",
- "paper-ripple.html",
- ],
-)
-
-filegroup(
- name = "paper_slider",
- srcs = [
- "index.html",
- "paper-slider.html",
- ],
-)
-
-filegroup(
- name = "paper_spinner",
- srcs = [
- "index.html",
- "paper-spinner.html",
- "paper-spinner-behavior.html",
- "paper-spinner-lite.html",
- "paper-spinner-styles.html",
- ],
-)
-
-filegroup(
- name = "paper_styles",
- srcs = [
- "classes/global.html",
- "classes/shadow.html",
- "classes/shadow-layout.html",
- "classes/typography.html",
- "color.html",
- "default-theme.html",
- "demo.css",
- "demo-pages.html",
- "index.html",
- "paper-styles.html",
- "paper-styles-classes.html",
- "shadow.html",
- "typography.html",
- ],
-)
-
-filegroup(
- name = "paper_tabs",
- srcs = [
- "index.html",
- "paper-tab.html",
- "paper-tabs.html",
- "paper-tabs-icons.html",
- ],
-)
-
-filegroup(
- name = "paper_toast",
- srcs = [
- "index.html",
- "paper-toast.html",
- ],
-)
-
-filegroup(
- name = "paper_toggle_button",
- srcs = [
- "index.html",
- "paper-toggle-button.html",
- ],
-)
-
-filegroup(
- name = "paper_toolbar",
- srcs = [
- "index.html",
- "paper-toolbar.html",
- ],
-)
-
-filegroup(
- name = "paper_tooltip",
- srcs = [
- "index.html",
- "paper-tooltip.html",
- ],
-)
-
-filegroup(
- name = "plottable",
- srcs = [
- "plottable.css",
- "plottable.js",
- "plottable.min.js",
- ],
-)
-
-filegroup(
- name = "polymer",
- srcs = [
- "polymer.html",
- "polymer-micro.html",
- "polymer-mini.html",
- ],
-)
-
-filegroup(
- name = "promise_polyfill",
- srcs = [
- "Gruntfile.js",
- "Promise.js",
- "Promise.min.js",
- "Promise-Statics.js",
- "promise-polyfill.html",
- "promise-polyfill-lite.html",
- ],
-)
-
-filegroup(
- name = "web_animations_js",
- srcs = [
- "web-animations.html",
- "web-animations.min.js",
- "web-animations-next.min.js",
- "web-animations-next-lite.min.js",
- ],
-)
-
-filegroup(
- name = "webcomponentsjs",
- srcs = [
- "CustomElements.js",
- "CustomElements.min.js",
- "HTMLImports.js",
- "HTMLImports.min.js",
- "MutationObserver.js",
- "MutationObserver.min.js",
- "ShadowDOM.js",
- "ShadowDOM.min.js",
- "webcomponents.js",
- "webcomponents.min.js",
- "webcomponents-lite.js",
- "webcomponents-lite.min.js",
- ],
-)
diff --git a/tensorflow/BUILD b/tensorflow/BUILD
index 8f67ae25df..acd5c108f9 100644
--- a/tensorflow/BUILD
+++ b/tensorflow/BUILD
@@ -321,7 +321,6 @@ filegroup(
"//tensorflow/python/saved_model:all_files",
"//tensorflow/python/tools:all_files",
"//tensorflow/tensorboard:all_files",
- "//tensorflow/tensorboard/app:all_files",
"//tensorflow/tensorboard/backend:all_files",
"//tensorflow/tensorboard/backend/event_processing:all_files",
"//tensorflow/tensorboard/components:all_files",
@@ -404,6 +403,8 @@ filegroup(
"//tensorflow/tensorboard/components/vz_distribution_chart:all_files",
"//tensorflow/tensorboard/components/vz_distribution_chart/demo:all_files",
"//tensorflow/tensorboard/components/vz_distribution_chart_d3v4:all_files",
+ "//tensorflow/tensorboard/components/vz_heatmap:all_files",
+ "//tensorflow/tensorboard/components/vz_heatmap_d3v4:all_files",
"//tensorflow/tensorboard/components/vz_histogram_timeseries:all_files",
"//tensorflow/tensorboard/components/vz_histogram_timeseries/demo:all_files",
"//tensorflow/tensorboard/components/vz_histogram_timeseries_d3v4:all_files",
@@ -419,7 +420,6 @@ filegroup(
"//tensorflow/tensorboard/components/vz_sorting_d3v4/test:all_files",
"//tensorflow/tensorboard/demo:all_files",
"//tensorflow/tensorboard/java/org/tensorflow/tensorboard/vulcanize:all_files",
- "//tensorflow/tensorboard/lib:all_files",
"//tensorflow/tensorboard/plugins:all_files",
"//tensorflow/tensorboard/plugins/projector:all_files",
"//tensorflow/tensorboard/plugins/text:all_files",
diff --git a/tensorflow/contrib/cmake/CMakeLists.txt b/tensorflow/contrib/cmake/CMakeLists.txt
index 83b405fd4e..62afa9481b 100644
--- a/tensorflow/contrib/cmake/CMakeLists.txt
+++ b/tensorflow/contrib/cmake/CMakeLists.txt
@@ -261,7 +261,6 @@ if(tensorflow_BUILD_CC_EXAMPLE)
endif()
include(tf_tools.cmake)
if(tensorflow_BUILD_PYTHON_BINDINGS)
- include(tensorboard)
include(tf_python.cmake)
endif()
if(tensorflow_BUILD_SHARED_LIB)
diff --git a/tensorflow/contrib/cmake/external/tensorboard.cmake b/tensorflow/contrib/cmake/external/tensorboard.cmake
deleted file mode 100644
index 457868c72b..0000000000
--- a/tensorflow/contrib/cmake/external/tensorboard.cmake
+++ /dev/null
@@ -1,148 +0,0 @@
-# Copyright 2017 The TensorFlow Authors. 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.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-# ==============================================================================
-include (ExternalProject)
-
-set(tensorboard_dependencies)
-add_custom_target(tensorboard_copy_dependencies)
-
-function(tb_new_http_archive)
- cmake_parse_arguments(_TB "" "NAME;URL" "FILES" ${ARGN})
- ExternalProject_Add(${_TB_NAME}
- PREFIX ${_TB_NAME}
- URL ${_TB_URL}
- DOWNLOAD_DIR "${DOWNLOAD_LOCATION}/${_TB_NAME}"
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- )
-
- set(src_dir "${CMAKE_CURRENT_BINARY_DIR}/${_TB_NAME}/src/${_TB_NAME}")
- set(dst_dir "${CMAKE_CURRENT_BINARY_DIR}/tensorboard_external/${_TB_NAME}")
-
- foreach(src_file ${_TB_FILES})
- add_custom_command(
- TARGET tensorboard_copy_dependencies PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src_dir}/${src_file} ${dst_dir}/${src_file}
- )
- endforeach()
-
- set(tensorboard_dependencies ${tensorboard_dependencies} ${_TB_NAME} PARENT_SCOPE)
-endfunction()
-
-function(tb_http_file)
- cmake_parse_arguments(_TB "" "NAME;URL" "" ${ARGN})
- get_filename_component(src_file ${_TB_URL} NAME)
- file(DOWNLOAD ${_TB_URL} "${DOWNLOAD_LOCATION}/${_TB_NAME}/${src_file}")
-
- set(src_dir "${DOWNLOAD_LOCATION}/${_TB_NAME}")
- set(dst_dir "${CMAKE_CURRENT_BINARY_DIR}/tensorboard_external/${_TB_NAME}/file")
-
- add_custom_command(
- TARGET tensorboard_copy_dependencies PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src_dir}/${src_file} ${dst_dir}/${src_file}
- )
-
- add_custom_target(${_TB_NAME} DEPENDS ${src_dir}/${src_file})
- set(tensorboard_dependencies ${tensorboard_dependencies} ${_TB_NAME} PARENT_SCOPE)
-endfunction()
-
-# Parse TensorBoard dependency names and URLs from Bazel's WORKSPACE file.
-set(tb_dep_names)
-file(STRINGS ${PROJECT_SOURCE_DIR}/../../../WORKSPACE workspace_contents)
-foreach(line ${workspace_contents})
- if(line MATCHES "# TENSORBOARD_BOWER_AUTOGENERATED_BELOW_THIS_LINE_DO_NOT_EDIT")
- set(tb_deps_started 1)
- endif()
-
- if(NOT tb_deps_started)
- continue()
- endif()
-
- if(line MATCHES "new_http_archive\\(")
- set(tb_dep_is_archive 1)
- continue()
- elseif(line MATCHES "http_file\\(")
- set(tb_dep_is_archive 0)
- continue()
- endif()
-
- string(REGEX MATCH "name.*=.*\"(.*)\"" has_name ${line})
- if(has_name)
- set(tb_dep_name ${CMAKE_MATCH_1})
- continue()
- endif()
-
- string(REGEX MATCH "url.*=.*\"(.*)\"" has_url ${line})
- if(has_url)
- list(APPEND tb_dep_names ${tb_dep_name})
- set(${tb_dep_name}_is_archive ${tb_dep_is_archive})
- set(${tb_dep_name}_url ${CMAKE_MATCH_1})
- endif()
-endforeach()
-
-# Parse the files needed for each TensorBoard dependency from Bazel's bower.BUILD file.
-# Due to CMAKE quirkiness, cannot use file(strings) with files that contain '[' and ']'.
-file(READ ${PROJECT_SOURCE_DIR}/../../../bower.BUILD bower_build_contents)
-string(REPLACE "\[" "OB" bower_build_contents "${bower_build_contents}")
-string(REPLACE "\]" "CB" bower_build_contents "${bower_build_contents}")
-string(REPLACE ";" "\\\\;" bower_build_contents "${bower_build_contents}")
-string(REPLACE "\n" "E;" bower_build_contents "${bower_build_contents}")
-foreach(line ${bower_build_contents})
- string(REGEX MATCH "name.*=.*\"(.*)\"" has_name ${line})
- if(has_name)
- set(tb_dep_name ${CMAKE_MATCH_1})
- set(${tb_dep_name}_files)
- continue()
- endif()
-
- string(REGEX MATCH "srcs.*=.*\"(.*)\"CB" has_single_line_src ${line})
- if(has_single_line_src)
- list(APPEND ${tb_dep_name}_files ${CMAKE_MATCH_1})
- continue()
- endif()
-
- if(line MATCHES "srcs.*=.*OB")
- set(inside_files_def 1)
- continue()
- elseif(line MATCHES "CB,")
- set(inside_files_def 0)
- continue()
- endif()
-
- if(inside_files_def)
- string(REGEX MATCH "\"(.*)\"," has_file ${line})
- if(has_file)
- list(APPEND ${tb_dep_name}_files ${CMAKE_MATCH_1})
- endif()
- endif()
-endforeach()
-
-# Generate a target for each dependency.
-foreach(tb_dep_name ${tb_dep_names})
- if (${tb_dep_name}_is_archive)
- tb_new_http_archive(
- NAME ${tb_dep_name}
- URL ${${tb_dep_name}_url}
- FILES ${${tb_dep_name}_files}
- )
- else()
- tb_http_file(
- NAME ${tb_dep_name}
- URL ${${tb_dep_name}_url}
- )
- endif()
-endforeach()
-
-add_dependencies(tensorboard_copy_dependencies ${tensorboard_dependencies})
diff --git a/tensorflow/contrib/cmake/tf_python.cmake b/tensorflow/contrib/cmake/tf_python.cmake
index b660508b8b..793f7b22f1 100755
--- a/tensorflow/contrib/cmake/tf_python.cmake
+++ b/tensorflow/contrib/cmake/tf_python.cmake
@@ -514,13 +514,6 @@ add_python_module("tensorflow/contrib/training/python/training")
add_python_module("tensorflow/contrib/util")
-# Additional directories with no Python sources.
-add_custom_command(TARGET tf_python_touchup_modules PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/dist")
-add_custom_command(TARGET tf_python_touchup_modules PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/lib/css")
-
-
########################################################
# tf_python_op_gen_main library
########################################################
@@ -859,7 +852,6 @@ endif(WIN32)
add_custom_target(tf_python_build_pip_package)
add_dependencies(tf_python_build_pip_package
pywrap_tensorflow_internal
- tensorboard_copy_dependencies
tf_python_copy_scripts_to_destination
tf_python_touchup_modules
tf_python_ops
@@ -887,23 +879,8 @@ add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
# Copy resources for TensorBoard.
add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${tensorflow_source_dir}/tensorflow/tensorboard/dist/bazel-html-imports.html
- ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/dist/)
-add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${tensorflow_source_dir}/tensorflow/tensorboard/dist/index.html
- ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/dist/)
-add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${tensorflow_source_dir}/tensorflow/tensorboard/dist/tf-tensorboard.html
- ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/dist/)
-add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${tensorflow_source_dir}/tensorflow/tensorboard/lib/css/global.css
- ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/lib/css/)
-add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${tensorflow_source_dir}/tensorflow/tensorboard/TAG
- ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/)
-add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/tensorboard_external
- ${CMAKE_CURRENT_BINARY_DIR}/tf_python/external)
+ ${CMAKE_CURRENT_BINARY_DIR}/tf_python/tensorflow/tensorboard/)
# Copy datasets for tf.contrib.learn.
add_custom_command(TARGET tf_python_build_pip_package POST_BUILD
diff --git a/tensorflow/tensorboard/.bowerrc b/tensorflow/tensorboard/.bowerrc
deleted file mode 100644
index 333544ec7a..0000000000
--- a/tensorflow/tensorboard/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory" : "components"
-} \ No newline at end of file
diff --git a/tensorflow/tensorboard/.gitignore b/tensorflow/tensorboard/.gitignore
deleted file mode 100644
index 98b9642545..0000000000
--- a/tensorflow/tensorboard/.gitignore
+++ /dev/null
@@ -1,27 +0,0 @@
-node_modules/*
-typings/*
-build/*
-dist/tf-tensorboard-demo.html
-
-# Since bower components are stored in the same directory as
-# tensorboard components, we ignore everything under components
-# except our own components which start with tf-.
-components/*
-# This rule should always be in sync with TF_COMPONENTS_TYPESCRIPT_GLOB
-# in gulpfile.js
-!components/tf-*
-!components/tf_*
-!components/vz-*
-!components/vz_*
-!components/index.html
-!components/BUILD
-# Ignore the sample graph files since they are too large to
-# be in the repo.
-components/tf-graph/demo/tf_model_zoo/*
-
-# All standalone code for TensorBoard components should be written in
-# typescript, and the compiled javascript code should be ignored.
-components/tf-*/**/*.js
-components/tf_*/**/*.js
-components/vz-*/**/*.js
-components/vz_*/**/*.js
diff --git a/tensorflow/tensorboard/BUILD b/tensorflow/tensorboard/BUILD
index 9772538524..11715319a0 100644
--- a/tensorflow/tensorboard/BUILD
+++ b/tensorflow/tensorboard/BUILD
@@ -1,39 +1,14 @@
# Description:
# TensorBoard, a dashboard for investigating TensorFlow
-package(
- default_visibility = ["//tensorflow:internal"],
- features = [
- "-layering_check",
- "-parse_headers",
- ],
-)
+package(default_visibility = ["//tensorflow:internal"])
licenses(["notice"]) # Apache 2.0
-exports_files(["LICENSE"])
-
-load("//tensorflow:tensorflow.bzl", "py_test")
-
-filegroup(
- name = "frontend",
- srcs = [
- "TAG",
- "dist/bazel-html-imports.html",
- "dist/index.html",
- "dist/tf-tensorboard.html",
- "//tensorflow/tensorboard/bower",
- "//tensorflow/tensorboard/lib:all_files",
- ],
-)
-
py_binary(
name = "tensorboard",
- srcs = [
- "__main__.py",
- "tensorboard.py",
- ],
- data = [":frontend"],
+ srcs = ["tensorboard.py"],
+ data = [":assets"],
srcs_version = "PY2AND3",
deps = [
"//tensorflow/python:platform",
@@ -46,15 +21,22 @@ py_binary(
)
filegroup(
+ name = "assets",
+ srcs = [
+ "TAG",
+ "//tensorflow/tensorboard/components:index.html",
+ ],
+)
+
+filegroup(
name = "all_files",
srcs = glob(
- ["**/*"],
+ ["**"],
exclude = [
- "**/METADATA",
- "**/OWNERS",
- "**/node_modules/**",
- "**/typings/**",
+ "METADATA",
+ "OWNERS",
+ "tensorboard.google.bzl",
],
),
- visibility = ["//tensorflow:__subpackages__"],
+ tags = ["notsan"],
)
diff --git a/tensorflow/tensorboard/DEVELOPMENT.md b/tensorflow/tensorboard/DEVELOPMENT.md
index 3ff2c87dab..8e86bf04db 100644
--- a/tensorflow/tensorboard/DEVELOPMENT.md
+++ b/tensorflow/tensorboard/DEVELOPMENT.md
@@ -2,125 +2,24 @@
## Launching a Development Instance
-The first step is getting a TensorBoard development environment set up. You
-should start by making sure you have [nodejs](https://nodejs.org/en/) and
-[npm](https://www.npmjs.com/). On Ubuntu, `sudo apt-get install -y nodejs
-nodejs-legacy npm`. Ensure your npm version is >=3.0 by running
-'npm --version'. If the version is <3.0, run 'sudo npm install npm -g' to
-update to the latest version. You may need to open a new terminal window after
-updating in order to make use of the newly-installed version.
+Run the following to launch a demo of TensorBoard in raw sources mode:
-Next, you'll want to install [gulp](http://gulpjs.com/) and
-[bower](http://bower.io/), which are used for build tooling and dependency
-management respectively. Both must be installed globally: `sudo npm install -g
-gulp bower` will do that.
+```sh
+bazel run third_party/tensorflow/tensorboard/components/tf_tensorboard_d3v4:demo
+```
-Then, cd into the TensorBoard directory:
-
-`cd tensorflow/tensorboard`
-
-and install dependencies:
-
-`npm run prep`
-
-Then, run gulp: `gulp`
-
-(Don't worry if there are some linter errors.)
-
-Now you can navigate to
-[http://localhost:8000/demo/index.html](http://localhost:8000/demo/index.html)
-and play with the demo TensorBoard instance. If you make changes to the source
-code, `gulp` should detect it, recompile (if Typescript), and reload your
-browser.
+Now you can navigate to <http://localhost:6006/demo/index.html> and play with
+the demo TensorBoard instance. This will have live source reloading.
This demo TensorBoard will have a small amount of demo data generated by
[generate_testdata.py](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tensorboard/scripts/generate_testdata.py).
You can use [serialize_tensorboard.py](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tensorboard/scripts/serialize_tensorboard.py)
to create a realistic demo directory from your own data files.
-## Launching TensorBoard with modified source
-
-If you are developing in open source, and have made some changes to TensorBoard
-that you'd like to try out on real data, then you need to regenerate
-`dist/tf-tensorboard.html`.
-
-Run `gulp regenerate`. That will recompile all of the TensorBoard assets, and
-produce a new tf-tensorboard.html with your changes.
-
-Now, you can use `bazel` to launch TensorBoard:
-
-`bazel run //tensorflow/tensorboard:tensorboard -- --logdir=/path/to/logs`.
-
-## Updating the vulcanized HTML file (for linux)
-
-The vulcanized HTML file `dist/tf-tensorboard.html.OPENSOURCE` is the version of
-Tensorboard started up by users who install TensorFlow via pip. Today, updating
-that file involves using gulp. Future efforts will streamline this process.
-
-First, `cd` into the `tensorflow/tensorboard` directory within a git repository
-(a piper client will not work). Run `npm run prepare`.
-
-Next, we build some third party JS dependencies via webfiles targets. Run
-
- bazel build \
- tensorflow/tensorboard/components/tf_imports:d3 \
- tensorflow/tensorboard/components/tf_imports:lodash \
- tensorflow/tensorboard/components/tf_imports:graphlib \
- tensorflow/tensorboard/components/tf_imports:dagre \
- tensorflow/tensorboard/components/tf_imports:plottable
-
-Users internal to Google should use the internal build tool instead. Move the
-output JS binaries into the tf_imports directory.
-
-Run `gulp vulcanize`. If compilation errors arise (such as those related to
-TypeScript), fix them and re-run. This step should update the contents of
-`dist/tf-tensorboard.html.OPENSOURCE`.
-
-Next, we perform some manual find-and-replaces on script `src` paths within
-`dist/tf-tensorboard.html.OPENSOURCE`. Manually replace:
-
-* `<script src="../tf-imports/d3.js"></script>` with `<script src="../d3/d3.js"></script>`
-* `<script src="../tf-imports/dagre.js"></script>` with `<script src="../dagre/dist/dagre.core.js"></script>`
-* `<script src="../tf-imports/graphlib.js"></script>` with `<script src="../graphlib/dist/graphlib.core.js"></script>`
-* `<script src="../tf-imports/lodash.js"></script>` with `<script src="../lodash/lodash.min.js"></script>`
-* `<script src="../tf-imports/plottable.js"></script>` with `<script src="../plottable/plottable.js"></script>`
-
-Also, remove duplicate instances of script includes. Each of those scripts
-should only be included once (the first time) within the vulcanized output.
-
-### Try out the vulcanized Tensorboard HTML output
-
-To test the vulcanized output, prepare a pip package within a virtualized
-environment, and run `tensorboard` after activating the environment.
-
-To do that, we first create and activate a virtual environment called say
-`tf_foo` (Pick your own name.).
-
- virtualenv --system-site-packages ~/tf_foo
- source ~/tf_foo/bin/activate
-
-Make sure that you have installed `pip` and `virtualenv` beforehand. If not, run
-
- sudo easy_install pip
- sudo pip install --upgrade virtualenv
-
-Next, we run this command from the `tensorflow directory`.
-
- tools/google/make_tree.sh --pip_dir=/tmp/pip_dir
-
-to create a pip package. If you are running within Google, also provide the
-`--pending_cl` flag. That script will generate a wheel file (.whl) within
-`/tmp/pip_dir`. Lets say that it is
-`tensorflow-1.0.0rc2-cp27-none-linux_x86_64.whl`.
-
-Run
-
- pip install --upgrade /tmp/pip_dir/tensorflow-1.0.0rc2-cp27-none-linux_x86_64.whl
+## Launching TensorBoard Proper
-to update the pip installation of TensorFlow within the virtual environment.
-Verify that the `tensorboard` command defers to the tensorboard instance
-installed within your virtual environment (`tf_foo`) by running
-`which tensorboard`. To run tensorboard, start it up as usual within the virtual
-environment:
+Running TensorBoard automatically asks Bazel to create a vulcanized HTML binary:
- tensorboard --logdir=/tmp/my/logdir
+```sh
+bazel run //tensorflow/tensorboard:tensorboard -- --logdir=/path/to/logs
+```
diff --git a/tensorflow/tensorboard/app/BUILD b/tensorflow/tensorboard/app/BUILD
deleted file mode 100644
index 9afcd23e9e..0000000000
--- a/tensorflow/tensorboard/app/BUILD
+++ /dev/null
@@ -1,19 +0,0 @@
-# Description:
-# Build rules for building the HTML/JS necessary for TensorBoard.
-package(default_visibility = ["//tensorflow:internal"])
-
-licenses(["notice"]) # Apache 2.0
-
-exports_files(["LICENSE"])
-
-filegroup(
- name = "all_files",
- srcs = glob(
- ["**/*"],
- exclude = [
- "**/METADATA",
- "**/OWNERS",
- ],
- ),
- visibility = ["//tensorflow:__subpackages__"],
-)
diff --git a/tensorflow/tensorboard/app/analytics.js b/tensorflow/tensorboard/app/analytics.js
deleted file mode 100644
index 497c02ced8..0000000000
--- a/tensorflow/tensorboard/app/analytics.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-// Nothing to see here. vulcanize doesn't like empty files.
diff --git a/tensorflow/tensorboard/backend/BUILD b/tensorflow/tensorboard/backend/BUILD
index d27a22a82b..adbdea5f45 100644
--- a/tensorflow/tensorboard/backend/BUILD
+++ b/tensorflow/tensorboard/backend/BUILD
@@ -57,7 +57,7 @@ py_test(
py_library(
name = "application",
srcs = ["application.py"],
- data = ["//tensorflow/tensorboard:frontend"],
+ data = ["//tensorflow/tensorboard:assets"],
srcs_version = "PY2AND3",
deps = [
":http_util",
diff --git a/tensorflow/tensorboard/backend/application.py b/tensorflow/tensorboard/backend/application.py
index 3c8963e302..c38ad92be6 100644
--- a/tensorflow/tensorboard/backend/application.py
+++ b/tensorflow/tensorboard/backend/application.py
@@ -24,7 +24,6 @@ from __future__ import print_function
import csv
import imghdr
-import mimetypes
import os
import re
import threading
@@ -159,8 +158,6 @@ class TensorBoardWSGIApp(object):
reload_multiplexer(self._multiplexer, path_to_run)
self.data_applications = {
- '/app.js':
- self._serve_js,
DATA_PREFIX + AUDIO_ROUTE:
self._serve_audio,
DATA_PREFIX + COMPRESSED_HISTOGRAMS_ROUTE:
@@ -545,59 +542,9 @@ class TensorBoardWSGIApp(object):
@wrappers.Request.application
def _serve_index(self, request):
"""Serves the index page (i.e., the tensorboard app itself)."""
- return self._serve_static_file(request, '/dist/index.html')
-
- @wrappers.Request.application
- def _serve_js(self, request):
- """Serves the JavaScript for the index page."""
- return self._serve_static_file(request, '/dist/app.js')
-
- def _serve_static_file(self, request, path):
- """Serves the static file located at the given path.
-
- Args:
- request: A werkzeug Request
- path: The path of the static file, relative to the tensorboard/ directory.
-
- Returns:
- A werkzeug.Response application.
- """
- # Strip off the leading forward slash.
- orig_path = path.lstrip('/')
- if not self._path_is_safe(orig_path):
- logging.warning('path not safe: %s', orig_path)
- return http_util.Respond(request, 'Naughty naughty!', 'text/plain', 400)
- # Resource loader wants a path relative to //WORKSPACE/tensorflow.
- path = os.path.join('tensorboard', orig_path)
- # Open the file and read it.
- try:
- contents = resource_loader.load_resource(path)
- except IOError:
- # For compatibility with latest version of Bazel, we renamed bower
- # packages to use '_' rather than '-' in their package name.
- # This means that the directory structure is changed too.
- # So that all our recursive imports work, we need to modify incoming
- # requests to map onto the new directory structure.
- path = orig_path
- components = path.split('/')
- components[0] = components[0].replace('-', '_')
- path = ('/').join(components)
- # Bazel keeps all the external dependencies in //WORKSPACE/external.
- # and resource loader wants a path relative to //WORKSPACE/tensorflow/.
- path = os.path.join('../external', path)
- try:
- contents = resource_loader.load_resource(path)
- except IOError:
- logging.warning('path %s not found, sending 404', path)
- return http_util.Respond(request, 'Not found', 'text/plain', code=404)
- mimetype, content_encoding = mimetypes.guess_type(path)
- mimetype = mimetype or 'application/octet-stream'
- return http_util.Respond(
- request,
- contents,
- mimetype,
- expires=3600,
- content_encoding=content_encoding)
+ contents = resource_loader.load_resource(
+ 'tensorboard/components/index.html')
+ return http_util.Respond(request, contents, 'text/html', expires=3600)
def __call__(self, environ, start_response): # pylint: disable=invalid-name
"""Central entry point for the TensorBoard application.
@@ -628,8 +575,9 @@ class TensorBoardWSGIApp(object):
elif clean_path in TAB_ROUTES:
return self._serve_index(environ, start_response)
else:
- return self._serve_static_file(request, clean_path)(environ,
- start_response)
+ logging.warning('path %s not found, sending 404', clean_path)
+ return http_util.Respond(request, 'Not found', 'text/plain', code=404)(
+ environ, start_response)
# pylint: enable=too-many-function-args
diff --git a/tensorflow/tensorboard/backend/application_test.py b/tensorflow/tensorboard/backend/application_test.py
index 4ea627def7..e8556c0c61 100644
--- a/tensorflow/tensorboard/backend/application_test.py
+++ b/tensorflow/tensorboard/backend/application_test.py
@@ -154,11 +154,6 @@ class TensorboardServerTest(test.TestCase):
response = self._get('/asdf')
self.assertEqual(response.status, 404)
- def testDirectoryTraversal(self):
- """Attempt a directory traversal attack."""
- response = self._get('/..' * 30 + '/etc/passwd')
- self.assertEqual(response.status, 400)
-
def testLogdir(self):
"""Test the format of the data/logdir endpoint."""
parsed_object = self._getJson('/data/logdir')
diff --git a/tensorflow/tensorboard/bower.json b/tensorflow/tensorboard/bower.json
deleted file mode 100644
index 0a0fac45fd..0000000000
--- a/tensorflow/tensorboard/bower.json
+++ /dev/null
@@ -1,187 +0,0 @@
-{
- "__autoadded_transitive_dep__": [
- "font-roboto",
- "iron-a11y-announcer",
- "iron-a11y-keys-behavior",
- "iron-autogrow-textarea",
- "iron-checked-element-behavior",
- "iron-dropdown",
- "iron-fit-behavior",
- "iron-flex-layout",
- "iron-form-element-behavior",
- "iron-icon",
- "iron-iconset-svg",
- "iron-input",
- "iron-menu-behavior",
- "iron-meta",
- "iron-overlay-behavior",
- "iron-range-behavior",
- "iron-resizable-behavior",
- "iron-scroll-target-behavior",
- "iron-validatable-behavior",
- "neon-animation",
- "paper-dialog-behavior",
- "paper-material",
- "paper-menu-button",
- "paper-ripple",
- "promise-polyfill",
- "web-animations-js",
- "webcomponentsjs"
- ],
- "authors": [
- "Google"
- ],
- "dependencies": {
- "d3": "3.5.15",
- "dagre": "0.7.4",
- "es6-promise": "2.1.0",
- "font-roboto": "PolymerElements/font-roboto#1.0.1",
- "graphlib": "1.0.7",
- "iron-a11y-announcer": "PolymerElements/iron-a11y-announcer#1.0.5",
- "iron-a11y-keys-behavior": "PolymerElements/iron-a11y-keys-behavior#1.1.8",
- "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.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",
- "iron-fit-behavior": "PolymerElements/iron-fit-behavior#1.2.5",
- "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.11",
- "iron-icons": "PolymerElements/iron-icons#1.1.3",
- "iron-iconset-svg": "PolymerElements/iron-iconset-svg#1.1.0",
- "iron-input": "PolymerElements/iron-input#1.0.10",
- "iron-list": "PolymerElements/iron-list#1.3.9",
- "iron-menu-behavior": "PolymerElements/iron-menu-behavior#1.1.10",
- "iron-meta": "PolymerElements/iron-meta#1.1.1",
- "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#1.10.1",
- "iron-range-behavior": "PolymerElements/iron-range-behavior#1.0.4",
- "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#1.0.3",
- "iron-scroll-target-behavior": "PolymerElements/iron-scroll-target-behavior#1.0.3",
- "iron-selector": "PolymerElements/iron-selector#1.5.2",
- "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#1.1.1",
- "lodash": "3.8.0",
- "neon-animation": "PolymerElements/neon-animation#1.2.2",
- "numericjs": "1.2.6",
- "paper-behaviors": "PolymerElements/paper-behaviors#1.0.12",
- "paper-button": "PolymerElements/paper-button#1.0.11",
- "paper-checkbox": "PolymerElements/paper-checkbox#1.4.0",
- "paper-dialog": "PolymerElements/paper-dialog#1.0.4",
- "paper-dialog-behavior": "PolymerElements/paper-dialog-behavior#1.2.5",
- "paper-dialog-scrollable": "PolymerElements/paper-dialog-scrollable#1.1.5",
- "paper-dropdown-menu": "PolymerElements/paper-dropdown-menu#1.4.0",
- "paper-header-panel": "PolymerElements/paper-header-panel#1.1.4",
- "paper-icon-button": "PolymerElements/paper-icon-button#1.1.3",
- "paper-input": "PolymerElements/paper-input#1.1.18",
- "paper-item": "PolymerElements/paper-item#1.1.4",
- "paper-listbox": "PolymerElements/paper-listbox#1.1.2",
- "paper-material": "PolymerElements/paper-material#1.0.6",
- "paper-menu": "PolymerElements/paper-menu#1.2.2",
- "paper-menu-button": "PolymerElements/paper-menu-button#1.5.1",
- "paper-progress": "PolymerElements/paper-progress#1.0.9",
- "paper-radio-button": "PolymerElements/paper-radio-button#1.1.2",
- "paper-radio-group": "PolymerElements/paper-radio-group#1.0.9",
- "paper-ripple": "PolymerElements/paper-ripple#1.0.5",
- "paper-slider": "PolymerElements/paper-slider#1.0.10",
- "paper-spinner": "PolymerElements/paper-spinner#1.1.1",
- "paper-styles": "PolymerElements/paper-styles#1.1.4",
- "paper-tabs": "PolymerElements/paper-tabs#1.7.0",
- "paper-toast": "PolymerElements/paper-toast#1.3.0",
- "paper-toggle-button": "PolymerElements/paper-toggle-button#1.2.0",
- "paper-toolbar": "PolymerElements/paper-toolbar#1.1.4",
- "paper-tooltip": "PolymerElements/paper-tooltip#1.1.2",
- "plottable": "1.16.1",
- "polymer": "1.7.0",
- "promise-polyfill": "polymerlabs/promise-polyfill#1.0.0",
- "three.js": "threejs#r77",
- "web-animations-js": "web-animations/web-animations-js#2.2.1",
- "webcomponentsjs": "webcomponents/webcomponentsjs#0.7.22",
- "weblas": "0.9.0"
- },
- "description": "TensorBoard: Visualizations for TensorFlow",
- "devDependencies": {
- "iron-component-page": "PolymerElements/iron-component-page#^1.1.4",
- "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.3",
- "web-component-tester": "Polymer/web-component-tester"
- },
- "ignore": [
- "**/.*",
- "node_modules",
- "bower_components",
- "test",
- "tests"
- ],
- "license": "Apache-2.0",
- "name": "tensorboard",
- "private": true,
- "resolutions": {
- "d3": "3.5.15",
- "dagre": "0.7.4",
- "es6-promise": "2.1.0",
- "font-roboto": "1.0.1",
- "graphlib": "1.0.7",
- "iron-a11y-announcer": "1.0.5",
- "iron-a11y-keys-behavior": "1.1.8",
- "iron-ajax": "1.2.0",
- "iron-autogrow-textarea": "1.0.12",
- "iron-behaviors": "1.0.17",
- "iron-checked-element-behavior": "1.0.4",
- "iron-collapse": "1.0.8",
- "iron-dropdown": "1.4.0",
- "iron-fit-behavior": "1.2.5",
- "iron-flex-layout": "1.3.0",
- "iron-form-element-behavior": "1.0.6",
- "iron-icon": "1.0.11",
- "iron-icons": "1.1.3",
- "iron-iconset-svg": "1.1.0",
- "iron-input": "1.0.10",
- "iron-list": "1.3.9",
- "iron-menu-behavior": "1.1.10",
- "iron-meta": "1.1.1",
- "iron-overlay-behavior": "1.10.1",
- "iron-range-behavior": "1.0.4",
- "iron-resizable-behavior": "1.0.3",
- "iron-scroll-target-behavior": "1.0.3",
- "iron-selector": "1.5.2",
- "iron-validatable-behavior": "1.1.1",
- "lodash": "3.8.0",
- "neon-animation": "1.2.2",
- "numericjs": "1.2.6",
- "paper-behaviors": "1.0.12",
- "paper-button": "1.0.11",
- "paper-checkbox": "1.4.0",
- "paper-dialog": "1.0.4",
- "paper-dialog-behavior": "1.2.5",
- "paper-dialog-scrollable": "1.1.5",
- "paper-dropdown-menu": "1.4.0",
- "paper-header-panel": "1.1.4",
- "paper-icon-button": "1.1.3",
- "paper-input": "1.1.18",
- "paper-item": "1.1.4",
- "paper-listbox": "1.1.2",
- "paper-material": "1.0.6",
- "paper-menu": "1.2.2",
- "paper-menu-button": "1.5.1",
- "paper-progress": "1.0.9",
- "paper-radio-button": "1.1.2",
- "paper-radio-group": "1.0.9",
- "paper-ripple": "1.0.5",
- "paper-slider": "1.0.10",
- "paper-spinner": "1.1.1",
- "paper-styles": "1.1.4",
- "paper-tabs": "1.7.0",
- "paper-toast": "1.3.0",
- "paper-toggle-button": "1.2.0",
- "paper-toolbar": "1.1.4",
- "paper-tooltip": "1.1.2",
- "plottable": "1.16.1",
- "polymer": "1.7.0",
- "promise-polyfill": "1.0.0",
- "three.js": "threejs#r77",
- "web-animations-js": "2.2.1",
- "webcomponentsjs": "0.7.22",
- "weblas": "0.9.0"
- },
- "version": "0.0.0"
-}
diff --git a/tensorflow/tensorboard/bower/BUILD b/tensorflow/tensorboard/bower/BUILD
deleted file mode 100644
index 2c2921d988..0000000000
--- a/tensorflow/tensorboard/bower/BUILD
+++ /dev/null
@@ -1,76 +0,0 @@
-# AUTOGENERATED FILE by tensorboard_bower_dependency_sync.py
-
-package(default_visibility = ["//visibility:public"])
-
-filegroup(
- name = "bower",
- srcs = [
- "@d3//:d3",
- "@dagre//:dagre",
- "@es6_promise//:es6_promise",
- "@font_roboto//:font_roboto",
- "@graphlib//:graphlib",
- "@iron_a11y_announcer//:iron_a11y_announcer",
- "@iron_a11y_keys_behavior//:iron_a11y_keys_behavior",
- "@iron_ajax//:iron_ajax",
- "@iron_autogrow_textarea//:iron_autogrow_textarea",
- "@iron_behaviors//:iron_behaviors",
- "@iron_checked_element_behavior//:iron_checked_element_behavior",
- "@iron_collapse//:iron_collapse",
- "@iron_dropdown//:iron_dropdown",
- "@iron_fit_behavior//:iron_fit_behavior",
- "@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",
- "@iron_menu_behavior//:iron_menu_behavior",
- "@iron_meta//:iron_meta",
- "@iron_overlay_behavior//:iron_overlay_behavior",
- "@iron_range_behavior//:iron_range_behavior",
- "@iron_resizable_behavior//:iron_resizable_behavior",
- "@iron_scroll_target_behavior//:iron_scroll_target_behavior",
- "@iron_selector//:iron_selector",
- "@iron_validatable_behavior//:iron_validatable_behavior",
- "@lodash//:lodash",
- "@neon_animation//:neon_animation",
- "@numericjs_numeric_min_js//file",
- "@paper_behaviors//:paper_behaviors",
- "@paper_button//:paper_button",
- "@paper_checkbox//:paper_checkbox",
- "@paper_dialog//:paper_dialog",
- "@paper_dialog_behavior//:paper_dialog_behavior",
- "@paper_dialog_scrollable//:paper_dialog_scrollable",
- "@paper_dropdown_menu//:paper_dropdown_menu",
- "@paper_header_panel//:paper_header_panel",
- "@paper_icon_button//:paper_icon_button",
- "@paper_input//:paper_input",
- "@paper_item//:paper_item",
- "@paper_listbox//:paper_listbox",
- "@paper_material//:paper_material",
- "@paper_menu//:paper_menu",
- "@paper_menu_button//:paper_menu_button",
- "@paper_progress//:paper_progress",
- "@paper_radio_button//:paper_radio_button",
- "@paper_radio_group//:paper_radio_group",
- "@paper_ripple//:paper_ripple",
- "@paper_slider//:paper_slider",
- "@paper_spinner//:paper_spinner",
- "@paper_styles//:paper_styles",
- "@paper_tabs//:paper_tabs",
- "@paper_toast//:paper_toast",
- "@paper_toggle_button//:paper_toggle_button",
- "@paper_toolbar//:paper_toolbar",
- "@paper_tooltip//:paper_tooltip",
- "@plottable//:plottable",
- "@polymer//:polymer",
- "@promise_polyfill//:promise_polyfill",
- "@three_js_orbitcontrols_js//file",
- "@three_js_three_min_js//file",
- "@web_animations_js//:web_animations_js",
- "@webcomponentsjs//:webcomponentsjs",
- "@weblas_weblas_js//file",
- ],
-)
diff --git a/tensorflow/tensorboard/components/BUILD b/tensorflow/tensorboard/components/BUILD
index 301425d96d..2d2c2d2d7b 100644
--- a/tensorflow/tensorboard/components/BUILD
+++ b/tensorflow/tensorboard/components/BUILD
@@ -1,23 +1,32 @@
package(default_visibility = ["//tensorflow:internal"])
+load("@io_bazel_rules_closure//closure:defs.bzl", "web_library")
+load("//tensorflow/tensorboard:vulcanize.bzl", "tensorboard_html_binary")
+
licenses(["notice"]) # Apache 2.0
-exports_files(["LICENSE"])
+web_library(
+ name = "tensorboard",
+ srcs = [
+ "analytics.html",
+ "tensorboard.html",
+ ],
+ path = "/",
+ deps = [
+ "//tensorflow/tensorboard/components/tf_tensorboard_d3v4",
+ "@org_polymer_webcomponentsjs",
+ ],
+)
+
+tensorboard_html_binary(
+ name = "index",
+ input_path = "/tensorboard.html",
+ output_path = "/index.html",
+ deps = [":tensorboard"],
+)
filegroup(
name = "all_files",
- srcs = glob(
- [
- "tf_*/**/*",
- "vz_*/**/*",
- ],
- exclude = [
- "**/tf_model_zoo/*",
- "**/METADATA",
- "**/OWNERS",
- ],
- ) + [
- "BUILD",
- ],
- visibility = ["//tensorflow:__subpackages__"],
+ srcs = glob(["**"]),
+ tags = ["notsan"],
)
diff --git a/tensorflow/tensorboard/components/tf_imports_google/graphlib.html b/tensorflow/tensorboard/components/analytics.html
index 56b37ebe4b..d319f576fc 100644
--- a/tensorflow/tensorboard/components/tf_imports_google/graphlib.html
+++ b/tensorflow/tensorboard/components/analytics.html
@@ -15,4 +15,4 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../graphlib-library/graphlib.html">
+<!-- TODO(jart): Give users the ability to opt-in to analytics. -->
diff --git a/tensorflow/tensorboard/components/index.html b/tensorflow/tensorboard/components/index.html
deleted file mode 100644
index c790a76f75..0000000000
--- a/tensorflow/tensorboard/components/index.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!doctype html>
-<!--
-@license
-Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
--->
-
-<html>
- <head>
- <meta charset="utf-8">
- <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
- <style>
- html, body {
- margin: 0;
- padding: 0;
- height: 100%;
- font-family: "RobotoDraft","Roboto",sans-serif;
- }
- </style>
- <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAMAAAD3eH5ZAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD/UExURfFlKfaELvFmKfNyK/67NvWALf68Nv69NvNxK/20NfyyNP22NfN0K/JrKvqhMv2zNf25Nf24Nf23NfeOL/yzNPyvNPJoKviWMPmeMfN1K/WBLfePL/FnKfeML/qlMvR7LPmcMfeLL/aJLvR5LPFoKfJuKvR3LP66NvywNPeNL/V/LfaILv21Nf26NfNzK/NvK/R6LPmaMfyxNPqfMvV+LfurM/iSMPmbMfJvKvmdMfumM/qiMvmZMfytNPJqKvysNPN2K/iYMPNwK/upM/JtKvJsKviVMPaHLvaGLvJpKvR8LPaKLvqkMvuqM/aFLvR4LPuoM/iTMPWDLfiRMPmYMXS0ngkAAALoSURBVHja7drnctpAFIbhFUISSKJ3MKYa0+y4xTW9937/15JkJhlTjhrSrHRmvuf/as6L0YLFCgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBJ6njenqspzgnPrsrGX9Zpi2tCrmnc6+dYNthVY5WpMmxQLWPdMsOuYVwzNj3ei2t3mQwaV43BJPDCS2NbJ5aEeuX/+9qcjQOtfFIkIkrvY2g4MVcmOBsFWbowKO/kNyj62gRpJcDaPBlxLr1B0zdG0C/8LzbJiJrshuvy1gzlA9+rD8mIkuyIJjFE3/dqnYwoSm7IUEPoD/wut8iIguSIDjlFxe/yfXL5vuSI21BTZLLhXoOILMO8Hxwa/L8bI0LfmUdhGowb2ZvT0e57pFNDgB06IlVyjmmIBl2T/nl9Rw6SD9GgSG/Q0uQkaW3XhmovKQ3eFQ4N2Uo9OQ1eFZsNerf7vP+rO4rhmY1Lg3vFVoP8+8BXg1sFnwbnCk4NThW8GuiKBDdkVVtTNFvNelVsNqTbyWnIOM2oeTRoyWvwmpJHg/ucXBrcJuXT4DwrpwZi2vy0VCx8YtXg/D2bU4OfiuQ3eFfE2KD4bfCqiLNB993gXsGlwa2CT4NzBacGIVQ6YsipQdh0xEdODUKjIxrSp88onZ8zbbFLg1DoiFO5BXvDGv2My9/JhUT8JUZTI0yDaNHLBzIbvqTDNYhUiVw/kdjQ1kM2CHFDPjKW+KzyRTF0g/ga9w9y+fANQpxvX8CU+Ny7FUWDeF3Y+g3lROIf4k0UDX9eCyvO531PyYhHga9zvPZJU5b73Y/eXj8Hv9D48n6HaF5LbcjRt8TZTtda5M1DfXnbkX1C0SHCFKzQB5Fe8op4GNGNHavvZESbVwT5r6W1xyuCPBY3Y9YgDqzknH/e3YfNzzuL30l0IebrZ5kKtuDIXt1n868ET6kf3/49tLvrCcZyF8Pu215dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcPIbNrBhOaBXucoAAAAASUVORK5CYII=">
- <link rel="import" href="tf-tensorboard/tf-tensorboard.html">
- <title>TensorBoard</title>
- </head>
- <body>
- <tf-tensorboard use-hash></tf-tensorboard>
- <script src="../app/analytics.js"></script>
- </body>
-</html>
diff --git a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/dist.html b/tensorflow/tensorboard/components/tensorboard.html
index 89bcfb733e..0652902cfa 100644
--- a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/dist.html
+++ b/tensorflow/tensorboard/components/tensorboard.html
@@ -16,17 +16,12 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
+<meta charset="utf-8">
<title>TensorBoard</title>
-<style>
- html, body {
- margin: 0;
- padding: 0;
- height: 100%;
- font-family: "RobotoDraft", "Roboto", sans-serif;
- }
-</style>
-<script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAMAAAD3eH5ZAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD/UExURfFlKfaELvFmKfNyK/67NvWALf68Nv69NvNxK/20NfyyNP22NfN0K/JrKvqhMv2zNf25Nf24Nf23NfeOL/yzNPyvNPJoKviWMPmeMfN1K/WBLfePL/FnKfeML/qlMvR7LPmcMfeLL/aJLvR5LPFoKfJuKvR3LP66NvywNPeNL/V/LfaILv21Nf26NfNzK/NvK/R6LPmaMfyxNPqfMvV+LfurM/iSMPmbMfJvKvmdMfumM/qiMvmZMfytNPJqKvysNPN2K/iYMPNwK/upM/JtKvJsKviVMPaHLvaGLvJpKvR8LPaKLvqkMvuqM/aFLvR4LPuoM/iTMPWDLfiRMPmYMXS0ngkAAALoSURBVHja7drnctpAFIbhFUISSKJ3MKYa0+y4xTW9937/15JkJhlTjhrSrHRmvuf/as6L0YLFCgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBJ6njenqspzgnPrsrGX9Zpi2tCrmnc6+dYNthVY5WpMmxQLWPdMsOuYVwzNj3ei2t3mQwaV43BJPDCS2NbJ5aEeuX/+9qcjQOtfFIkIkrvY2g4MVcmOBsFWbowKO/kNyj62gRpJcDaPBlxLr1B0zdG0C/8LzbJiJrshuvy1gzlA9+rD8mIkuyIJjFE3/dqnYwoSm7IUEPoD/wut8iIguSIDjlFxe/yfXL5vuSI21BTZLLhXoOILMO8Hxwa/L8bI0LfmUdhGowb2ZvT0e57pFNDgB06IlVyjmmIBl2T/nl9Rw6SD9GgSG/Q0uQkaW3XhmovKQ3eFQ4N2Uo9OQ1eFZsNerf7vP+rO4rhmY1Lg3vFVoP8+8BXg1sFnwbnCk4NThW8GuiKBDdkVVtTNFvNelVsNqTbyWnIOM2oeTRoyWvwmpJHg/ucXBrcJuXT4DwrpwZi2vy0VCx8YtXg/D2bU4OfiuQ3eFfE2KD4bfCqiLNB993gXsGlwa2CT4NzBacGIVQ6YsipQdh0xEdODUKjIxrSp88onZ8zbbFLg1DoiFO5BXvDGv2My9/JhUT8JUZTI0yDaNHLBzIbvqTDNYhUiVw/kdjQ1kM2CHFDPjKW+KzyRTF0g/ga9w9y+fANQpxvX8CU+Ny7FUWDeF3Y+g3lROIf4k0UDX9eCyvO531PyYhHga9zvPZJU5b73Y/eXj8Hv9D48n6HaF5LbcjRt8TZTtda5M1DfXnbkX1C0SHCFKzQB5Fe8op4GNGNHavvZESbVwT5r6W1xyuCPBY3Y9YgDqzknH/e3YfNzzuL30l0IebrZ5kKtuDIXt1n868ET6kf3/49tLvrCcZyF8Pu215dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcPIbNrBhOaBXucoAAAAASUVORK5CYII=">
-<link rel="import" href="tf-tensorboard.html">
+<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
+<link rel="import" href="tf-tensorboard/style.html">
+<link rel="import" href="tf-tensorboard/tf-tensorboard.html">
+<link rel="import" href="analytics.html">
<body>
<tf-tensorboard use-hash></tf-tensorboard>
diff --git a/tensorflow/tensorboard/components/tf_audio_dashboard/BUILD b/tensorflow/tensorboard/components/tf_audio_dashboard/BUILD
index 9172ebb22a..010643b856 100644
--- a/tensorflow/tensorboard/components/tf_audio_dashboard/BUILD
+++ b/tensorflow/tensorboard/components/tf_audio_dashboard/BUILD
@@ -44,9 +44,9 @@ tensorboard_webcomponent_library(
],
destdir = "tf-audio-dashboard",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
"//tensorflow/tensorboard/components/tf_backend:legacy",
"//tensorflow/tensorboard/components/tf_dashboard_common:legacy",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//third_party/javascript/polymer/v1/paper-icon-button:lib",
"//third_party/javascript/polymer/v1/paper-styles:lib",
"//third_party/javascript/polymer/v1/polymer:lib",
@@ -59,5 +59,4 @@ tensorboard_ts_library(
srcs = [],
deps_mgmt = "off",
runtime = "nodejs",
- deps = ["//tensorflow/tensorboard/components:common_deps"],
)
diff --git a/tensorflow/tensorboard/components/tf_backend/BUILD b/tensorflow/tensorboard/components/tf_backend/BUILD
index d9f3a035d8..14781f327c 100644
--- a/tensorflow/tensorboard/components/tf_backend/BUILD
+++ b/tensorflow/tensorboard/components/tf_backend/BUILD
@@ -58,7 +58,7 @@ tensorboard_webcomponent_library(
visibility = ["//visibility:public"],
destdir = "tf-backend",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//third_party/javascript/polymer/v1/polymer:lib",
],
)
@@ -75,7 +75,15 @@ tensorboard_ts_library(
deps_mgmt = "off",
runtime = "nodejs",
deps = [
- "//tensorflow/tensorboard/components:common_deps",
"//tensorflow/tensorboard/components/vz_sorting:legacy_ts",
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
],
)
diff --git a/tensorflow/tensorboard/components/tf_backend_d3v4/BUILD b/tensorflow/tensorboard/components/tf_backend_d3v4/BUILD
index 019283816f..179ca27cd2 100644
--- a/tensorflow/tensorboard/components/tf_backend_d3v4/BUILD
+++ b/tensorflow/tensorboard/components/tf_backend_d3v4/BUILD
@@ -13,6 +13,7 @@ web_library(
"tf-backend.html",
],
path = "/tf-backend",
+ visibility = ["//visibility:public"],
deps = [
"//tensorflow/tensorboard/components/tf_imports_d3v4:d3",
"//tensorflow/tensorboard/components/tf_imports_d3v4:lodash",
diff --git a/tensorflow/tensorboard/components/tf_color_scale/BUILD b/tensorflow/tensorboard/components/tf_color_scale/BUILD
index 3e64fccd2e..f6f1be099a 100644
--- a/tensorflow/tensorboard/components/tf_color_scale/BUILD
+++ b/tensorflow/tensorboard/components/tf_color_scale/BUILD
@@ -48,7 +48,7 @@ tensorboard_webcomponent_library(
],
destdir = "tf-color-scale",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//third_party/javascript/polymer/v1/polymer:lib",
],
)
@@ -61,5 +61,15 @@ tensorboard_ts_library(
],
deps_mgmt = "off",
runtime = "nodejs",
- deps = ["//tensorflow/tensorboard/components:common_deps"],
+ deps = [
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
+ ],
)
diff --git a/tensorflow/tensorboard/components/tf_dashboard_common/BUILD b/tensorflow/tensorboard/components/tf_dashboard_common/BUILD
index 321e4a821e..5a9e941e46 100644
--- a/tensorflow/tensorboard/components/tf_dashboard_common/BUILD
+++ b/tensorflow/tensorboard/components/tf_dashboard_common/BUILD
@@ -65,7 +65,7 @@ tensorboard_webcomponent_library(
srcs = glob(["*.html"]) + [":legacy_ts"],
destdir = "tf-dashboard-common",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//tensorflow/tensorboard/components/tf_storage:legacy",
"//tensorflow/tensorboard/components/vz_sorting:legacy",
"//third_party/javascript/polymer/v1/iron-ajax:lib",
@@ -97,7 +97,15 @@ tensorboard_ts_library(
deps_mgmt = "off",
runtime = "nodejs",
deps = [
- "//tensorflow/tensorboard/components:common_deps",
"//tensorflow/tensorboard/components/vz_sorting:legacy_ts",
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
],
)
diff --git a/tensorflow/tensorboard/components/tf_distribution_dashboard/BUILD b/tensorflow/tensorboard/components/tf_distribution_dashboard/BUILD
index 38c3eb6c11..65341e7647 100644
--- a/tensorflow/tensorboard/components/tf_distribution_dashboard/BUILD
+++ b/tensorflow/tensorboard/components/tf_distribution_dashboard/BUILD
@@ -42,9 +42,9 @@ tensorboard_webcomponent_library(
],
destdir = "tf-distribution-dashboard",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
"//tensorflow/tensorboard/components/tf_backend:legacy",
"//tensorflow/tensorboard/components/tf_dashboard_common:legacy",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//tensorflow/tensorboard/components/vz_distribution_chart:legacy",
"//third_party/javascript/polymer/v1/iron-collapse:lib",
"//third_party/javascript/polymer/v1/paper-icon-button:lib",
@@ -59,6 +59,4 @@ tensorboard_ts_library(
srcs = [],
deps_mgmt = "off",
runtime = "nodejs",
- # Don't list the common_deps here; a src-less ts_library re-exports them,
- # and they are outside the package where the Fileset will be rooted.
)
diff --git a/tensorflow/tensorboard/components/tf_graph_common/BUILD b/tensorflow/tensorboard/components/tf_graph_common/BUILD
index 50f7a309ed..7c0a4a44a2 100644
--- a/tensorflow/tensorboard/components/tf_graph_common/BUILD
+++ b/tensorflow/tensorboard/components/tf_graph_common/BUILD
@@ -51,7 +51,7 @@ tensorboard_webcomponent_library(
],
destdir = "tf-graph-common",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//third_party/javascript/polymer/v1/polymer:lib",
],
)
@@ -61,5 +61,15 @@ tensorboard_ts_library(
srcs = glob(["*.ts"]),
deps_mgmt = "off",
runtime = "nodejs",
- deps = ["//tensorflow/tensorboard/components:common_deps"],
+ deps = [
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
+ ],
)
diff --git a/tensorflow/tensorboard/components/tf_histogram_dashboard/BUILD b/tensorflow/tensorboard/components/tf_histogram_dashboard/BUILD
index 3f17bda34c..d8e2f672f8 100644
--- a/tensorflow/tensorboard/components/tf_histogram_dashboard/BUILD
+++ b/tensorflow/tensorboard/components/tf_histogram_dashboard/BUILD
@@ -42,9 +42,9 @@ tensorboard_webcomponent_library(
],
destdir = "tf-histogram-dashboard",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
"//tensorflow/tensorboard/components/tf_backend:legacy",
"//tensorflow/tensorboard/components/tf_dashboard_common:legacy",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//tensorflow/tensorboard/components/vz_histogram_timeseries:legacy",
"//third_party/javascript/polymer/v1/iron-collapse:lib",
"//third_party/javascript/polymer/v1/paper-icon-button:lib",
diff --git a/tensorflow/tensorboard/components/tf_imports/BUILD b/tensorflow/tensorboard/components/tf_imports/BUILD
index ac4c41d00f..81d1234d66 100644
--- a/tensorflow/tensorboard/components/tf_imports/BUILD
+++ b/tensorflow/tensorboard/components/tf_imports/BUILD
@@ -24,7 +24,7 @@ web_library(
name = "graphlib",
srcs = [
"graphlib.html",
- "@io_github_cpettitt_graphlib",
+ "@io_github_cpettitt_graphlib//:graphlib.core.js",
],
path = "/tf-imports",
deps = [":lodash"],
@@ -34,7 +34,7 @@ web_library(
name = "dagre",
srcs = [
"dagre.html",
- "@io_github_cpettitt_dagre",
+ "@io_github_cpettitt_dagre//:dagre.core.js",
],
path = "/tf-imports",
deps = [
diff --git a/tensorflow/tensorboard/components/tf_imports_d3v4/BUILD b/tensorflow/tensorboard/components/tf_imports_d3v4/BUILD
index edf759052f..abebe34368 100644
--- a/tensorflow/tensorboard/components/tf_imports_d3v4/BUILD
+++ b/tensorflow/tensorboard/components/tf_imports_d3v4/BUILD
@@ -26,7 +26,7 @@ web_library(
name = "numericjs",
srcs = [
"numericjs.html",
- "@com_numericjs",
+ "@com_numericjs//:numeric.js",
],
path = "/tf-imports",
)
@@ -35,7 +35,7 @@ web_library(
name = "weblas",
srcs = [
"weblas.html",
- "@io_github_waylonflinn_weblas",
+ "@io_github_waylonflinn_weblas//:weblas.js",
],
path = "/tf-imports",
)
@@ -44,7 +44,7 @@ web_library(
name = "graphlib",
srcs = [
"graphlib.html",
- "@io_github_cpettitt_graphlib",
+ "@io_github_cpettitt_graphlib//:graphlib.core.js",
],
path = "/tf-imports",
deps = [":lodash"],
@@ -54,7 +54,7 @@ web_library(
name = "dagre",
srcs = [
"dagre.html",
- "@io_github_cpettitt_dagre",
+ "@io_github_cpettitt_dagre//:dagre.core.js",
],
path = "/tf-imports",
deps = [
@@ -67,7 +67,7 @@ web_library(
name = "d3",
srcs = [
"d3.html",
- "@org_d3js_v4",
+ "@org_d3js_v4//:d3.js",
],
path = "/tf-imports",
)
diff --git a/tensorflow/tensorboard/components/tf_imports_google/README.md b/tensorflow/tensorboard/components/tf_imports_google/README.md
deleted file mode 100644
index 60d9cce777..0000000000
--- a/tensorflow/tensorboard/components/tf_imports_google/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This file acts as import routers for third party javascript libraries,
-e.g. Plottable and D3 from `g3/third_party`; it exists to facilitate development
-inside google.
diff --git a/tensorflow/tensorboard/components/tf_imports_google/d3.html b/tensorflow/tensorboard/components/tf_imports_google/d3.html
deleted file mode 100644
index dbfd11aa87..0000000000
--- a/tensorflow/tensorboard/components/tf_imports_google/d3.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!--
-@license
-Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
--->
-
-<script src="../d3-library/d3.js"></script>
diff --git a/tensorflow/tensorboard/components/tf_imports_google/dagre.html b/tensorflow/tensorboard/components/tf_imports_google/dagre.html
deleted file mode 100644
index 5b8b981741..0000000000
--- a/tensorflow/tensorboard/components/tf_imports_google/dagre.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!--
-@license
-Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
--->
-
-<link rel="import" href="../dagre-library/dagre.html">
diff --git a/tensorflow/tensorboard/components/tf_imports_google/plottable.html b/tensorflow/tensorboard/components/tf_imports_google/plottable.html
deleted file mode 100644
index 6f9678f9cb..0000000000
--- a/tensorflow/tensorboard/components/tf_imports_google/plottable.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!--
-@license
-Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
--->
-
-<link rel="import" href="d3.html">
-<link rel="import" href="../plottable-library/plottable.html">
diff --git a/tensorflow/tensorboard/components/tf_scalar_dashboard/BUILD b/tensorflow/tensorboard/components/tf_scalar_dashboard/BUILD
index c1fbf80c4b..46c91c484f 100644
--- a/tensorflow/tensorboard/components/tf_scalar_dashboard/BUILD
+++ b/tensorflow/tensorboard/components/tf_scalar_dashboard/BUILD
@@ -49,10 +49,10 @@ tensorboard_webcomponent_library(
],
destdir = "tf-scalar-dashboard",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
"//tensorflow/tensorboard/components/tf_backend:legacy",
"//tensorflow/tensorboard/components/tf_color_scale:legacy",
"//tensorflow/tensorboard/components/tf_dashboard_common:legacy",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//tensorflow/tensorboard/components/vz_line_chart:legacy",
"//third_party/javascript/polymer/v1/iron-collapse:lib",
"//third_party/javascript/polymer/v1/paper-checkbox:lib",
@@ -73,5 +73,4 @@ tensorboard_ts_library(
srcs = [],
deps_mgmt = "off",
runtime = "nodejs",
- deps = ["//tensorflow/tensorboard/components:common_deps"],
)
diff --git a/tensorflow/tensorboard/components/tf_storage/BUILD b/tensorflow/tensorboard/components/tf_storage/BUILD
index 8b2e006367..940d09681d 100644
--- a/tensorflow/tensorboard/components/tf_storage/BUILD
+++ b/tensorflow/tensorboard/components/tf_storage/BUILD
@@ -52,8 +52,8 @@ tensorboard_webcomponent_library(
visibility = ["//visibility:public"],
destdir = "tf-storage",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
"//tensorflow/tensorboard/components/tf_globals:legacy",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//third_party/javascript/polymer/v1/polymer:lib",
],
)
@@ -64,7 +64,15 @@ tensorboard_ts_library(
deps_mgmt = "off",
runtime = "nodejs",
deps = [
- "//tensorflow/tensorboard/components:common_deps",
"//tensorflow/tensorboard/components/tf_globals:legacy_ts",
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
],
)
diff --git a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/BUILD b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/BUILD
index 683302bb45..e17a083cb1 100644
--- a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/BUILD
+++ b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/BUILD
@@ -1,7 +1,6 @@
package(default_visibility = ["//tensorflow:internal"])
load("@io_bazel_rules_closure//closure:defs.bzl", "web_library")
-load("//tensorflow/tensorboard:vulcanize.bzl", "tensorboard_html_binary")
load("//tensorflow/tensorboard:defs.bzl", "tensorboard_typescript_genrule")
licenses(["notice"]) # Apache 2.0
@@ -9,32 +8,35 @@ licenses(["notice"]) # Apache 2.0
web_library(
name = "tf_tensorboard_d3v4",
srcs = [
+ "style.html",
"tf-tensorboard.html",
":ts",
],
path = "/tf-tensorboard",
+ visibility = ["//visibility:public"],
deps = [
- "@org_polymer_iron_icons",
- "@org_polymer_paper_button",
- "@org_polymer_paper_checkbox",
- "@org_polymer_paper_dialog",
- "@org_polymer_paper_header_panel",
- "@org_polymer_paper_icon_button",
- "@org_polymer_paper_tabs",
- "@org_polymer_paper_toolbar",
- "@org_polymer",
"//tensorflow/tensorboard/components/tf_audio_dashboard_d3v4",
"//tensorflow/tensorboard/components/tf_backend_d3v4",
"//tensorflow/tensorboard/components/tf_dashboard_common_d3v4",
"//tensorflow/tensorboard/components/tf_distribution_dashboard_d3v4",
"//tensorflow/tensorboard/components/tf_globals_d3v4",
- # "//tensorflow/tensorboard/components/tf_graph_dashboard_d3v4",
+ "//tensorflow/tensorboard/components/tf_graph_dashboard_d3v4",
"//tensorflow/tensorboard/components/tf_histogram_dashboard_d3v4",
"//tensorflow/tensorboard/components/tf_image_dashboard_d3v4",
"//tensorflow/tensorboard/components/tf_scalar_dashboard_d3v4",
"//tensorflow/tensorboard/components/tf_storage_d3v4",
"//tensorflow/tensorboard/components/tf_text_dashboard_d3v4",
"//tensorflow/tensorboard/components/vz_projector_d3v4",
+ "@org_polymer",
+ "@org_polymer_font_roboto",
+ "@org_polymer_iron_icons",
+ "@org_polymer_paper_button",
+ "@org_polymer_paper_checkbox",
+ "@org_polymer_paper_dialog",
+ "@org_polymer_paper_header_panel",
+ "@org_polymer_paper_icon_button",
+ "@org_polymer_paper_tabs",
+ "@org_polymer_paper_toolbar",
],
)
@@ -49,25 +51,6 @@ web_library(
],
)
-web_library(
- name = "dist",
- srcs = ["dist.html"],
- path = "/tf-tensorboard",
- deps = [
- ":tf_tensorboard_d3v4",
- "//tensorflow/tensorboard/demo:demo_data",
- "@org_polymer_webcomponentsjs",
- ],
-)
-
-tensorboard_html_binary(
- name = "index",
- # input_path = "/tf-dashboard-common/tf-chart-scaffold.html",
- input_path = "/tf-tensorboard/dist.html",
- output_path = "/index.html",
- deps = [":dist"],
-)
-
tensorboard_typescript_genrule(
name = "ts",
srcs = ["autoReloadBehavior.ts"],
diff --git a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/demo.html b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/demo.html
index 95f5b718a5..c8a9238aef 100644
--- a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/demo.html
+++ b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/demo.html
@@ -16,17 +16,10 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<title>TensorBoard</title>
-<style>
- html, body {
- margin: 0;
- padding: 0;
- height: 100%;
- font-family: "RobotoDraft", "Roboto", sans-serif;
- }
-</style>
+<meta charset="utf-8">
+<title>TensorBoard Demo</title>
<script src="../webcomponentsjs/webcomponents-lite.min.js"></script>
-<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAMAAAD3eH5ZAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD/UExURfFlKfaELvFmKfNyK/67NvWALf68Nv69NvNxK/20NfyyNP22NfN0K/JrKvqhMv2zNf25Nf24Nf23NfeOL/yzNPyvNPJoKviWMPmeMfN1K/WBLfePL/FnKfeML/qlMvR7LPmcMfeLL/aJLvR5LPFoKfJuKvR3LP66NvywNPeNL/V/LfaILv21Nf26NfNzK/NvK/R6LPmaMfyxNPqfMvV+LfurM/iSMPmbMfJvKvmdMfumM/qiMvmZMfytNPJqKvysNPN2K/iYMPNwK/upM/JtKvJsKviVMPaHLvaGLvJpKvR8LPaKLvqkMvuqM/aFLvR4LPuoM/iTMPWDLfiRMPmYMXS0ngkAAALoSURBVHja7drnctpAFIbhFUISSKJ3MKYa0+y4xTW9937/15JkJhlTjhrSrHRmvuf/as6L0YLFCgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBJ6njenqspzgnPrsrGX9Zpi2tCrmnc6+dYNthVY5WpMmxQLWPdMsOuYVwzNj3ei2t3mQwaV43BJPDCS2NbJ5aEeuX/+9qcjQOtfFIkIkrvY2g4MVcmOBsFWbowKO/kNyj62gRpJcDaPBlxLr1B0zdG0C/8LzbJiJrshuvy1gzlA9+rD8mIkuyIJjFE3/dqnYwoSm7IUEPoD/wut8iIguSIDjlFxe/yfXL5vuSI21BTZLLhXoOILMO8Hxwa/L8bI0LfmUdhGowb2ZvT0e57pFNDgB06IlVyjmmIBl2T/nl9Rw6SD9GgSG/Q0uQkaW3XhmovKQ3eFQ4N2Uo9OQ1eFZsNerf7vP+rO4rhmY1Lg3vFVoP8+8BXg1sFnwbnCk4NThW8GuiKBDdkVVtTNFvNelVsNqTbyWnIOM2oeTRoyWvwmpJHg/ucXBrcJuXT4DwrpwZi2vy0VCx8YtXg/D2bU4OfiuQ3eFfE2KD4bfCqiLNB993gXsGlwa2CT4NzBacGIVQ6YsipQdh0xEdODUKjIxrSp88onZ8zbbFLg1DoiFO5BXvDGv2My9/JhUT8JUZTI0yDaNHLBzIbvqTDNYhUiVw/kdjQ1kM2CHFDPjKW+KzyRTF0g/ga9w9y+fANQpxvX8CU+Ny7FUWDeF3Y+g3lROIf4k0UDX9eCyvO531PyYhHga9zvPZJU5b73Y/eXj8Hv9D48n6HaF5LbcjRt8TZTtda5M1DfXnbkX1C0SHCFKzQB5Fe8op4GNGNHavvZESbVwT5r6W1xyuCPBY3Y9YgDqzknH/e3YfNzzuL30l0IebrZ5kKtuDIXt1n868ET6kf3/49tLvrCcZyF8Pu215dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcPIbNrBhOaBXucoAAAAASUVORK5CYII=">
+<link rel="import" href="style.html">
<link rel="import" href="tf-tensorboard.html">
-
+<body>
<tf-tensorboard demo-dir="/data" use-hash></tf-tensorboard>
diff --git a/tensorflow/tensorboard/components/tf_imports_google/lodash.html b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/style.html
index eb8fef2883..575e89e398 100644
--- a/tensorflow/tensorboard/components/tf_imports_google/lodash.html
+++ b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/style.html
@@ -15,4 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<link rel="import" href="../lodash-library/lodash-library.html">
+<link rel="import" href="../font-roboto/roboto.html">
+
+<style>
+ html,
+ body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ font-family: "RobotoDraft", "Roboto", sans-serif;
+ }
+</style>
diff --git a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/tf-tensorboard.html b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/tf-tensorboard.html
index f3f5d590f9..3a56535410 100644
--- a/tensorflow/tensorboard/components/tf_tensorboard_d3v4/tf-tensorboard.html
+++ b/tensorflow/tensorboard/components/tf_tensorboard_d3v4/tf-tensorboard.html
@@ -30,12 +30,13 @@ limitations under the License.
<link rel="import" href="../tf-histogram-dashboard/tf-histogram-dashboard.html">
<link rel="import" href="../tf-image-dashboard/tf-image-dashboard.html">
<link rel="import" href="../tf-audio-dashboard/tf-audio-dashboard.html">
-<!-- <link rel="import" href="../tf-graph-dashboard/tf-graph-dashboard.html"> -->
+<link rel="import" href="../tf-graph-dashboard/tf-graph-dashboard.html">
<link rel="import" href="../tf-text-dashboard/tf-text-dashboard.html">
<link rel="import" href="../tf-dashboard-common/tensorboard-color.html">
<link rel="import" href="../tf-backend/tf-backend.html">
<link rel="import" href="../tf-storage/tf-storage.html">
<link rel="import" href="../vz-projector/vz-projector-dashboard.html">
+<link rel="import" href="../vz-projector/bundle.html">
<!--
tf-tensorboard is the frontend entry point for TensorBoard.
diff --git a/tensorflow/tensorboard/components/vz_distribution_chart/BUILD b/tensorflow/tensorboard/components/vz_distribution_chart/BUILD
index 3346805fb0..3b46082bec 100644
--- a/tensorflow/tensorboard/components/vz_distribution_chart/BUILD
+++ b/tensorflow/tensorboard/components/vz_distribution_chart/BUILD
@@ -51,7 +51,7 @@ tensorboard_webcomponent_library(
visibility = ["//visibility:public"],
destdir = "vz-distribution-chart",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//tensorflow/tensorboard/components/vz_sorting:legacy",
"//third_party/javascript/polymer/v1/polymer:lib",
],
@@ -63,7 +63,15 @@ tensorboard_ts_library(
deps_mgmt = "off",
runtime = "nodejs",
deps = [
- "//tensorflow/tensorboard/components:common_deps",
"//tensorflow/tensorboard/components/vz_line_chart:legacy_ts",
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
],
)
diff --git a/tensorflow/tensorboard/components/vz_heatmap/BUILD b/tensorflow/tensorboard/components/vz_heatmap/BUILD
new file mode 100644
index 0000000000..eccc411f1b
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz_heatmap/BUILD
@@ -0,0 +1,29 @@
+package(default_visibility = ["//tensorflow:internal"])
+
+load("//tensorflow/tensorboard:defs.bzl", "tensorboard_webcomponent_library")
+
+licenses(["notice"]) # Apache 2.0
+
+tensorboard_webcomponent_library(
+ name = "legacy",
+ srcs = [
+ "demo/index.html",
+ "index.html",
+ "vz-heatmap.html",
+ ],
+ visibility = ["//visibility:public"],
+ destdir = "vz-heatmap",
+ deps = [
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
+ "//tensorflow/tensorboard/components/vz_sorting:legacy",
+ "//third_party/javascript/polymer/v1/iron-component-page:lib",
+ "//third_party/javascript/polymer/v1/polymer:lib",
+ "//third_party/javascript/polymer/v1/webcomponentsjs:lib",
+ ],
+)
+
+filegroup(
+ name = "all_files",
+ srcs = glob(["**"]),
+ tags = ["notsan"],
+)
diff --git a/tensorflow/tensorboard/components/vz_heatmap/demo/index.html b/tensorflow/tensorboard/components/vz_heatmap/demo/index.html
index 63dd04485f..0ef092fccc 100644
--- a/tensorflow/tensorboard/components/vz_heatmap/demo/index.html
+++ b/tensorflow/tensorboard/components/vz_heatmap/demo/index.html
@@ -22,6 +22,7 @@ limitations under the License.
<title>Heatmap example</title>
<link rel="import" href="../vz-heatmap.html">
<link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../tf-imports/d3.html">
</head>
<script>
function generateRandomMatrix() {
@@ -108,7 +109,7 @@ limitations under the License.
setTimeout(function () {
var heatmapColor = document.getElementById('color_function');
heatmapColor.colorFunction =
- d3.scale.linear().range(['white', 'black']).domain([0, 5]);
+ d3.scaleLinear().range(['white', 'black']).domain([0, 5]);
}, 1500);
</script>
</div>
diff --git a/tensorflow/tensorboard/components/vz_heatmap/vz-heatmap.html b/tensorflow/tensorboard/components/vz_heatmap/vz-heatmap.html
index 7acba47d5f..4a00542276 100644
--- a/tensorflow/tensorboard/components/vz_heatmap/vz-heatmap.html
+++ b/tensorflow/tensorboard/components/vz_heatmap/vz-heatmap.html
@@ -89,7 +89,7 @@ fits itself to the width of the container it is placed in.
_generatedColorScale: {
type: Object,
value: (function () {
- var retFunction = d3.scale.linear();
+ var retFunction = d3.scaleLinear();
if (this.colors) {
retFunction.range(this.colors)
}
@@ -277,7 +277,7 @@ fits itself to the width of the container it is placed in.
*/
_updateColorFunction: function () {
if (Array.isArray(this.colors) && this.colors.length) {
- this._generatedColorScale = d3.scale.linear().range(this.colors);
+ this._generatedColorScale = d3.scaleLinear().range(this.colors);
}
if (this.values) {
@@ -294,7 +294,7 @@ fits itself to the width of the container it is placed in.
}
},
_getLinearInterpolation: function (domain, range) {
- return d3.scale.linear().domain(domain).range(range);
+ return d3.scaleLinear().domain(domain).range(range);
},
/**
* Find side length of each tile in heat map by dividing current width
diff --git a/tensorflow/tensorboard/components/vz_heatmap_d3v4/BUILD b/tensorflow/tensorboard/components/vz_heatmap_d3v4/BUILD
new file mode 100644
index 0000000000..b62629fc55
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz_heatmap_d3v4/BUILD
@@ -0,0 +1,41 @@
+package(default_visibility = ["//tensorflow:internal"])
+
+load("@io_bazel_rules_closure//closure:defs.bzl", "web_library")
+
+licenses(["notice"]) # Apache 2.0
+
+web_library(
+ name = "vz_heatmap_d3v4",
+ srcs = ["vz-heatmap.html"],
+ path = "/vz-heatmap",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//tensorflow/tensorboard/components/tf_imports_d3v4:d3",
+ "@org_polymer",
+ "@org_polymer_iron_resizable_behavior",
+ ],
+)
+
+web_library(
+ name = "index",
+ srcs = [
+ "demo/index.html",
+ "index.html",
+ ],
+ path = "/vz-heatmap",
+ visibility = ["//visibility:public"],
+ deps = [
+ ":vz_heatmap_d3v4",
+ "//tensorflow/tensorboard/components/tf_imports_d3v4:d3",
+ "@org_polymer",
+ "@org_polymer_iron_component_page",
+ "@org_polymer_iron_demo_helpers",
+ "@org_polymer_webcomponentsjs",
+ ],
+)
+
+filegroup(
+ name = "all_files",
+ srcs = glob(["**"]),
+ tags = ["notsan"],
+)
diff --git a/tensorflow/tensorboard/components/vz_heatmap_d3v4/demo/index.html b/tensorflow/tensorboard/components/vz_heatmap_d3v4/demo/index.html
new file mode 100644
index 0000000000..0ef092fccc
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz_heatmap_d3v4/demo/index.html
@@ -0,0 +1,161 @@
+<!doctype html>
+<!--
+@license
+Copyright 2016 The TensorFlow Authors. 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.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+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.
+-->
+
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Heatmap example</title>
+ <link rel="import" href="../vz-heatmap.html">
+ <link rel="import" href="../../iron-demo-helpers/demo-snippet.html">
+ <link rel="import" href="../../tf-imports/d3.html">
+</head>
+<script>
+ function generateRandomMatrix() {
+ var rows = getRandomArbitrary(10, 20);
+ var columns = getRandomArbitrary(10, 20);
+ var data = [];
+ // Generate new data array.
+ for (var row = 0; row < rows; row++) {
+ var currentRow = [];
+ for (var col = 0; col < columns; col++) {
+ currentRow[col] = getRandomArbitrary(0, 20);
+ }
+ data[row] = currentRow;
+ }
+ return data;
+ }
+
+ // Returns a random number between min (inclusive) and max (exclusive)
+ function getRandomArbitrary(min, max) {
+ return Math.random() * (max - min) + min;
+ }
+
+ function getRandomColorRange() {
+ return [getRandomColor(), getRandomColor()];
+ }
+
+ function getRandomColor() {
+ var letters = '0123456789ABCDEF';
+ var color = '#';
+ for (var i = 0; i < 6; i++) {
+ color += letters[Math.floor(Math.random() * 16)];
+ }
+ return color;
+ }
+</script>
+<body>
+<h1>Initialized with data</h1>
+<div style="width: 30%">
+ <demo-snippet>
+ <template>
+ <vz-heatmap data="[[1,2],[3,4]]"></vz-heatmap>
+ </template>
+ </demo-snippet>
+</div>
+
+<h1>Initialized with data and custom data range</h1>
+<div style="width: 30%">
+ <demo-snippet>
+ <template>
+ <vz-heatmap data="[[1,2],[3,4]]" values="[0,10]"></vz-heatmap>
+ </template>
+ </demo-snippet>
+</div>
+
+<h1>Initialized with data and colors</h1>
+<div style="width: 30%">
+ <demo-snippet>
+ <template>
+ <vz-heatmap data="[[1,2],[3,4]]" colors='["yellow", "red"]'></vz-heatmap>
+ </template>
+ </demo-snippet>
+</div>
+
+<h1>Initialized with data and colors and threshold values</h1>
+<div style="width: 30%">
+ <demo-snippet>
+ <template>
+ <vz-heatmap data="[[1,2],[3,4]]"
+ values="[0, 10]"
+ colors='["yellow", "red"]'
+ ></vz-heatmap>
+ </template>
+ </demo-snippet>
+</div>
+<h1>Initialized with data and color function</h1>
+<div style="width: 30%">
+ <demo-snippet>
+ <template>
+ <vz-heatmap id="color_function" data="[[1,2],[3,4]]"
+ ></vz-heatmap>
+ </template>
+ </demo-snippet>
+ <script>
+ setTimeout(function () {
+ var heatmapColor = document.getElementById('color_function');
+ heatmapColor.colorFunction =
+ d3.scaleLinear().range(['white', 'black']).domain([0, 5]);
+ }, 1500);
+ </script>
+</div>
+
+<h1>Initialized with data and updated data</h1>
+<h3>Click on the heatmap to create new random data.</h3>
+<div style="width: 30%">
+ <demo-snippet>
+ <template>
+ <vz-heatmap data="[[1,2],[3,4]]"
+ onclick="this.data = generateRandomMatrix()"
+ >
+ </vz-heatmap>
+ </template>
+ </demo-snippet>
+</div>
+
+<h1>Initialized with data and updated color update</h1>
+<h3>Click on the heatmap to update the color scheme.</h3>
+<div style="width: 30%">
+ <demo-snippet>
+ <template>
+ <vz-heatmap id="data_color_update" data="[[1,2],[3,4]]"
+ onclick="this.colors = getRandomColorRange();
+ this.data = generateRandomMatrix()"
+ >
+ </vz-heatmap>
+ </template>
+ </demo-snippet>
+</div>
+
+<h2>Let's try to actually break it... *Puts on fedora*</h2>
+<p>Code below is not meant to be seen, but to ensure that no errors are
+ thrown when invalid data is passed into the Polymer element.</p>
+<demo-snippet>
+ <template>
+ <vz-heatmap id="break_the_heatmap" data="undefined"></vz-heatmap>
+ </template>
+</demo-snippet>
+<script>
+ var bth = document.getElementById('break_the_heatmap');
+ bth.data = []; // Empty 1D array.
+ bth.data = [[]]; // Empty 2D array.
+ bth.colors = ['yellow', 'blue', '']; // More than 2 elements in colors array.
+ bth.values = [1, 2, 3]; // More than 2 elements in values array.
+</script>
+
+</body>
+</html>
diff --git a/tensorflow/tensorboard/dist/bazel-html-imports.html b/tensorflow/tensorboard/components/vz_heatmap_d3v4/index.html
index 2268e6d7d4..656306499e 100644
--- a/tensorflow/tensorboard/dist/bazel-html-imports.html
+++ b/tensorflow/tensorboard/components/vz_heatmap_d3v4/index.html
@@ -16,8 +16,16 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- TENSORBOARD_BOWER_AUTOGENERATED_BELOW_THIS_LINE_DO_NOT_EDIT -->
-<script src="../numericjs_numeric_min_js/file/numeric.min.js"></script>
-<script src="../three_js_three_min_js/file/three.min.js"></script>
-<script src="../three_js_orbitcontrols_js/file/OrbitControls.js"></script>
-<script src="../weblas_weblas_js/file/weblas.js"></script>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <script src="../webcomponentsjs/webcomponents-lite.js"></script>
+ <link rel="import" href="../iron-component-page/iron-component-page.html">
+ </head>
+ <body>
+
+ <iron-component-page src="vz-heatmap.html"></iron-component-page>
+
+ </body>
+</html>
diff --git a/tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html b/tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html
new file mode 100644
index 0000000000..8a3503468f
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html
@@ -0,0 +1,360 @@
+<!--
+@license
+Copyright 2016 The TensorFlow Authors. 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.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+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.
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="../tf-imports/d3.html">
+
+<!--
+`vz-heatmap` A simple heatmap to visualize 2D data using predefined or user
+defined colour scheme, with a dependency on d3.js. The heatmap automatically
+fits itself to the width of the container it is placed in.
+
+@element vz-heatmap
+@demo demo/index.html
+-->
+<dom-module id="vz-heatmap">
+ <template>
+ <div>
+ <canvas id="heatmap"
+ style="width: 100%; image-rendering:pixelated"></canvas>
+ </div>
+ </template>
+ <script>
+ Polymer({
+ is: 'vz-heatmap',
+ properties: {
+ /**
+ * An 2-element array which defines the lower and upper bound of the
+ * automatically created color scale for the heatmap.
+ */
+ colors: {
+ type: Array,
+ value: ['white', 'steelblue'],
+ observer: '_onColorsChange'
+ },
+ /**
+ * A function which returns a hex-formatted color string
+ * given a number type.
+ * i.e. (input: number) => string
+ *
+ * Passing a colorFunction deactivates the color
+ * function generated by the `colors` and `values` fields.
+ */
+ colorFunction: {
+ type: Object,
+ observer: '_onColorFunctionChange'
+ },
+ /**
+ * A 2D data array containing the data to be visualized.
+ */
+ data: {
+ type: Array,
+ value: [],
+ observer: '_onDataChange'
+ },
+ /**
+ * A 2-element array containing the domain of values which
+ * maps to the color range defined by the `colors` array.
+ */
+ values: {
+ type: Array,
+ observer: '_onValuesChange'
+ },
+ _currentDataValid: {
+ type: Boolean,
+ value: false
+ },
+ _dataExtent: {
+ type: Array,
+ value: [0, 1]
+ },
+ _debug: {
+ type: Boolean,
+ value: false
+ },
+ _generatedColorScale: {
+ type: Object,
+ value: (function () {
+ var retFunction = d3.scaleLinear();
+ if (this.colors) {
+ retFunction.range(this.colors)
+ }
+
+ return retFunction;
+ })()
+ },
+ _isReady: {
+ type: Boolean,
+ value: false
+ },
+ _height: {
+ type: Number,
+ value: 0
+ },
+ _width: {
+ type: Number,
+ value: 0
+ },
+ _useUserColorPickerFunction: {
+ type: Boolean,
+ value: false
+ }
+ },
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ],
+ listeners: {
+ 'iron-resize': '_onWidthChange'
+ },
+ _onWidthChange: function () {
+ // Re-render the chart if the data has already been set.
+ if (this._currentDataValid) {
+ this._renderData();
+ }
+ },
+ ready: function () {
+ this._isReady = true;
+ this._onWidthChange();
+ },
+ attached: function () {
+ this._onWidthChange();
+ },
+ /**
+ * Data change handler, if new data is valid, sets flag to indicate data
+ * now valid, updates the data extent and renders the new data.
+ */
+ _onDataChange: function () {
+ // Validate new data.
+ if (!this._isDataValid(this.data)) {
+ return;
+ }
+
+ // Set flag to indicate data is now valid.
+ this._currentDataValid = true;
+
+ // Calculate new data extent.
+ this._updateDataExtent();
+
+ if (this._isReady) {
+ this._renderData();
+ }
+ },
+ /**
+ * (Re-)Renders the heatmap. Called when data, color mapping, or width
+ * changes.
+ * @private
+ */
+ _renderData: function () {
+ // Ensure dimensions are up-to-date and valid before starting rendering.
+ this._updateDimensions();
+ if (!this._width || !this._height) return;
+
+ // Ensure the color function is up-to-date.
+ this._updateColorFunction();
+ var width = this._width;
+ var rows = this.data.length;
+ var columns = this.data[0].length;
+
+ // Calculate side length of each tile.
+ var sideLength = width / columns;
+
+ // Set domain and range of the axis.
+ var colorScale = (this._useUserColorPickerFunction) ?
+ this.colorFunction : this._generatedColorScale;
+
+ // Clear the canvas.
+ this.resetCanvas();
+
+ // Render heatmap.
+ var ctx = this.$.heatmap.getContext("2d");
+
+ for (var row = 0; row < rows; row++) {
+ for (var column = 0; column < columns; column++) {
+ var value = this.data[row][column];
+ // Set location and dimensions.
+ ctx.fillStyle = colorScale(value);
+ // Preferred way to set color of individual pixels.
+ ctx.fillRect(column, row, 1, 1);
+ }
+ }
+ },
+ /**
+ * Observer for this.colors.
+ * @private
+ */
+ _onColorsChange: function () {
+ if (Array.isArray(this.colors) && this.colors.length == 2) {
+ this._updateColorFunction();
+ }
+ this._useUserColorPickerFunction = false;
+ this._renderData();
+ },
+ /**
+ * Observer for this.values. Updates the
+ * internal color function. If data invalid, internal color scale uses
+ * the extent of the data as domain.
+ * @private
+ */
+ _onValuesChange: function () {
+ // Verify that validity of the new content of values.
+ // If data not valid, set this.values to undefined, as
+ // _updateColorFunction will then use the data's extent as the color
+ // scale's domain.
+ if (!(Array.isArray(this.values) && this.values.length == 2)) {
+ this.values = undefined;
+ }
+
+ this._updateColorFunction();
+ this._useUserColorPickerFunction = false;
+ this._renderData();
+ },
+ /**
+ * Observer for this.colorFunction. This field allows the user to
+ * provide a custom color scale. Updates to this value are ignored, if the new
+ * value is not a function object.
+ * @private
+ */
+ _onColorFunctionChange: function () {
+ if (typeof this.colorFunction === 'function') {
+ this._useUserColorPickerFunction = true;
+ this._renderData();
+ } else if (this._debug) {
+ console.log('The colorFunction provided is not of function type, and was not set as default.')
+ }
+ },
+ /**
+ * Calculates the extent of the data if it is required by the
+ * current color function. This function is called when
+ * properties change which would result in the color scale
+ * changing, or when the computed color scale is started to be used.
+ * @private
+ */
+ _updateDataExtent: function () {
+ if (this._currentDataValid) {
+ var rows = this.data.length;
+ var columns = this.data[0].length;
+
+ var min = this.data[0][0];
+ var max = this.data[0][0];
+
+ for (var row = 0; row < rows; row++) {
+ for (var col = 0; col < columns; col++) {
+ var currentElement = this.data[row][col];
+
+ if (currentElement < min) {
+ min = currentElement;
+ } else if (currentElement > max) {
+ max = currentElement;
+ }
+ }
+ }
+
+ this._dataExtent = [min, max];
+ } else if (!this._dataExtent) {
+ this._dataExtent = [0, 1];
+ }
+ },
+ /**
+ * Updates the internal color function only when the number of
+ * elements in this.colors is equal to the number of elements in
+ * this.values or if this.values is empty, in which case the
+ * extent of the data is used.
+ * @private
+ */
+ _updateColorFunction: function () {
+ if (Array.isArray(this.colors) && this.colors.length) {
+ this._generatedColorScale = d3.scaleLinear().range(this.colors);
+ }
+
+ if (this.values) {
+ // Check that the number of elements in this.values and
+ // this.colors have an identical number of elements.
+ if (this.colors.length === this.values.length) {
+ this._generatedColorScale.domain(this.values);
+ }
+
+ // If values reset, and data field contains valid data, set colour scale
+ // to use the data extent as domain.
+ } else if (this._currentDataValid) {
+ this._generatedColorScale.domain(this._dataExtent);
+ }
+ },
+ _getLinearInterpolation: function (domain, range) {
+ return d3.scaleLinear().domain(domain).range(range);
+ },
+ /**
+ * Find side length of each tile in heat map by dividing current width
+ * by number of columns
+ */
+ _findTileSideLength: function () {
+ if (this._currentDataValid) {
+ var numOfColumns = this.data[0].length;
+
+ // Make sure value is finite.
+ if (numOfColumns === 0) {
+ var returnValue = 0;
+ } else {
+ returnValue = this._width / numOfColumns;
+ }
+ return returnValue;
+
+ } else {
+ if (this._debug == true) {
+ console.log("WARNING: vz-heatmap is reverting to zero height as passed data is invalid.")
+ }
+ return 0; // Fall back to 0 height, if data is invalid.
+ }
+ },
+ /**
+ * Verifies whether input data is valid.
+ * @param newData Number[][]
+ * @private
+ */
+ _isDataValid: function (newData) {
+ return Array.isArray(newData) && newData.length &&
+ Array.isArray(newData[0]);
+ },
+ _updateDimensions: function () {
+ var canvasElement = this.$.heatmap;
+
+ var rows = 0;
+ var columns = 0;
+ if (this._currentDataValid) {
+ rows = this.data.length;
+ columns = this.data[0].length;
+ }
+
+ // Recalculate height and width, and ensure data can be rendered.
+ this._width = canvasElement.parentNode.clientWidth;
+ this._height = this._findTileSideLength() * rows;
+ // Update the attribute height and width.
+ canvasElement.setAttribute('height', rows);
+ canvasElement.setAttribute('width', columns);
+ },
+ /**
+ * Function which clears the canvas.
+ */
+ resetCanvas: function () {
+ // Reset the canvas.
+ var canvas = this.$.heatmap;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ }
+ });
+ </script>
+</dom-module>
diff --git a/tensorflow/tensorboard/components/vz_histogram_timeseries/BUILD b/tensorflow/tensorboard/components/vz_histogram_timeseries/BUILD
index d36cf2b331..3ca3936f32 100644
--- a/tensorflow/tensorboard/components/vz_histogram_timeseries/BUILD
+++ b/tensorflow/tensorboard/components/vz_histogram_timeseries/BUILD
@@ -37,7 +37,7 @@ tensorboard_webcomponent_library(
visibility = ["//visibility:public"],
destdir = "vz-histogram-timeseries",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//third_party/javascript/polymer/v1/polymer:lib",
],
)
diff --git a/tensorflow/tensorboard/components/vz_line_chart/BUILD b/tensorflow/tensorboard/components/vz_line_chart/BUILD
index 130d58dec7..6b988e5279 100644
--- a/tensorflow/tensorboard/components/vz_line_chart/BUILD
+++ b/tensorflow/tensorboard/components/vz_line_chart/BUILD
@@ -54,7 +54,7 @@ tensorboard_webcomponent_library(
visibility = ["//visibility:public"],
destdir = "vz-line-chart",
deps = [
- "//tensorflow/tensorboard/components:tf_imports",
+ "//tensorflow/tensorboard/components/tf_imports_google:lib",
"//tensorflow/tensorboard/components/vz_sorting:legacy",
"//third_party/javascript/polymer/v1/polymer:lib",
],
@@ -69,5 +69,15 @@ tensorboard_ts_library(
],
deps_mgmt = "off",
runtime = "nodejs",
- deps = ["//tensorflow/tensorboard/components:common_deps"],
+ deps = [
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
+ ],
)
diff --git a/tensorflow/tensorboard/components/vz_sorting/BUILD b/tensorflow/tensorboard/components/vz_sorting/BUILD
index ab5d7c3989..795fc5cee4 100644
--- a/tensorflow/tensorboard/components/vz_sorting/BUILD
+++ b/tensorflow/tensorboard/components/vz_sorting/BUILD
@@ -46,5 +46,4 @@ tensorboard_ts_library(
srcs = ["sorting.ts"],
deps_mgmt = "off",
runtime = "nodejs",
- deps = ["//tensorflow/tensorboard/components:common_deps"],
)
diff --git a/tensorflow/tensorboard/components/vz_sorting/test/BUILD b/tensorflow/tensorboard/components/vz_sorting/test/BUILD
index d1cf5a596a..649bfc5c6e 100644
--- a/tensorflow/tensorboard/components/vz_sorting/test/BUILD
+++ b/tensorflow/tensorboard/components/vz_sorting/test/BUILD
@@ -34,7 +34,15 @@ tensorboard_ts_library(
deps_mgmt = "off",
runtime = "nodejs",
deps = [
- "//tensorflow/tensorboard/components:common_deps",
"//tensorflow/tensorboard/components/vz_sorting:legacy_ts",
+ "//third_party/javascript/node_modules/typescript:es2015.promise",
+ "//third_party/javascript/plottable/v1:typings",
+ "//third_party/javascript/typings/chai",
+ "//third_party/javascript/typings/d3",
+ "//third_party/javascript/typings/lodash",
+ "//third_party/javascript/typings/mocha",
+ "//third_party/javascript/typings/polymer:polymer_without_externs",
+ "//third_party/javascript/typings/sinon",
+ "//third_party/javascript/typings/webcomponents_js",
],
)
diff --git a/tensorflow/tensorboard/dist/index.html b/tensorflow/tensorboard/dist/index.html
deleted file mode 100644
index 5f09faf31d..0000000000
--- a/tensorflow/tensorboard/dist/index.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!doctype html>
-<!--
-@license
-Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
--->
-
-<html>
- <head>
- <title>TensorBoard</title>
- <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
- <link rel="stylesheet" type="text/css" href="lib/css/global.css">
- <link rel="stylesheet" type="text/css" href="plottable/plottable.css">
- <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAADECAMAAAD3eH5ZAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD/UExURfFlKfaELvFmKfNyK/67NvWALf68Nv69NvNxK/20NfyyNP22NfN0K/JrKvqhMv2zNf25Nf24Nf23NfeOL/yzNPyvNPJoKviWMPmeMfN1K/WBLfePL/FnKfeML/qlMvR7LPmcMfeLL/aJLvR5LPFoKfJuKvR3LP66NvywNPeNL/V/LfaILv21Nf26NfNzK/NvK/R6LPmaMfyxNPqfMvV+LfurM/iSMPmbMfJvKvmdMfumM/qiMvmZMfytNPJqKvysNPN2K/iYMPNwK/upM/JtKvJsKviVMPaHLvaGLvJpKvR8LPaKLvqkMvuqM/aFLvR4LPuoM/iTMPWDLfiRMPmYMXS0ngkAAALoSURBVHja7drnctpAFIbhFUISSKJ3MKYa0+y4xTW9937/15JkJhlTjhrSrHRmvuf/as6L0YLFCgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBJ6njenqspzgnPrsrGX9Zpi2tCrmnc6+dYNthVY5WpMmxQLWPdMsOuYVwzNj3ei2t3mQwaV43BJPDCS2NbJ5aEeuX/+9qcjQOtfFIkIkrvY2g4MVcmOBsFWbowKO/kNyj62gRpJcDaPBlxLr1B0zdG0C/8LzbJiJrshuvy1gzlA9+rD8mIkuyIJjFE3/dqnYwoSm7IUEPoD/wut8iIguSIDjlFxe/yfXL5vuSI21BTZLLhXoOILMO8Hxwa/L8bI0LfmUdhGowb2ZvT0e57pFNDgB06IlVyjmmIBl2T/nl9Rw6SD9GgSG/Q0uQkaW3XhmovKQ3eFQ4N2Uo9OQ1eFZsNerf7vP+rO4rhmY1Lg3vFVoP8+8BXg1sFnwbnCk4NThW8GuiKBDdkVVtTNFvNelVsNqTbyWnIOM2oeTRoyWvwmpJHg/ucXBrcJuXT4DwrpwZi2vy0VCx8YtXg/D2bU4OfiuQ3eFfE2KD4bfCqiLNB993gXsGlwa2CT4NzBacGIVQ6YsipQdh0xEdODUKjIxrSp88onZ8zbbFLg1DoiFO5BXvDGv2My9/JhUT8JUZTI0yDaNHLBzIbvqTDNYhUiVw/kdjQ1kM2CHFDPjKW+KzyRTF0g/ga9w9y+fANQpxvX8CU+Ny7FUWDeF3Y+g3lROIf4k0UDX9eCyvO531PyYhHga9zvPZJU5b73Y/eXj8Hv9D48n6HaF5LbcjRt8TZTtda5M1DfXnbkX1C0SHCFKzQB5Fe8op4GNGNHavvZESbVwT5r6W1xyuCPBY3Y9YgDqzknH/e3YfNzzuL30l0IebrZ5kKtuDIXt1n868ET6kf3/49tLvrCcZyF8Pu215dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcPIbNrBhOaBXucoAAAAASUVORK5CYII=">
- <link rel="import" href="dist/bazel-html-imports.html">
- <link rel="import" href="dist/tf-tensorboard.html">
- </head>
- <body>
- <tf-tensorboard use-hash></tf-tensorboard>
- </body>
-</html>
diff --git a/tensorflow/tensorboard/dist/tf-tensorboard.html b/tensorflow/tensorboard/dist/tf-tensorboard.html
deleted file mode 100644
index 9f13cdfb47..0000000000
--- a/tensorflow/tensorboard/dist/tf-tensorboard.html
+++ /dev/null
@@ -1,27143 +0,0 @@
-<!-- Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-============================================================================
-
-This file is generated by `gulp` & `vulcanize`. Do not directly change it.
-Instead, use `gulp regenerate` to create a new version with your changes.
--->
-
-<html><head><!--
-@license
-Copyright 2017 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
---><!--
-@license
-Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
---><meta charset="UTF-8"><link rel="import" href="../polymer/polymer.html">
-<link rel="import" href="../iron-icons/iron-icons.html">
-<link rel="import" href="../paper-tabs/paper-tabs.html">
-<link rel="import" href="../paper-dialog/paper-dialog.html">
-<link rel="import" href="../paper-checkbox/paper-checkbox.html">
-<link rel="import" href="../paper-toolbar/paper-toolbar.html">
-<link rel="import" href="../paper-button/paper-button.html">
-<link rel="import" href="../paper-icon-button/paper-icon-button.html">
-<link rel="import" href="../paper-header-panel/paper-header-panel.html">
-
-
-</head><body><div hidden="" by-vulcanize=""><dom-module id="tf-globals" assetpath="../tf-globals/">
- <script>/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-/* tslint:disable:no-namespace */
-var TF;
-(function (TF) {
- var Globals;
- (function (Globals) {
- // The names of TensorBoard tabs.
- Globals.TABS = [
- 'scalars', 'images', 'audio', 'graphs', 'distributions', 'histograms',
- 'embeddings', 'text'
- ];
- // 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.
- Globals.USE_HASH = false;
- // If USE_HASH is false, FAKE_HASH holds the hash contents.
- Globals.FAKE_HASH = '';
- })(Globals = TF.Globals || (TF.Globals = {}));
-})(TF || (TF = {}));
-</script>
-</dom-module>
-
-<script src="../lodash/lodash.min.js"></script>
-<link rel="import" href="../paper-slider/paper-slider.html">
-<link rel="import" href="../paper-input/paper-input.html">
-
-<dom-module id="tf-smoothing-input" assetpath="../tf-scalar-dashboard/">
- <template>
- <h3 class="title">Smoothing</h3>
- <div class="smoothing-block">
- <paper-slider id="slider" value="{{weight}}" immediate-value="{{_immediateWeightNumberForPaperSlider}}" type="number" step="[[step]]" min="[[min]]" max="[[max]]"></paper-slider>
- <paper-input id="input" label="weight" no-label-float="" value="{{_inputWeightStringForPaperInput}}" type="number" step="[[step]]" min="[[min]]" max="[[max]]"></paper-input>
- </div>
- <style>
- .title {
- color: var(--paper-grey-800);
- margin: 0;
- font-weight: normal;
- font-size: 14px;
- margin-bottom: 5px;
- }
-
- .smoothing-block {
- display: flex;
- }
-
- paper-slider {
- margin-left: 12px;
- --paper-slider-knob-color: var(--tb-orange-strong);
- --paper-slider-active-color: var(--tb-orange-strong);
- flex-grow: 2;
- }
-
- paper-input {
- --paper-input-container-focus-color: var(--tb-orange-strong);
- --paper-input-container-input: {
- font-size: 14px;
- };
- --paper-input-container-label: {
- font-size: 14px;
- };
- width: 60px;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-smoothing-input",
-
- properties: {
- step: Number,
- max: Number,
- min: Number,
-
- weight: {
- type: Number,
- value: 0.6,
- notify: true
- },
-
- _immediateWeightNumberForPaperSlider: {
- type: Number,
- notify: true,
- observer: '_immediateWeightNumberForPaperSliderChanged'
- },
-
- // Paper input treats values as strings even if you specify them as
- // numbers.
- _inputWeightStringForPaperInput: {
- type: String,
- notify: true,
- observer: '_inputWeightStringForPaperInputChanged'
- }
- },
-
- _updateWeight: _.debounce(function(val) {
- this.weight = val;
- }, 250),
-
- _immediateWeightNumberForPaperSliderChanged: function() {
- this._inputWeightStringForPaperInput =
- this._immediateWeightNumberForPaperSlider.toString();
- this._updateWeight.call(this, this._immediateWeightNumberForPaperSlider);
- },
-
- _inputWeightStringForPaperInputChanged: function() {
- if (+this._inputWeightStringForPaperInput < 0) {
- this._inputWeightStringForPaperInput = '0';
- }
- else if (+this._inputWeightStringForPaperInput > 1) {
- this._inputWeightStringForPaperInput = '1';
- }
-
- var d = +this._inputWeightStringForPaperInput;
- if (!isNaN(d)) {
- this._updateWeight.call(this, d);
- }
- }
- });
- </script>
-</dom-module>
-<script src="../d3/d3.js"></script>
-<script>/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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 VZ;
-(function (VZ) {
- var Sorting;
- (function (Sorting) {
- /**
- * Compares tag names asciinumerically broken into components.
- *
- * <p>This is the comparison function used for sorting most string values in
- * TensorBoard. Unlike the standard asciibetical comparator, this function
- * knows that 'a10b' > 'a2b'. Fixed point and engineering notation are
- * supported. This function also splits the input by slash and underscore to
- * perform array comparison. Therefore it knows that 'a/a' < 'a+/a' even
- * though '+' < '/' in the ASCII table.
- */
- 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++;
- }
- }
- Sorting.compareTagNames = compareTagNames;
- function consumeNumber(s, i) {
- var State;
- (function (State) {
- State[State["NATURAL"] = 0] = "NATURAL";
- State[State["REAL"] = 1] = "REAL";
- State[State["EXPONENT_SIGN"] = 2] = "EXPONENT_SIGN";
- State[State["EXPONENT"] = 3] = "EXPONENT";
- })(State || (State = {}));
- var state = State.NATURAL;
- for (; i < s.length; i++) {
- if (state === State.NATURAL) {
- if (s[i] === '.') {
- state = State.REAL;
- }
- else if (s[i] === 'e' || s[i] === 'E') {
- state = State.EXPONENT_SIGN;
- }
- else if (!isDigit(s[i])) {
- break;
- }
- }
- else if (state === State.REAL) {
- if (s[i] === 'e' || s[i] === 'E') {
- state = State.EXPONENT_SIGN;
- }
- else if (!isDigit(s[i])) {
- break;
- }
- }
- else if (state === State.EXPONENT_SIGN) {
- if (isDigit(s[i]) || s[i] === '+' || s[i] === '-') {
- state = State.EXPONENT;
- }
- else {
- break;
- }
- }
- else if (state === State.EXPONENT) {
- if (!isDigit(s[i])) {
- 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);
- }
- })(Sorting = VZ.Sorting || (VZ.Sorting = {}));
-})(VZ || (VZ = {}));
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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 () {
- var extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
-})();
-var TF;
-(function (TF) {
- var Backend;
- (function (Backend) {
- /**
- * Manages many fetch requests. Launches up to nSimultaneousRequests
- * simultaneously, and maintains a LIFO queue of requests to process when
- * more urls are requested than can be handled at once. The queue can be
- * cleared.
- *
- * When a request is made, a Promise is returned which resolves with the
- * parsed JSON result from the request.
- */
- var RequestCancellationError = (function (_super) {
- __extends(RequestCancellationError, _super);
- function RequestCancellationError() {
- var _this = _super !== null && _super.apply(this, arguments) || this;
- _this.name = 'RequestCancellationError';
- return _this;
- }
- return RequestCancellationError;
- }(Error));
- Backend.RequestCancellationError = RequestCancellationError;
- var RequestNetworkError = (function (_super) {
- __extends(RequestNetworkError, _super);
- function RequestNetworkError(req, url) {
- var _this = _super.call(this) || this;
- _this.message = "RequestNetworkError: " + req.status + " at " + url;
- _this.name = 'RequestNetworkError';
- _this.req = req;
- _this.url = url;
- return _this;
- }
- return RequestNetworkError;
- }(Error));
- Backend.RequestNetworkError = RequestNetworkError;
- var RequestManager = (function () {
- function RequestManager(nSimultaneousRequests, maxRetries) {
- if (nSimultaneousRequests === void 0) { nSimultaneousRequests = 10; }
- if (maxRetries === void 0) { maxRetries = 3; }
- this._queue = [];
- this._nActiveRequests = 0;
- this._nSimultaneousRequests = nSimultaneousRequests;
- this._maxRetries = maxRetries;
- }
- /**
- * Gives a promise that loads assets from given url (respects queuing). If
- * postData is provided, this request will use POST, not GET. This is an
- * object mapping POST keys to string values.
- */
- RequestManager.prototype.request = function (url, postData) {
- var _this = this;
- var promise = new Promise(function (resolve, reject) {
- var resolver = { resolve: resolve, reject: reject };
- _this._queue.push(resolver);
- _this.launchRequests();
- })
- .then(function () {
- return _this.promiseWithRetries(url, _this._maxRetries, postData);
- })
- .then(function (response) {
- // Success - Let's free space for another active
- // reqest, and launch it
- _this._nActiveRequests--;
- _this.launchRequests();
- return response;
- }, function (rejection) {
- if (rejection.name === 'RequestNetworkError') {
- // If we failed due to network error, we should
- // decrement
- // _nActiveRequests because this request was
- // active
- _this._nActiveRequests--;
- _this.launchRequests();
- }
- return Promise.reject(rejection);
- });
- return promise;
- };
- RequestManager.prototype.clearQueue = function () {
- while (this._queue.length > 0) {
- this._queue.pop().reject(new RequestCancellationError('Request cancelled by clearQueue'));
- }
- };
- /* Return number of currently pending requests */
- RequestManager.prototype.activeRequests = function () {
- return this._nActiveRequests;
- };
- /* Return total number of outstanding requests (includes queue) */
- RequestManager.prototype.outstandingRequests = function () {
- return this._nActiveRequests + this._queue.length;
- };
- RequestManager.prototype.launchRequests = function () {
- while (this._nActiveRequests < this._nSimultaneousRequests &&
- this._queue.length > 0) {
- this._nActiveRequests++;
- this._queue.pop().resolve();
- }
- };
- /**
- * Try to request a given URL using overwritable _promiseFromUrl method.
- * If the request fails for any reason, we will retry up to maxRetries
- * times. In practice, this will help us paper over transient network issues
- * like '502 Bad Gateway'.
- * By default, Chrome displays network errors in console, so
- * the user will be able to tell when the requests are failing. I think this
- * is a feature, if the request failures and retries are causing any
- * pain to users, they can see it and file issues.
- */
- RequestManager.prototype.promiseWithRetries = function (url, maxRetries, postData) {
- var _this = this;
- var success = function (x) { return x; };
- var failure = function (x) {
- if (maxRetries > 0) {
- return _this.promiseWithRetries(url, maxRetries - 1, postData);
- }
- else {
- return Promise.reject(x);
- }
- };
- return this._promiseFromUrl(url, postData).then(success, failure);
- };
- /* Actually get promise from url using XMLHttpRequest */
- RequestManager.prototype._promiseFromUrl = function (url, postData) {
- return new Promise(function (resolve, reject) {
- var req = new XMLHttpRequest();
- req.open(postData ? 'POST' : 'GET', url);
- var formData;
- if (postData) {
- // We are to make a POST request.
- formData = new FormData();
- for (var postKey in postData) {
- if (postKey) {
- // The linter requires 'for in' loops to be filtered by an if
- // condition.
- formData.append(postKey, postData[postKey]);
- }
- }
- }
- req.onload = function () {
- if (req.status === 200) {
- resolve(JSON.parse(req.responseText));
- }
- else {
- reject(new RequestNetworkError(req, url));
- }
- };
- req.onerror = function () {
- reject(new RequestNetworkError(req, url));
- };
- req.send(formData);
- });
- };
- return RequestManager;
- }());
- Backend.RequestManager = RequestManager;
- })(Backend = TF.Backend || (TF.Backend = {}));
-})(TF || (TF = {}));
-</script><script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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 Backend;
- (function (Backend) {
- Backend.BAD_CHARACTERS = '#%&{}\\/<>*? $!\'":@+`|=() ';
- /** Cleanup a url so that it can be loaded from a filesystem. */
- function demoify(s) {
- // for consistency with python's urllib.urlencode
- s = s.replace(new RegExp('%20', 'g'), '+');
- for (var i = 0; i < Backend.BAD_CHARACTERS.length; i++) {
- var c = Backend.BAD_CHARACTERS[i];
- s = s.replace(new RegExp('\\' + c, 'g'), '_');
- }
- return s;
- }
- Backend.demoify = demoify;
- function queryEncoder(params) {
- // It's important that the keys be sorted, so we always grab the right file
- // if we are talking to the backend generated by serialze_tensorboard.py
- if (params == null) {
- return '';
- }
- var components = _.keys(params)
- .sort()
- .filter(function (k) { return params[k] !== undefined; })
- .map(function (k) { return k + '=' + encodeURIComponent(params[k]); });
- var result = components.length ? '?' + components.join('&') : '';
- // Replace parens for consistency with urllib.urlencode
- return result.replace(/\(/g, '%28').replace(/\)/g, '%29');
- }
- Backend.queryEncoder = queryEncoder;
- })(Backend = TF.Backend || (TF.Backend = {}));
-})(TF || (TF = {}));
-</script><script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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 Backend;
- (function (Backend) {
- ;
- /**
- * The standard router for communicating with the TensorBoard backend
- * @param dataDir {string} The base prefix for finding data on server.
- * @param demoMode {boolean} Whether to modify urls for filesystem demo usage.
- */
- function router(dataDir, demoMode) {
- if (dataDir === void 0) { dataDir = 'data'; }
- if (demoMode === void 0) { demoMode = false; }
- var clean = demoMode ? Backend.demoify : function (x) { return x; };
- if (dataDir[dataDir.length - 1] === '/') {
- dataDir = dataDir.slice(0, dataDir.length - 1);
- }
- function standardRoute(route, demoExtension) {
- if (demoExtension === void 0) { demoExtension = '.json'; }
- return function (tag, run) {
- var url = dataDir + '/' + route + clean(Backend.queryEncoder({ tag: tag, run: run }));
- if (demoMode) {
- url += demoExtension;
- }
- return url;
- };
- }
- function individualImageUrl(query, wallTime) {
- var url = dataDir + '/' + clean('individualImage?' + query);
- // Include wall_time just to disambiguate the URL and force the browser
- // to reload the image when the URL changes. The backend doesn't care
- // about the value.
- url += demoMode ? '.png' : '&ts=' + wallTime;
- return url;
- }
- function individualAudioUrl(query) {
- var url = dataDir + '/' + clean('individualAudio?' + query);
- if (demoMode) {
- url += '.wav';
- }
- return url;
- }
- function graphUrl(run, limit_attr_size, large_attrs_key) {
- var query_params = [['run', clean(run)]];
- if (limit_attr_size != null && !demoMode) {
- query_params.push(['limit_attr_size', String(limit_attr_size)]);
- }
- if (large_attrs_key != null && !demoMode) {
- query_params.push(['large_attrs_key', large_attrs_key]);
- }
- var query = query_params
- .map(function (param) {
- return param[0] + '=' + encodeURIComponent(param[1]);
- })
- .join('&');
- var url = dataDir + '/graph' + clean('?' + query);
- if (demoMode) {
- url += '.pbtxt';
- }
- return url;
- }
- return {
- logdir: function () { return dataDir + '/logdir'; },
- runs: function () { return dataDir + '/runs' + (demoMode ? '.json' : ''); },
- individualImage: individualImageUrl,
- individualAudio: individualAudioUrl,
- graph: graphUrl,
- scalars: standardRoute('scalars'),
- histograms: standardRoute('histograms'),
- compressedHistograms: standardRoute('compressedHistograms'),
- images: standardRoute('images'),
- audio: standardRoute('audio'),
- runMetadata: standardRoute('run_metadata', '.pbtxt'),
- healthPills: function () { return dataDir + '/plugin/debugger/health_pills'; },
- textRuns: function () { return dataDir + '/plugin/text/runs' + (demoMode ? '.json' : ''); },
- text: standardRoute('plugin/text/text'),
- };
- }
- Backend.router = router;
- ;
- })(Backend = TF.Backend || (TF.Backend = {}));
-})(TF || (TF = {}));
-</script><script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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 Backend;
- (function (Backend_1) {
- ;
- ;
- Backend_1.TYPES = [
- 'scalar', 'histogram', 'compressedHistogram', 'graph', 'image', 'audio',
- 'runMetadata', 'text'
- ];
- /**
- * The Backend class provides a convenient and typed interface to the backend.
- *
- * It provides methods corresponding to the different data sources on the
- * TensorBoard backend. These methods return a promise containing the data
- * from the backend. This class does some post-processing on the data; for
- * example, converting data elements tuples into js objects so that they can
- * be accessed in a more convenient and clearly-documented fashion.
- */
- var Backend = (function () {
- /**
- * Construct a Backend instance.
- * @param router the Router with info on what urls to get data from
- * @param requestManager The RequestManager, overwritable so you may
- * manually clear request queue, etc. Defaults to a new RequestManager.
- */
- function Backend(router, requestManager) {
- this.router = router;
- this.requestManager = requestManager || new Backend_1.RequestManager();
- }
- /**
- * Returns a promise for requesting the logdir string.
- */
- Backend.prototype.logdir = function () {
- return this.requestManager.request(this.router.logdir());
- };
- /**
- * Returns a listing of all the available data in the TensorBoard backend.
- */
- Backend.prototype.runs = function () {
- return this.requestManager.request(this.router.runs());
- };
- /**
- * Return a promise showing the Run-to-Tag mapping for scalar data.
- */
- Backend.prototype.scalarRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, 'scalars'); });
- };
- /**
- * Return a promise showing the Run-to-Tag mapping for histogram data.
- */
- Backend.prototype.histogramRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, 'histograms'); });
- };
- /**
- * Return a promise showing the Run-to-Tag mapping for image data.
- */
- Backend.prototype.imageRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, 'images'); });
- };
- /**
- * Return a promise showing the Run-to-Tag mapping for audio data.
- */
- Backend.prototype.audioRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, 'audio'); });
- };
- /**
- * Return a promise showing the Run-to-Tag mapping for compressedHistogram
- * data.
- */
- Backend.prototype.compressedHistogramRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, 'compressedHistograms'); });
- };
- /**
- * Return a promise showing list of runs that contain graphs.
- */
- Backend.prototype.graphRuns = function () {
- return this.runs().then(function (x) { return _.keys(x).filter(function (k) { return x[k].graph; }); });
- };
- /**
- * Return a promise showing the Run-to-Tag mapping for run_metadata objects.
- */
- Backend.prototype.runMetadataRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, 'run_metadata'); });
- };
- /**
- * Returns a promise showing the Run-to-Tag mapping for text data.
- */
- Backend.prototype.textRuns = function () {
- return this.requestManager.request(this.router.textRuns());
- };
- /**
- * Returns a promise containing TextDatums for given run and tag.
- */
- Backend.prototype.text = function (tag, run) {
- var url = this.router.text(tag, run);
- // tslint:disable-next-line:no-any it's convenient and harmless here
- return this.requestManager.request(url).then(map(function (x) {
- x.wall_time = timeToDate(x.wall_time);
- return x;
- }));
- };
- /**
- * Return a promise of a graph string from the backend.
- */
- Backend.prototype.graph = function (tag, limit_attr_size, large_attrs_key) {
- var url = this.router.graph(tag, limit_attr_size, large_attrs_key);
- return this.requestManager.request(url);
- };
- /**
- * Return a promise containing ScalarDatums for given run and tag.
- */
- Backend.prototype.scalar = function (tag, run) {
- var p;
- var url = this.router.scalars(tag, run);
- p = this.requestManager.request(url);
- return p.then(map(detupler(createScalar)));
- };
- /**
- * Returns a promise for requesting the health pills for a list of nodes.
- */
- Backend.prototype.healthPills = function (nodeNames, step) {
- var postData = { 'node_names': JSON.stringify(nodeNames) };
- if (step !== undefined) {
- // The user requested health pills for a specific step. This request
- // might be slow since the backend reads events sequentially from disk.
- postData['step'] = step;
- }
- return this.requestManager.request(this.router.healthPills(), postData);
- };
- /**
- * Return a promise containing HistogramDatums for given run and tag.
- */
- Backend.prototype.histogram = function (tag, run) {
- var p;
- 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, min, max)
- };
- });
- });
- };
- /**
- * Return a promise containing ImageDatums for given run and tag.
- */
- Backend.prototype.image = function (tag, run) {
- var url = this.router.images(tag, run);
- var p;
- p = this.requestManager.request(url);
- return p.then(map(this.createImage.bind(this)));
- };
- /**
- * Return a promise containing AudioDatums for given run and tag.
- */
- Backend.prototype.audio = function (tag, run) {
- var url = this.router.audio(tag, run);
- var p;
- p = this.requestManager.request(url);
- return p.then(map(this.createAudio.bind(this)));
- };
- /**
- * Returns a promise to load the string RunMetadata for given run/tag.
- */
- Backend.prototype.runMetadata = function (tag, run) {
- var url = this.router.runMetadata(tag, run);
- return this.requestManager.request(url);
- };
- /**
- * Get compressedHistogram data.
- * Unlike other methods, don't bother reprocessing this data into a nicer
- * format. This is because we will deprecate this route.
- */
- Backend.prototype.compressedHistogram = function (tag, run) {
- var url = this.router.compressedHistograms(tag, run);
- var p;
- p = this.requestManager.request(url);
- return p.then(map(detupler(function (x) { return x; })));
- };
- Backend.prototype.createImage = function (x) {
- return {
- width: x.width,
- height: x.height,
- wall_time: timeToDate(x.wall_time),
- step: x.step,
- url: this.router.individualImage(x.query, x.wall_time),
- };
- };
- Backend.prototype.createAudio = function (x) {
- return {
- content_type: x.content_type,
- wall_time: timeToDate(x.wall_time),
- step: x.step,
- url: this.router.individualAudio(x.query),
- };
- };
- return Backend;
- }());
- Backend_1.Backend = Backend;
- /** Given a RunToTag, return sorted array of all runs */
- function getRuns(r) {
- return _.keys(r).sort(VZ.Sorting.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(VZ.Sorting.compareTagNames);
- }
- Backend_1.getTags = getTags;
- /**
- * Given a RunToTag and an array of runs, return every tag that appears for
- * at least one run.
- * Sorted, deduplicated.
- */
- function filterTags(r, runs) {
- var result = [];
- runs.forEach(function (x) { return result = result.concat(r[x]); });
- return _.uniq(result).sort(VZ.Sorting.compareTagNames);
- }
- Backend_1.filterTags = filterTags;
- function timeToDate(x) { return new Date(x * 1000); }
- ;
- /** Just a curryable map to make things cute and tidy. */
- function map(f) {
- return function (arr) { return arr.map(f); };
- }
- ;
- /**
- * This is a higher order function that takes a function that transforms a
- * T into a G, and returns a function that takes TupleData<T>s and converts
- * them into the intersection of a G and a Datum.
- */
- function detupler(xform) {
- return function (x) {
- // Create a G, assert it has type <G & Datum>
- var obj = xform(x[2]);
- // ... patch in the properties of datum
- obj.wall_time = timeToDate(x[0]);
- obj.step = x[1];
- return obj;
- };
- }
- ;
- function createScalar(x) { return { scalar: x }; }
- ;
- function createHistogram(x) {
- return {
- min: x[0],
- max: x[1],
- nItems: x[2],
- sum: x[3],
- sumSquares: x[4],
- bucketRightEdges: x[5],
- bucketCounts: x[6],
- };
- }
- ;
- /**
- * 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. 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, 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.'));
- }
- if (max === min) {
- // Create bins even if all the data has a single value.
- max = min * 1.1 + 1;
- min = min / 1.1 - 1;
- }
- 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;
- })(Backend = TF.Backend || (TF.Backend = {}));
-})(TF || (TF = {}));
-</script><script>/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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 Backend;
- (function (Backend) {
- Backend.Behavior = {
- properties: {
- /** *** Required properties *** */
- /** Data type. One of TF.Backend.TYPES */
- dataType: {
- type: String,
- observer: '_throwErrorOnUnrecognizedType',
- },
- /** TF.Backend.Backend for data loading. */
- backend: {
- type: Object,
- },
- /** Should it automatically load when configured ready? Default true. */
- autoLoad: {
- type: Boolean,
- value: true,
- },
- /** *** Component-provided properties *** */
- /** Every tag available for data type (sorted, dedpulicated) */
- tags: {
- type: Array,
- readOnly: true,
- notify: true,
- },
- /** Every run available for data type (sorted) */
- runs: {
- type: Array,
- readOnly: true,
- notify: true,
- },
- /** Mapping from runs to tags for the data type */
- run2tag: {
- type: Object,
- readOnly: true,
- notify: true,
- },
- /** Promise provider for the data. Useful for passing to subcomponents */
- dataProvider: { type: Function, computed: '_getDataProvider(dataType, backend)' },
- /** Has the dashboard loaded yet? */
- loadState: {
- type: String,
- value: 'noload',
- readOnly: true,
- },
- /**
- * True if dashboard has loaded, and no tags were found.
- * Persists through subsequent reloads (ie. still true while
- * next load is pending) so warning won't flash away every reload
- * when there is no data.
- */
- dataNotFound: {
- type: Boolean,
- value: false,
- readOnly: true,
- }
- },
- observers: ['_do_autoLoad(dataType, backend, autoLoad)'],
- /**
- * Reloading works in two steps:
- * Backend reload, which gets metadata on available runs, tags, etc from
- * the backend.
- * Frontend reload, which loads new data for each chart or visual display.
- * Backend reload logic is provided by this behaivor. The frontend reload
- * logic should be provided elsewhere, since it is component-specific.
- * To keep things simple and consistent, we do the backend reload first,
- * and the frontend reload afterwards.
- */
- reload: function () {
- var _this = this;
- return this.backendReload().then(function (x) { return _this.frontendReload(); });
- },
- /**
- * Load data from backend and then set run2tag, tags, runs, and loadState.
- * Returns a promise that resolves/rejects when data is loaded.
- */
- backendReload: function () {
- var _this = this;
- if (this.dataType == null) {
- throw new Error('TF.Backend.Behavior: Need a dataType to reload.');
- }
- if (this.backend == null) {
- throw new Error('TF.Backend.Behavior: Need a backend to reload.');
- }
- var runsRoute = this.backend[this.dataType + 'Runs'].bind(this.backend);
- this._setLoadState('pending');
- return runsRoute().then(function (x) {
- _this._setLoadState('loaded');
- if (_.isEqual(x, _this.run2tag)) {
- // If x and run2tag are equal, let's avoid updating everything
- // since that can needlessly trigger run changes, reloads, etc
- return x;
- }
- _this._setRun2tag(x);
- var tags = TF.Backend.getTags(x);
- _this._setDataNotFound(tags.length === 0);
- _this._setTags(tags);
- _this._setRuns(TF.Backend.getRuns(x));
- return x;
- }, function (fail) {
- _this._setLoadState('failure');
- return fail;
- });
- },
- _do_autoLoad: function (type, backend, autoLoad) {
- if (autoLoad) {
- this.reload();
- }
- ;
- },
- _getDataProvider: function (dataType, backend) {
- return this.backend[this.dataType].bind(this.backend);
- },
- _throwErrorOnUnrecognizedType: function (dataType) {
- if (TF.Backend.TYPES.indexOf(dataType) === -1) {
- throw new Error('TF.Backend.Behavior: Unknown dataType ' + dataType);
- }
- },
- };
- })(Backend = TF.Backend || (TF.Backend = {}));
-})(TF || (TF = {}));
-</script>
-
-
-
-
-
-<dom-module id="tf-color-scale" assetpath="../tf-color-scale/">
- <script>/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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) {
- TF.palettes = {
- googleStandard: [
- '#db4437',
- '#ff7043',
- '#f4b400',
- '#0f9d58',
- '#00796b',
- '#00acc1',
- '#4285f4',
- '#5c6bc0',
- '#ab47bc' // purple 400
- ],
- googleCool: [
- '#9e9d24',
- '#0f9d58',
- '#00796b',
- '#00acc1',
- '#4285f4',
- '#5c6bc0',
- '#607d8b' // blue gray 500
- ],
- googleWarm: [
- '#795548',
- '#ab47bc',
- '#f06292',
- '#c2185b',
- '#db4437',
- '#ff7043',
- '#f4b400' // google yellow 700
- ],
- googleColorBlindAssist: [
- '#ff7043',
- '#00ACC1',
- '#AB47BC',
- '#2A56C6',
- '#0b8043',
- '#F7CB4D',
- '#c0ca33',
- '#5e35b1',
- '#A52714',
- ],
- // These palettes try to be better for color differentiation.
- // https://personal.sron.nl/~pault/
- colorBlindAssist1: ['#4477aa', '#44aaaa', '#aaaa44', '#aa7744', '#aa4455', '#aa4488'],
- colorBlindAssist2: [
- '#88ccee', '#44aa99', '#117733', '#999933', '#ddcc77', '#cc6677',
- '#882255', '#aa4499'
- ],
- colorBlindAssist3: [
- '#332288', '#6699cc', '#88ccee', '#44aa99', '#117733', '#999933',
- '#ddcc77', '#cc6677', '#aa4466', '#882255', '#661100', '#aa4499'
- ],
- // based on this palette: http://mkweb.bcgsc.ca/biovis2012/
- colorBlindAssist4: [
- '#FF6DB6', '#920000', '#924900', '#DBD100', '#24FF24', '#006DDB',
- '#490092'
- ],
- mldash: [
- '#E47EAD', '#F4640D', '#FAA300', '#F5E636', '#00A077', '#0077B8',
- '#00B7ED'
- ]
- };
-})(TF || (TF = {}));
-</script>
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-// Example usage:
-// runs = ["train", "test", "test1", "test2"]
-// ccs = new TF.ColorScale();
-// ccs.domain(runs);
-// ccs.getColor("train");
-// ccs.getColor("test1");
-var TF;
-(function (TF) {
- var ColorScale = (function () {
- /**
- * Creates a color scale with optional custom palette.
- * @param {string[]} [palette=TF.palettes.googleColorBlind] - The color
- * palette you want as an Array of hex strings.
- */
- function ColorScale(palette) {
- if (palette === void 0) { palette = TF.palettes.googleColorBlindAssist; }
- this.identifiers = d3.map();
- this.palette = palette;
- }
- /**
- * Set the domain of strings.
- * @param {string[]} strings - An array of possible strings to use as the
- * domain for your scale.
- */
- ColorScale.prototype.domain = function (strings) {
- var _this = this;
- this.identifiers = d3.map();
- strings.forEach(function (s, i) {
- _this.identifiers.set(s, _this.palette[i % _this.palette.length]);
- });
- return this;
- };
- /**
- * Use the color scale to transform an element in the domain into a color.
- * @param {string} The input string to map to a color.
- * @return {string} The color corresponding to that input string.
- * @throws Will error if input string is not in the scale's domain.
- */
- ColorScale.prototype.scale = function (s) {
- if (!this.identifiers.has(s)) {
- throw new Error('String was not in the domain.');
- }
- return this.identifiers.get(s);
- };
- return ColorScale;
- }());
- TF.ColorScale = ColorScale;
-})(TF || (TF = {}));
-</script>
- <script>
- (function() {
- Polymer({
- is: "tf-color-scale",
- properties: {
- runs: {
- type: Array,
- },
- outColorScale: {
- type: Object,
- readOnly: true,
- notify: true,
- value: function() {
- return new TF.ColorScale();
- },
- },
- },
- observers: ['updateColorScale(runs.*)'],
- updateColorScale: function(runsChange) {
- this.outColorScale.domain(this.runs);
- },
- });
- })();
- </script>
-</dom-module>
-<link rel="import" href="../paper-styles/paper-styles.html">
-
-<dom-module id="scrollbar-style" assetpath="../tf-dashboard-common/">
- <template>
- <style>
- .scrollbar::-webkit-scrollbar-track
- {
- visibility: hidden;
- }
-
- .scrollbar::-webkit-scrollbar
- {
- width: 10px;
- }
-
- .scrollbar::-webkit-scrollbar-thumb
- {
- border-radius: 10px;
- -webkit-box-shadow: inset 0 0 2px rgba(0,0,0,.3);
- background-color: var(--paper-grey-500);
- color: var(--paper-grey-900);
- }
- .scrollbar {
- box-sizing: border-box;
- }
- </style>
- </template>
-</dom-module>
-<style is="custom-style">
-
- :root {
- --tb-orange-weak: #ffa726;
- --tb-orange-strong: #f57c00;
- --tb-grey-darker: #e2e2e2;
- --tb-grey-lighter: #f3f3f3;
- --tb-ui-dark-accent: #757575;
- --tb-ui-light-accent: #e0e0e0;
- --tb-graph-faded: #e0d4b3;
- }
-
-</style>
-
-<dom-module id="tf-dashboard-layout" assetpath="../tf-dashboard-common/">
- <template>
- <div id="sidebar">
- <content select=".sidebar"></content>
- </div>
-
- <div id="center" class="scrollbar">
- <content select=".center"></content>
- </div>
- <style include="scrollbar-style"></style>
- <style>
- #sidebar {
- width: inherit;
- height: 100%;
- overflow: ellipsis;
- flex-grow: 0;
- flex-shrink: 0;
- }
-
- #center {
- height: 100%;
- overflow-y: auto;
- flex-grow: 1;
- flex-shrink: 1;
- }
-
- .tf-graph-dashboard #center {
- background: white;
- }
-
- :host {
- display: flex;
- flex-direction: row;
- height: 100%;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-dashboard-layout",
- });
- </script>
-</dom-module>
-<dom-module id="dashboard-style" assetpath="../tf-dashboard-common/">
- <template>
- <style>
- .sidebar {
- display: flex;
- flex-direction: column;
- height: 100%;
- margin-right: 20px;
- }
-
- .sidebar-section {
- border-top: solid 1px rgba(0, 0, 0, 0.12);
- padding: 15px 0px 15px 30px;
- }
-
- .sidebar-section:first-child {
- border: none;
- }
-
- .sidebar-section:last-child {
- flex-grow: 1;
- display: flex;
- }
-
- paper-checkbox {
- --paper-checkbox-checked-color: var(--tb-ui-dark-accent);
- --paper-checkbox-unchecked-color: var(--tb-ui-dark-accent);
- font-size: 14px;
- margin-top: 5px;
- }
- </style>
- </template>
-</dom-module>
-<link rel="import" href="../paper-dropdown-menu/paper-dropdown-menu.html">
-<link rel="import" href="../paper-menu/paper-menu.html">
-<link rel="import" href="../paper-item/paper-item.html">
-
-<dom-module id="tf-downloader" assetpath="../tf-dashboard-common/">
- <template>
- <paper-dropdown-menu no-label-float="true" label="run to download" selected-item-label="{{_run}}">
- <paper-menu class="dropdown-content">
- <template is="dom-repeat" items="[[runs]]">
- <paper-item no-label-float="true">[[item]]</paper-item>
- </template>
- </paper-menu>
- </paper-dropdown-menu>
- <div class="center">
- <span>
- <a download="[[_csvName(_run)]]" href="[[_csvUrl(_run, urlFn)]]">CSV</a>
- <a download="[[_jsonName(_run)]]" href="[[_jsonUrl(_run, urlFn)]]">JSON</a>
- </span>
- </div>
- <style>
- :host {
- display: flex;
- height: 32px;
- }
- .center {
- display: flex;
- align-self: center;
- }
- paper-dropdown-menu {
- width: 100px;
- --paper-input-container-label: {
- font-size: 10px;
- }
- --paper-input-container-input: {
- font-size: 10px;
- }
- }
- a {
- font-size: 10px;
- border-radius: 3px;
- border: 1px solid #EEE;
- }
- paper-input {
- font-size: 22px;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-downloader",
- properties: {
- _run: String,
- runs: Array,
- tag: String,
- urlFn: Function,
- },
- _csvUrl: function(_run, urlFn) {
- return urlFn(this.tag, _run) + "&format=csv";
- },
- _jsonUrl: function(_run, urlFn) {
- return urlFn(this.tag, _run);
- },
- _csvName: function(_run) {
- return "run_" + _run + ",tag_" + this.tag + ".csv";
- },
- _jsonName: function(_run) {
- return "run-" + _run + "-tag-" + this.tag + ".json";
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-no-data-warning" assetpath="../tf-dashboard-common/">
- <template>
- <template is="dom-if" if="[[showWarning]]">
- <div class="warning">
- <template is="dom-if" if="[[_isGraph(dataType)]]">
- <h3>
- No graph definition files were found.
- </h3>
- <p>
- To store a graph, create a
- <code>tf.summary.FileWriter</code>
- and pass the graph either via the constructor, or by calling its
- <code>add_graph()</code> method.
- You may want to check out the
- <a href="https://www.tensorflow.org/get_started/graph_viz">
- graph visualizer tutorial
- </a>.
- </p>
- </template>
- <template is="dom-if" if="[[_isProjector(dataType)]]">
- <h3>
- No checkpoint was found.
- </h3>
- <p>
- Probable causes:
- </p><ul>
- <li>
- No checkpoint has been saved yet. Please refresh the page periodically.
- </li>
- <li>
- You are not saving any checkpoint. To save your model,
- create a
- <a href="https://www.tensorflow.org/api_docs/python/tf/train/Saver">
- <code>tf.train.Saver</code>
- </a>
- and save your model periodically
- by calling <code>saver.save(session, LOG_DIR/model.ckpt, step)</code>.
- </li>
- </ul>
- <p></p>
- </template>
- <template is="dom-if" if="[[_isOther(dataType)]]">
- <h3>
- No <span>[[dataType]]</span> data was found.
- </h3>
- <p>
- Probable causes:
- </p><ul>
- <li>
- You haven't written any <span>[[dataType]]</span> data
- to your event files.
- </li>
- <li>
- TensorBoard can't find your event files.
- </li>
- </ul>
- <p></p>
- </template>
- <p>
- If you're new to using TensorBoard, and want to find out how to add
- data and set up your event files, check out the
- <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tensorboard/README.md">
- README
- </a>
- and perhaps the
- <a href="https://www.tensorflow.org/get_started/summaries_and_tensorboard">
- TensorBoard tutorial
- </a>.
- </p>
-
- <p>
- If you think TensorBoard is configured properly, please see the
- <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tensorboard/README.md#my-tensorboard-isnt-showing-any-data-whats-wrong">
- section of the README devoted to missing data problems
- </a>
- and consider filing an issue on GitHub.
- </p>
-
- </div>
- </template>
- <style>
- .warning {
- max-width: 540px;
- margin: 80px auto 0 auto;
- }
- </style>
- </template>
-
- <script>
- Polymer({
- is: "tf-no-data-warning",
- properties: {
- dataType: String,
- showWarning: Boolean
- },
- _isGraph: function(dataType) {
- return dataType === "graph";
- },
- _isProjector: function(dataType) {
- return dataType === "projector";
- },
- _isOther: function(dataType) {
- return !this._isGraph(dataType) && !this._isProjector(dataType);
- }
- });
- </script>
-</dom-module>
-<script>/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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 Dashboard;
- (function (Dashboard) {
- /**
- * A behavior that TensorBoard dashboards must implement. This behavior serves
- * the purpose of an interface.
- */
- function DashboardBehavior(dashboardName) {
- return {
- properties: {
- name: {
- type: String,
- value: dashboardName,
- readOnly: true,
- },
- },
- // This method is called when the dashboard reloads, either when the
- // dashboard is first visited, periodically reloaded, or manually reloaded
- // via the user clicking the button. Note that dashboard custom elements
- // that use TF.Dashboard.ReloadBehavior already implement a reload method.
- reload: function () {
- throw Error('The ' + dashboardName + ' dashboard does not implement reload.');
- },
- };
- }
- Dashboard.DashboardBehavior = DashboardBehavior;
- })(Dashboard = TF.Dashboard || (TF.Dashboard = {}));
-})(TF || (TF = {}));
-</script><script>/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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 Dashboard;
- (function (Dashboard) {
- /**
- * ReloadBehavior: A simple behavior for dashboards where the
- * frontendReload() function should find every child element with a
- * given tag name (e.g. "tf-line-chart" or "tf-image-loader")
- * and call a `reload` method on that child.
- * May later extend it so it has more sophisticated logic, e.g. reloading
- * only tags that are in view.
- */
- function ReloadBehavior(tagName) {
- return {
- properties: {
- reloadTag: {
- type: String,
- value: tagName,
- },
- },
- frontendReload: function () {
- var elements = this.getElementsByTagName(this.reloadTag);
- Array.prototype.forEach.call(elements, function (x) { x.reload(); });
- },
- };
- }
- Dashboard.ReloadBehavior = ReloadBehavior;
- })(Dashboard = TF.Dashboard || (TF.Dashboard = {}));
-})(TF || (TF = {}));
-</script>
-
-
-<dom-module id="tf-option-selector" assetpath="../tf-dashboard-common/">
- <template>
- <div id="wrap">
- <h3>[[name]]</h3>
- <div class="content-wrapper"><content></content></div>
- </div>
- <style>
- .content-wrapper ::content > * {
- width: 30%;
- font-size: 13px;
- background: none;
- margin-top: 10px;
- color: var(--tb-ui-dark-accent);
- }
-
- .content-wrapper ::content :first-of-type {
- margin-left: 0;
- }
-
- .content-wrapper ::content .selected {
- background-color: var(--tb-ui-dark-accent);
- color: white!important;
- }
-
- h3 {
- color: var(--paper-grey-800);
- margin: 0;
- font-weight: normal;
- font-size: 14px;
- margin-bottom: 5px;
- display: block;
- pointer-events: none;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-option-selector",
- properties: {
- name: String,
- selectedId: {
- type: String,
- notify: true,
- observer: '_selectedIdChanged'
- }
- },
- attached: function() {
- this.async(function() {
- this.getEffectiveChildren().forEach(function(node) {
- this.listen(node, 'tap', '_selectTarget');
- }.bind(this));
- });
- },
- _selectTarget: function(e) {
- this.selectedId = e.currentTarget.id;
- },
- _selectedIdChanged: function() {
- var selected = this.queryEffectiveChildren('#' + this.selectedId);
- if (!selected) {
- return;
- }
-
- this.getEffectiveChildren().forEach(function(node) {
- node.classList.remove("selected");
- });
- selected.classList.add("selected");
- }
- });
- </script>
-</dom-module>
-<link rel="import" href="../iron-collapse/iron-collapse.html">
-
-<dom-module id="tf-collapsable-pane" assetpath="../tf-dashboard-common/">
- <template>
- <button class="heading" on-tap="togglePane" open-button$="[[opened]]">
- <span class="name">[[name]]</span>
- <span class="count">
- <span>[[count]]</span>
- </span>
- </button>
- <iron-collapse opened="[[opened]]">
- <div class="content">
- <template is="dom-if" if="[[opened]]" restamp="[[restamp]]">
- <content></content>
- </template>
- </div>
- </iron-collapse>
- <style>
- :host {
- display: block;
- margin: 0 5px 1px 10px;
- }
-
- :host:first-of-type {
- margin-top: 20px;
- }
-
- :host:last-of-type {
- margin-bottom: 20px;
- }
-
- .heading {
- background-color: white;
- border: none;
- cursor: pointer;
- width: 100%;
- font-size: 15px;
- line-height: 1;
- box-shadow: 0 1px 5px rgba(0,0,0,0.2);
- padding: 10px 15px;
- }
-
- .content {
- padding: 15px;
- border: 1px solid #dedede;
- border-top: none;
- border-bottom-left-radius: 2px;
- border-bottom-right-radius: 2px;
- background: white;
- }
-
- [open-button] {
- border-bottom-left-radius: 0px !important;
- border-bottom-right-radius: 0px !important;
- }
-
- .name {
- float: left;
- }
-
- .count {
- float: right;
- margin-right: 5px;
- font-size: 12px;
- color: var(--paper-grey-500);
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-collapsable-pane",
- properties: {
- opened: {type: Boolean, value: false},
- restamp: {type: Boolean, value: true},
- name: {type: String, observer: "hide"},
- count: {type: Number},
- },
- hide: function() {
- this.opened = false;
- },
- togglePane: function() {
- this.opened = !this.opened;
- }
- });
- </script>
-
-</dom-module>
-<script src="../plottable/plottable.js"></script>
-<style>
-/**
- * @license
- * The MIT License (MIT)
- *
- * Copyright (c) 2014-2015 Palantir Technologies, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * Plottable 1.16.1 (https://github.com/palantir/plottable)
- * Copyright 2014-2015 Palantir Technologies
- * Licensed under MIT (https://github.com/palantir/plottable/blob/master/LICENSE)
-*/
-.plottable-colors-0 {
- background-color: #5279c7; /* INDIGO */
-}
-
-.plottable-colors-1 {
- background-color: #fd373e; /* CORAL_RED */
-}
-
-.plottable-colors-2 {
- background-color: #63c261; /* FERN */
-}
-
-.plottable-colors-3 {
- background-color: #fad419; /* BRIGHT_SUN */
-}
-
-.plottable-colors-4 {
- background-color: #2c2b6f; /* JACARTA */
-}
-
-.plottable-colors-5 {
- background-color: #ff7939; /* BURNING_ORANGE */
-}
-
-.plottable-colors-6 {
- background-color: #db2e65; /* CERISE_RED */
-}
-
-.plottable-colors-7 {
- background-color: #99ce50; /* CONIFER */
-}
-
-.plottable-colors-8 {
- background-color: #962565; /* ROYAL_HEATH */
-}
-
-.plottable-colors-9 {
- background-color: #06cccc; /* ROBINS_EGG_BLUE */
-}
-
-svg.plottable {
- display : block; /* SVGs must be block elements for width/height calculations to work in Firefox. */
- pointer-events: visibleFill;
-}
-
-.plottable .background-fill {
- fill: none;
- pointer-events: none;
-}
-
-.plottable .bounding-box {
- /* Invisible pink bounding-box to allow for collision testing */
- fill: pink;
- visibility: hidden;
-}
-
-.plottable .label text {
- font-family: "Helvetica Neue", sans-serif;
- fill: #32313F;
-}
-
-.plottable .bar-label-text-area text {
- font-family: "Helvetica Neue", sans-serif;
- font-size: 14px;
-}
-
-.plottable .label-area text {
- fill: #32313F;
- font-family: "Helvetica Neue", sans-serif;
- font-size: 14px;
-}
-
-.plottable .light-label text {
- fill: white;
-}
-
-.plottable .dark-label text {
- fill: #32313F;
-}
-
-.plottable .off-bar-label text {
- fill: #32313F;
-}
-
-.plottable .stacked-bar-plot .off-bar-label {
- /* HACKHACK #2795: correct off-bar label logic to be implemented on StackedBar */
- visibility: hidden !important;
-}
-
-.plottable .axis-label text {
- font-size: 10px;
- font-weight: bold;
- letter-spacing: 1px;
- line-height: normal;
- text-transform: uppercase;
-}
-
-.plottable .title-label text {
- font-size: 20px;
- font-weight: bold;
-}
-
-.plottable .axis line.baseline {
- stroke: #CCC;
- stroke-width: 1px;
-}
-
-.plottable .axis line.tick-mark {
- stroke: #CCC;
- stroke-width: 1px;
-}
-
-.plottable .axis text {
- fill: #32313F;
- font-family: "Helvetica Neue", sans-serif;
- font-size: 12px;
- font-weight: 200;
- line-height: normal;
-}
-
-.plottable .axis .annotation-circle {
- fill: white;
- stroke-width: 1px;
- stroke: #CCC;
-}
-
-.plottable .axis .annotation-line {
- stroke: #CCC;
- stroke-width: 1px;
-}
-
-.plottable .axis .annotation-rect {
- stroke: #CCC;
- stroke-width: 1px;
- fill: white;
-}
-
-.plottable .bar-plot .baseline {
- stroke: #999;
-}
-
-.plottable .gridlines line {
- stroke: #3C3C3C; /* hackhack: gridlines should be solid; see #820 */
- opacity: 0.25;
- stroke-width: 1px;
-}
-
-.plottable .selection-box-layer .selection-area {
- fill: black;
- fill-opacity: 0.03;
- stroke: #CCC;
-}
-/* DragBoxLayer */
-.plottable .drag-box-layer.x-resizable .drag-edge-lr {
- cursor: ew-resize;
-}
-.plottable .drag-box-layer.y-resizable .drag-edge-tb {
- cursor: ns-resize;
-}
-
-.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-tl {
- cursor: nwse-resize;
-}
-.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-tr {
- cursor: nesw-resize;
-}
-.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-bl {
- cursor: nesw-resize;
-}
-.plottable .drag-box-layer.x-resizable.y-resizable .drag-corner-br {
- cursor: nwse-resize;
-}
-
-.plottable .drag-box-layer.movable .selection-area {
- cursor: move; /* IE fallback */
- cursor: -moz-grab;
- cursor: -webkit-grab;
- cursor: grab;
-}
-
-.plottable .drag-box-layer.movable .selection-area:active {
- cursor: -moz-grabbing;
- cursor: -webkit-grabbing;
- cursor: grabbing;
-}
-/* /DragBoxLayer */
-
-.plottable .guide-line-layer line.guide-line {
- stroke: #CCC;
- stroke-width: 1px;
-}
-
-.plottable .drag-line-layer.enabled.vertical line.drag-edge {
- cursor: ew-resize;
-}
-
-.plottable .drag-line-layer.enabled.horizontal line.drag-edge {
- cursor: ns-resize;
-}
-
-.plottable .legend text {
- fill: #32313F;
- font-family: "Helvetica Neue", sans-serif;
- font-size: 12px;
- font-weight: bold;
- line-height: normal;
-}
-
-.plottable .interpolated-color-legend rect.swatch-bounding-box {
- fill: none;
- stroke: #CCC;
- stroke-width: 1px;
- pointer-events: none;
-}
-
-.plottable .waterfall-plot line.connector {
- stroke: #CCC;
- stroke-width: 1px;
-}
-
-.plottable .pie-plot .arc.outline {
- stroke-linejoin: round;
-}
-
-</style>
-
-<dom-module id="tf-chart-scaffold" assetpath="../tf-dashboard-common/">
- <template>
- <content></content>
- <style>
- :host {
- -webkit-user-select: none;
- -moz-user-select: none;
- display: flex;
- flex-direction: column;
- flex-grow: 1;
- flex-shrink: 1;
- position: relative;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-chart-scaffold",
- properties: {
- tag: String,
- dataProvider: Function,
- visibleSeries: Array,
- _attached: {
- type: Boolean,
- value: false
- },
-
- // Storing the update ID of the previous request for data enables us to determine if a
- // data response is outdated. We rely on an increasing ID instead of timestamp because
- // successive updates often fire within the same millisecond.
- _dataUpdateIdOfLastRequest: Number,
- _nextAvailableDataUpdateId: {
- type: Number,
- value: 1,
- },
- },
- observers: [
- "reload(tag, dataProvider)",
- "_changeSeries(visibleSeries.*)"
- ],
- ready: function() {
- this.fire('ready');
- },
- attached: function() {
- this._attached = true;
- this._changeSeries();
- },
- detached: function() {
- this._attached = false;
- },
- reload: function() {
- if (!this._attached) {
- return;
- }
- else if (!this.dataProvider) {
- throw new Error('tf-chart-scaffold requires a dataProvider.');
- }
- else if (!this.tag) {
- throw new Error('tf-chart-scaffold requires a tag.');
- }
-
- // TODO(chizeng): At this point, notify effective children that the previous data has been
- // invalidated. For instance, the image dashboard may want to clear its images. Today, the
- // chart scaffold only informs children when the new image URLs response finishes loading.
-
- const dataUpdateId = this._nextAvailableDataUpdateId++;
- this._dataUpdateIdOfLastRequest = dataUpdateId;
-
- this.visibleSeries.forEach(function(name) {
- this.dataProvider(this.tag, name).then(function(data) {
- if (dataUpdateId != this._dataUpdateIdOfLastRequest) {
- // This response is outdated. Ignore it.
- // TODO(chizeng): Explore canceling an outdated request before we even receive its
- // response. This involves creating hooks into the request manager and might introduce
- // some complexity that may not be worth it; Tensorboard frankly does not seem
- // bottlenecked by the network (It is often run in fast corp networks or locally.).
- return;
- }
- this.chart().setSeriesData(name, data);
- }.bind(this));
- }.bind(this));
- },
- _changeSeries: function() {
- if (!this._attached) {
- return;
- }
- else if (!this.visibleSeries) {
- throw new Error('tf-chart-scaffold requires a visibleSeries.');
- }
-
- this.chart().setVisibleSeries(this.visibleSeries);
- this.reload();
- },
- chart: function() {
- var children = this.getEffectiveChildren();
- if (!children.length) {
- throw new Error('tf-chart-scaffold has no children');
- }
-
- var child = children[0];
- if (!child.setVisibleSeries || !child.setSeriesData) {
- throw new Error("tf-chart-scaffold's content doesn't implement the " +
- "required interface");
- }
- return child;
- }
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-panes-helper" assetpath="../tf-dashboard-common/">
- <template>
- <content></content>
- <tf-no-data-warning data-type="[[dataType]]" show-warning="[[dataNotFound]]"></tf-no-data-warning>
-
- <template is="dom-repeat" items="[[categories]]" as="category">
- <tf-collapsable-pane name="[[category.name]]" count="[[_count(category.tags, selectedRuns.*)]]">
- <div class="layout horizontal wrap">
- <template is="dom-repeat" items="[[_categoryCards(category, selectedRuns.*, run2tag.*)]]">
- <div class="card">
- <div class="card-title-container" style="border-color: [[_titleBorderColor(item.run)]]">
- <div class="card-title" inner-h-t-m-l="[[_break(item.tag)]]"></div>
- <template is="dom-if" if="[[repeatForRuns]]">
- <div class="card-subtitle" title="[[item.run]]">[[item.run]]</div>
- </template>
- </div>
- <div class="card-content">
- <tf-chart-scaffold tag="[[item.tag]]" data-provider="[[dataProvider]]" visible-series="[[item.runs]]" on-ready="_instantiateTemplate">
-
- </tf-chart-scaffold>
- </div>
- <div class="card-bottom-row">
- <paper-icon-button class="expand-button" icon="fullscreen" on-tap="_toggleExpanded"></paper-icon-button>
- <template is="dom-if" if="[[showDownloadLinks]]">
- <tf-downloader runs="[[item.runs]]" tag="[[item.tag]]" url-fn="[[downloadLinkUrlFunction]]">
- </tf-downloader>
- </template>
- </div>
- </div>
- </template>
- </div>
- </tf-collapsable-pane>
- </template>
-
- <style>
- .card {
- height: var(--card-height, 200px);
- width: var(--card-width, 300px);
- display: flex;
- flex-direction: column;
- margin: 5px;
- padding: var(--card-padding, 0 30px 35px 0);
- -webkit-user-select: none;
- -moz-user-select: none;
- position: relative;
- }
-
- .card-expanded {
- height: var(--card-expanded-height, 400px);
- width: var(--card-expanded-width, 100%);
- }
-
- .card-title, .card-subtitle {
- flex-grow: 0;
- flex-shrink: 0;
- font-size: 14px;
- text-overflow: ellipsis;
- overflow: hidden;
- }
-
- .card-subtitle {
- font-size: 12px;
- }
-
- .card-content {
- flex-grow: 1;
- flex-shrink: 1;
- display: flex;
- margin-top: 10px;
- }
-
- .card-bottom-row {
- position: absolute;
- left: 0px;
- bottom: 0px;
- width: 100%;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- pointer-events: none;
- }
-
- .card-title-container {
- border-left: 4px solid;
- padding-left: 5px;
- }
-
- .expand-button {
- color: #2196F3;
- width: 32px;
- height: 32px;
- padding: 4px;
- border-radius: 100%;
- pointer-events: auto;
- display: var(--show-expand-button, block);
- }
-
- .card-expanded .expand-button {
- background: var(--tb-ui-light-accent);
- }
-
- tf-downloader {
- margin-right: 30px;
- pointer-events: auto;
- }
-
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-panes-helper",
- properties: {
- /**
- * Categories that separate the template instances. Each category will
- * be given its own collapsible pane. The category must be an array of
- * objects, each with a 'name' property and a 'tags' array of strings.
- */
- categories: Array,
-
- /**
- * Input of the colors that are used for the user's runs.
- */
- colorScale: Object,
-
- /**
- * The name of the data type that is used by this dashboard. This will
- * be used to display what is missing when there is no data available.
- */
- dataType: String,
-
- /**
- * The function that requests and returns a promise with the data of the
- * required type for the templates from the backend.
- */
- dataProvider: Object,
-
- /**
- * If false, instantiates one template for each tag and calls
- * setVisibleSeries on the first element of the template with all valid
- * runs the tag has. If true, instantiates one template for each run of
- * each tag, and calls setVisibleSeries of the first element of the
- * instantiated template with just the one run.
- */
- repeatForRuns: {
- type: Boolean,
- value: false
- },
-
- /**
- * Map from runs to the valid tags that have them.
- */
- run2tag: Object,
-
- /**
- * Array with the runs that are selected by the user (i.e. valid to be
- * displayed).
- */
- selectedRuns: Array,
-
- /**
- * If true, shows a menu with download links for the template data.
- * If this is set to true, urlFn must also be provided.
- */
- showDownloadLinks: Boolean,
-
- /**
- * Function that returns the route to get data to download. Must be
- * provided if showDownloadLinks is enabled.
- */
- downloadLinkUrlFunction: Function,
- _contentTemplate: {
- type: Object,
- value: null
- },
- _stampedTemplates: {
- type: Array,
- value: function() { return [] }
- }
- },
- behaviors: [
- Polymer.Templatizer,
- ],
-
- /**
- * Initializes the Polymer.Templatizer behavior with the template supplied
- * by the user. With this, all calls to this.stamp() will produce an
- * instance of the user template.
- */
- _initTemplatizer: function() {
- if (!this._contentTemplate) {
- // First template is used as the content.
- this._contentTemplate = Polymer.dom(this).querySelector('template');
- this.templatize(this._contentTemplate);
- }
- },
-
- /**
- * Called every time a tf-chart-scaffold is ready, stamps the user
- * template inside the scaffold element (before it is attached) and
- * stores the stamped template in an array to use for data binding
- * (forwardParentProp/Path).
- */
- _instantiateTemplate: function(e) {
- var scaffold = e.target;
- this._initTemplatizer();
- var instance = this.stamp();
- this._stampedTemplates.push(instance);
- Polymer.dom(scaffold).appendChild(instance.root);
- },
- _toggleExpanded: function(e) {
- var currentTarget = Polymer.dom(e.currentTarget);
- var card = currentTarget.node.closest('.card');
- var scaffold = card.querySelector('tf-chart-scaffold');
- card.classList.toggle('card-expanded');
- scaffold.chart().redraw();
- },
- _count: function(tags) {
- if (!this.repeatForRuns) {
- return tags.length;
- }
-
- var targetTags = d3.set(tags);
- var count = 0;
- this.selectedRuns.forEach(function(r) {
- this.run2tag[r].forEach(function(t) {
- if (targetTags.has(t)) {
- count++;
- }
- });
- }.bind(this));
- return count;
- },
- _categoryCards: function(category) {
- var cards = [];
- category.tags.forEach(function(tag) {
- var runs = this.selectedRuns.filter(function(r) {
- return this.run2tag[r] && this.run2tag[r].indexOf(tag) !== -1;
- }.bind(this));
-
- if (this.repeatForRuns) {
- runs.forEach(function(run) {
- cards.push({tag: tag, run: run, runs: [run]});
- });
- } else {
- cards.push({tag: tag, runs: runs});
- }
- }.bind(this));
-
- return cards;
- },
- _titleBorderColor: function(run) {
- return this.repeatForRuns ? this.colorScale.scale(run) : 'white';
- },
-
- /*
- * Polymer data binding forwarding functions. Check the
- * Polymer.Templatizer documentation for more information.
- */
-
- _forwardParentProp: function(property, value) {
- this._stampedTemplates.forEach(function(instance) {
- instance[property] = value;
- });
- },
- _forwardParentPath: function(path, value) {
- this._stampedTemplates.forEach(function(instance) {
- instance.notifyPath(path, value, true);
- });
- },
- // TODO(renatoutsch): implement the instance forwarding for two-way data
- // binding.
- // Add breaks to input so it will wrap nicely
- _break: function(ipt) {
- return ipt.replace(/([\/_-])/g, "$1<wbr>")
- },
- });
- </script>
-</dom-module>
-<dom-module id="tf-storage" assetpath="../tf-storage/">
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-/* tslint:disable:no-namespace variable-name */
-/**
- * The Storage Module provides storage for URL parameters, and an API for
- * getting and setting TensorBoard's stateful URI.
- *
- * It generates URI components like: events&runPrefix=train*
- * which TensorBoard uses after like localhost:8000/#events&runPrefix=train*
- * to store state in the URI.
- *
- * It also allows saving the values to localStorage for long-term persistance.
- */
-var TF;
-(function (TF) {
- var URIStorage;
- (function (URIStorage) {
- /**
- * A key that users cannot use, since TensorBoard uses this to store info
- * about the active tab.
- */
- URIStorage.TAB = '__tab__';
- /**
- * The name of the property for users to set on a Polymer component
- * in order for its stored properties to be stored in the URI unambiguously.
- * (No need to set this if you want mutliple instances of the component to
- * share URI state)
- *
- * Example:
- * <my-component disambiguator="0"></my-component>
- *
- * The disambiguator should be set to any unique value so that multiple
- * instances of the component can store properties in URI storage.
- *
- * Because it's hard to dereference this variable in HTML property bindings,
- * it is NOT safe to change the disambiguator string without find+replace
- * across the codebase.
- */
- URIStorage.DISAMBIGUATOR = 'disambiguator';
- /**
- * Return a string stored in URI or localStorage.
- * Undefined if not found.
- */
- function getString(key, useLocalStorage) {
- if (useLocalStorage) {
- return window.localStorage.getItem(key);
- }
- else {
- return _componentToDict(_readComponent())[key];
- }
- }
- URIStorage.getString = getString;
- /**
- * Set a string in URI or localStorage.
- */
- function setString(key, value, useLocalStorage) {
- if (useLocalStorage) {
- window.localStorage.setItem(key, value);
- }
- else {
- var items = _componentToDict(_readComponent());
- items[key] = value;
- _writeComponent(_dictToComponent(items));
- }
- }
- URIStorage.setString = setString;
- /**
- * Return a boolean stored in stored in URI or localStorage.
- * Undefined if not found.
- */
- function getBoolean(key, useLocalStorage) {
- var item = getString(key, useLocalStorage);
- return item === 'true' ? true : item === 'false' ? false : undefined;
- }
- URIStorage.getBoolean = getBoolean;
- /**
- * Store a boolean in URI or localStorage.
- */
- function setBoolean(key, value, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- setString(key, value.toString(), useLocalStorage);
- }
- URIStorage.setBoolean = setBoolean;
- /**
- * Return a number stored in stored in URI or localStorage.
- * Undefined if not found.
- */
- function getNumber(key, useLocalStorage) {
- var item = getString(key, useLocalStorage);
- return item === undefined ? undefined : +item;
- }
- URIStorage.getNumber = getNumber;
- /**
- * Store a number in URI or localStorage.
- */
- function setNumber(key, value, useLocalStorage) {
- setString(key, '' + value, useLocalStorage);
- }
- URIStorage.setNumber = setNumber;
- /**
- * Return an object stored in stored in URI or localStorage.
- * Undefined if not found.
- */
- function getObject(key, useLocalStorage) {
- var item = getString(key, useLocalStorage);
- return item === undefined ? undefined : JSON.parse(atob(item));
- }
- URIStorage.getObject = getObject;
- /**
- * Store an object in URI or localStorage.
- */
- function setObject(key, value, useLocalStorage) {
- setString(key, btoa(JSON.stringify(value)), useLocalStorage);
- }
- URIStorage.setObject = setObject;
- /**
- * Get a unique storage name for a (Polymer component, propertyName) tuple.
- *
- * DISAMBIGUATOR must be set on the component, if other components use the
- * same propertyName.
- */
- function getURIStorageName(component, propertyName) {
- var d = component[URIStorage.DISAMBIGUATOR];
- var components = d == null ? [propertyName] : [d, propertyName];
- return components.join('.');
- }
- URIStorage.getURIStorageName = getURIStorageName;
- /**
- * Return a function that:
- * (1) Initializes a Polymer boolean property with a default value, if its
- * value is not already set
- * (2) Sets up listener that updates Polymer property on hash change.
- */
- function getBooleanInitializer(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- return _getInitializer(getBoolean, propertyName, defaultVal, useLocalStorage);
- }
- URIStorage.getBooleanInitializer = getBooleanInitializer;
- /**
- * Return a function that:
- * (1) Initializes a Polymer string property with a default value, if its
- * value is not already set
- * (2) Sets up listener that updates Polymer property on hash change.
- */
- function getStringInitializer(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- return _getInitializer(getString, propertyName, defaultVal, useLocalStorage);
- }
- URIStorage.getStringInitializer = getStringInitializer;
- /**
- * Return a function that:
- * (1) Initializes a Polymer number property with a default value, if its
- * value is not already set
- * (2) Sets up listener that updates Polymer property on hash change.
- */
- function getNumberInitializer(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- return _getInitializer(getNumber, propertyName, defaultVal, useLocalStorage);
- }
- URIStorage.getNumberInitializer = getNumberInitializer;
- /**
- * Return a function that:
- * (1) Initializes a Polymer Object property with a default value, if its
- * value is not already set
- * (2) Sets up listener that updates Polymer property on hash change.
- *
- * Generates a deep clone of the defaultVal to avoid mutation issues.
- */
- function getObjectInitializer(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- return _getInitializer(getObject, propertyName, defaultVal, useLocalStorage);
- }
- URIStorage.getObjectInitializer = getObjectInitializer;
- /**
- * Return a function that updates URIStorage when a string property changes.
- */
- function getBooleanObserver(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- return _getObserver(getBoolean, setBoolean, propertyName, defaultVal, useLocalStorage);
- }
- URIStorage.getBooleanObserver = getBooleanObserver;
- /**
- * Return a function that updates URIStorage when a string property changes.
- */
- function getStringObserver(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- return _getObserver(getString, setString, propertyName, defaultVal, useLocalStorage);
- }
- URIStorage.getStringObserver = getStringObserver;
- /**
- * Return a function that updates URIStorage when a number property changes.
- */
- function getNumberObserver(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- return _getObserver(getNumber, setNumber, propertyName, defaultVal, useLocalStorage);
- }
- URIStorage.getNumberObserver = getNumberObserver;
- /**
- * Return a function that updates URIStorage when an object property changes.
- * Generates a deep clone of the defaultVal to avoid mutation issues.
- */
- function getObjectObserver(propertyName, defaultVal, useLocalStorage) {
- if (useLocalStorage === void 0) { useLocalStorage = false; }
- var clone = _.cloneDeep(defaultVal);
- return _getObserver(getObject, setObject, propertyName, clone, useLocalStorage);
- }
- URIStorage.getObjectObserver = getObjectObserver;
- /**
- * Read component from URI (e.g. returns "events&runPrefix=train*").
- */
- function _readComponent() {
- return TF.Globals.USE_HASH ? window.location.hash.slice(1) :
- TF.Globals.FAKE_HASH;
- }
- /**
- * Write component to URI.
- */
- function _writeComponent(component) {
- if (TF.Globals.USE_HASH) {
- window.location.hash = component;
- }
- else {
- TF.Globals.FAKE_HASH = component;
- }
- }
- /**
- * Convert dictionary of strings into a URI Component.
- * All key value entries get added as key value pairs in the component,
- * with the exception of a key with the TAB value, which if present
- * gets prepended to the URI Component string for backwards comptability
- * reasons.
- */
- function _dictToComponent(items) {
- var component = '';
- // Add the tab name e.g. 'events', 'images', 'histograms' as a prefix
- // for backwards compatbility.
- if (items[URIStorage.TAB] !== undefined) {
- component += items[URIStorage.TAB];
- }
- // Join other strings with &key=value notation
- var nonTab = _.pairs(items)
- .filter(function (pair) { return pair[0] !== URIStorage.TAB; })
- .map(function (pair) {
- return encodeURIComponent(pair[0]) + '=' +
- encodeURIComponent(pair[1]);
- })
- .join('&');
- return nonTab.length > 0 ? (component + '&' + nonTab) : component;
- }
- /**
- * Convert a URI Component into a dictionary of strings.
- * Component should consist of key-value pairs joined by a delimiter
- * with the exception of the tabName.
- * Returns dict consisting of all key-value pairs and
- * dict[TAB] = tabName
- */
- function _componentToDict(component) {
- var items = {};
- var tokens = component.split('&');
- tokens.forEach(function (token) {
- var kv = token.split('=');
- // Special backwards compatibility for URI components like #events
- if (kv.length === 1 && _.contains(TF.Globals.TABS, kv[0])) {
- items[URIStorage.TAB] = kv[0];
- }
- else if (kv.length === 2) {
- items[decodeURIComponent(kv[0])] = decodeURIComponent(kv[1]);
- }
- });
- return items;
- }
- /**
- * Return a function that:
- * (1) Initializes a Polymer property with a default value, if its
- * value is not already set
- * (2) Sets up listener that updates Polymer property on hash change.
- */
- function _getInitializer(get, propertyName, defaultVal, useLocalStorage) {
- return function () {
- var _this = this;
- var URIStorageName = getURIStorageName(this, propertyName);
- // setComponentValue will be called every time the hash changes, and is
- // responsible for ensuring that new state in the hash will be propagated
- // to the component with that property.
- // It is important that this function does not re-assign needlessly,
- // to avoid Polymer observer churn.
- var setComponentValue = function () {
- var uriValue = get(URIStorageName, false);
- var currentValue = _this[propertyName];
- // if uriValue is undefined, we will ensure that the property has the
- // default value
- if (uriValue === undefined) {
- var valueToSet = void 0;
- // if we are using localStorage, we will set the value to the value
- // from localStorage. Then, the corresponding observer will proxy
- // the localStorage value into URI storage.
- // in this way, localStorage takes precedence over the default val
- // but not over the URI value.
- if (useLocalStorage) {
- var useLocalStorageValue = get(URIStorageName, true);
- valueToSet = useLocalStorageValue === undefined ?
- defaultVal :
- useLocalStorageValue;
- }
- else {
- valueToSet = defaultVal;
- }
- if (!_.isEqual(currentValue, valueToSet)) {
- // If we don't have an explicit URI value, then we need to ensure
- // the property value is equal to the default value.
- // We will assign a clone rather than the canonical default, because
- // the component receiving this property may mutate it, and we need
- // to keep a pristine copy of the default.
- _this[propertyName] = _.clone(valueToSet);
- }
- // In this case, we have an explicit URI value, so we will ensure that
- // the component has an equivalent value.
- }
- else {
- if (!_.isEqual(uriValue, currentValue)) {
- _this[propertyName] = uriValue;
- }
- }
- };
- // Set the value on the property.
- setComponentValue();
- // Update it when the hashchanges.
- window.addEventListener('hashchange', setComponentValue);
- };
- }
- /**
- * Return a function that updates URIStorage when a property changes.
- */
- function _getObserver(get, set, propertyName, defaultVal, useLocalStorage) {
- return function () {
- var URIStorageName = getURIStorageName(this, propertyName);
- var newVal = this[propertyName];
- // if this is a localStorage property, we always synchronize the value
- // in localStorage to match the one currently in the URI.
- if (useLocalStorage) {
- set(URIStorageName, newVal, true);
- }
- if (!_.isEqual(newVal, get(URIStorageName, false))) {
- if (_.isEqual(newVal, defaultVal)) {
- _unsetFromURI(URIStorageName);
- }
- else {
- set(URIStorageName, newVal, false);
- }
- }
- };
- }
- /**
- * Delete a key from the URI.
- */
- function _unsetFromURI(key) {
- var items = _componentToDict(_readComponent());
- delete items[key];
- _writeComponent(_dictToComponent(items));
- }
- })(URIStorage = TF.URIStorage || (TF.URIStorage = {}));
-})(TF || (TF = {}));
-</script>
-</dom-module>
-
-<dom-module id="tf-regex-group" assetpath="../tf-dashboard-common/">
- <template>
- <div class="regex-list">
- <template is="dom-repeat" items="{{rawRegexes}}">
- <div class="regex-line">
- <paper-input id="text-input" class="regex-input" label="Write a regex to create a tag group" no-label-float="" value="{{item.regex}}" invalid="[[!item.valid]]" on-keyup="moveFocus"></paper-input>
- <paper-icon-button icon="close" class="delete-button" aria-label="Delete Regex" tabindex="0" on-tap="deleteRegex"></paper-icon-button>
- </div>
- <style>
- .regex-input {
- width: 250px;
- display: inline-block;
- margin-left: -3px;
- }
-
- .delete-button {
- color: var(--paper-grey-700);
- width: 40px;
- height: 40px;
- margin-right: -10px;
- }
-
- .regex-list {
- margin-bottom: 10px;
- }
-
- paper-input {
- --paper-input-container-focus-color: var(--tb-orange-strong);
- --paper-input-container-input: {
- font-size: 14px;
- };
- --paper-input-container-label: {
- font-size: 14px;
- };
- }
- </style>
- </template>
- </div>
- </template>
- <script>
- Polymer({
- is: "tf-regex-group",
- properties: {
- rawRegexes: {
- type: Array,
- value: TF.URIStorage.getObjectInitializer('rawRegexes', [{regex: "", valid: true}]),
- },
- regexes: {type: Array, computed: "usableRegexes(rawRegexes.*)", notify: true},
- },
- observers: [
- "addNewRegexIfNeeded(rawRegexes.*)",
- "checkValidity(rawRegexes.*)",
- "_uriStoreRegexes(rawRegexes.*)",
- ],
- _uriStoreRegexes: TF.URIStorage.getObjectObserver('rawRegexes', [{regex: "", valid: true}]),
- checkValidity: function(x) {
- var match = x.path.match(/rawRegexes\.(\d+)\.regex/);
- if (match) {
- var idx = match[1];
- this.set("rawRegexes." + idx + ".valid", this.isValid(x.value));
- }
- },
- isValid: function(s) {
- try {
- new RegExp(s);
- return true;
- } catch (e) {
- return false;
- }
- },
- usableRegexes: function(regexes) {
- var isValid = this.isValid;
- return regexes.base.filter(function (r) {
- // Checking validity here (rather than using the data property)
- // is necessary because otherwise we might send invalid regexes due
- // to the fact that this function can call before the observer does
- return r.regex !== "" && isValid(r.regex);
- }).map(function(r) {
- return r.regex;
- });
- },
- addNewRegexIfNeeded: function() {
- var last = this.rawRegexes[this.rawRegexes.length - 1];
- if (last.regex !== "") {
- this.push("rawRegexes", {regex: "", valid: true});
- }
- },
- deleteRegex: function(e) {
- if (this.rawRegexes.length > 1) {
- this.splice("rawRegexes", e.model.index, 1);
- }
- },
- moveFocus: function(e) {
- if (e.keyCode === 13) {
- var idx = e.model.index;
- var inputs = Polymer.dom(this.root).querySelectorAll(".regex-input");
- if (idx < this.rawRegexes.length - 1) {
- inputs[idx+1].$.input.focus();
- } else {
- document.activeElement.blur();
- }
- }
- }
- });
- </script>
-</dom-module>
-<link rel="import" href="../paper-toggle-button/paper-toggle-button.html">
-
-<dom-module id="tf-categorizer" assetpath="../tf-dashboard-common/">
- <template>
- <div class="inputs">
- <tf-regex-group id="regexGroup" regexes="{{regexes}}"></tf-regex-group>
- </div>
- <style>
- :host {
- display: block;
- padding-bottom: 5px;
- }
- paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-grey-600);
- --paper-checkbox-unchecked-color: var(--paper-grey-600);
- font-size: 14px;
- }
- </style>
- </template>
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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) {
- /**
- * This module contains methods that allow sorting tags into 'categories'.
- * A category contains a name and a list of tags.
- * The sorting strategy is defined by a 'CustomCategorization', which contains
- * 'categoryDefinitions' which are regex rules used to construct a category.
- * E.g. the regex rule 'xent' will create a category called 'xent' that
- * contains values whose tags match the regex.
- *
- * After custom categories are evaluated, the tags are sorted by a hardcoded
- * fallback categorizer, which may, for example, group tags into categories
- * based on their top namespace.
- */
- /* Canonical TensorFlow ops are namespaced using forward slashes.
- * This fallback categorizer categorizes by the top-level namespace.
- */
- Categorizer.topLevelNamespaceCategorizer = splitCategorizer(/\//);
- function fallbackCategorizer(s) {
- switch (s) {
- case 'TopLevelNamespaceCategorizer':
- return Categorizer.topLevelNamespaceCategorizer;
- default:
- throw new Error('Unrecognized categorization strategy: ' + s);
- }
- }
- Categorizer.fallbackCategorizer = fallbackCategorizer;
- /* An 'extractor' is a function that takes a tag name, and 'extracts' a
- * category name.
- * This function takes an extractor, and produces a categorizer.
- * Currently, it is just used for the fallbackCategorizer, but we may want to
- * refactor the general categorization logic to use the concept of extractors.
- */
- function extractorToCategorizer(extractor) {
- return function (tags) {
- if (tags.length === 0) {
- return [];
- }
- var sortedTags = tags.slice().sort(VZ.Sorting.compareTagNames);
- var categories = [];
- var currentCategory = {
- name: extractor(sortedTags[0]),
- tags: [],
- };
- sortedTags.forEach(function (t) {
- var topLevel = extractor(t);
- if (currentCategory.name !== topLevel) {
- categories.push(currentCategory);
- currentCategory = {
- name: topLevel,
- tags: [],
- };
- }
- currentCategory.tags.push(t);
- });
- categories.push(currentCategory);
- return categories;
- };
- }
- function splitCategorizer(r) {
- var extractor = function (t) {
- return t.split(r)[0];
- };
- return extractorToCategorizer(extractor);
- }
- function defineCategory(ruledef) {
- var r = new RegExp(ruledef);
- var f = function (tag) {
- return r.test(tag);
- };
- return { name: ruledef, matches: f };
- }
- Categorizer.defineCategory = defineCategory;
- function _categorizer(rules, fallback) {
- return function (tags) {
- var remaining = d3.set(tags);
- var userSpecified = rules.map(function (def) {
- var tags = [];
- remaining.forEach(function (t) {
- if (def.matches(t)) {
- tags.push(t);
- }
- });
- var cat = { name: def.name, tags: tags.sort(VZ.Sorting.compareTagNames) };
- return cat;
- });
- var defaultCategories = fallback(remaining.values());
- return userSpecified.concat(defaultCategories);
- };
- }
- Categorizer._categorizer = _categorizer;
- function categorizer(s) {
- var rules = s.categoryDefinitions.map(defineCategory);
- var fallback = fallbackCategorizer(s.fallbackCategorizer);
- return _categorizer(rules, fallback);
- }
- Categorizer.categorizer = categorizer;
- ;
-})(Categorizer || (Categorizer = {}));
-</script>
- <script>
- Polymer({
- is: "tf-categorizer",
- properties: {
- regexes: {type: Array},
- tags: {type: Array},
- categoriesAreExclusive: {type: Boolean, value: true},
- fallbackCategorizer: {
- type: String,
- value: "TopLevelNamespaceCategorizer",
- },
- categorizer: {
- type: Object,
- computed: "computeCategorization(regexes.*, categoriesAreExclusive, fallbackCategorizer)",
- },
- categories: {type: Array, value: function() {return [];}, notify: true, readOnly: true},
- },
- observers: ['recategorize(tags.*, categorizer)'],
- computeCategorization: function(regexes, categoriesAreExclusive, fallbackCategorizer) {
- var categorizationStrategy = {
- categoryDefinitions: regexes.base,
- categoriesAreExclusive: categoriesAreExclusive,
- fallbackCategorizer: fallbackCategorizer,
- };
- return Categorizer.categorizer(categorizationStrategy);
- },
- recategorize: function() {
- this.debounce("tf-categorizer-recategorize", function (){
- var categories = this.categorizer(this.tags);
- this._setCategories(categories);
- })
- },
- });
- </script>
-</dom-module>
-<dom-module id="run-color-style" assetpath="../tf-dashboard-common/">
- <template>
- <style>
- [color-class="light-blue"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-light-blue-500);
- --paper-checkbox-checked-ink-color: var(--paper-light-blue-500);
- --paper-checkbox-unchecked-color: var(--paper-light-blue-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-light-blue-900);
- }
- [color-class="red"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-red-500);
- --paper-checkbox-checked-ink-color: var(--paper-red-500);
- --paper-checkbox-unchecked-color: var(--paper-red-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-red-900);
- }
- [color-class="green"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-green-500);
- --paper-checkbox-checked-ink-color: var(--paper-green-500);
- --paper-checkbox-unchecked-color: var(--paper-green-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-green-900);
- }
- [color-class="purple"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-purple-500);
- --paper-checkbox-checked-ink-color: var(--paper-purple-500);
- --paper-checkbox-unchecked-color: var(--paper-purple-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-purple-900);
- }
- [color-class="teal"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-teal-500);
- --paper-checkbox-checked-ink-color: var(--paper-teal-500);
- --paper-checkbox-unchecked-color: var(--paper-teal-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-teal-900);
- }
- [color-class="pink"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-pink-500);
- --paper-checkbox-checked-ink-color: var(--paper-pink-500);
- --paper-checkbox-unchecked-color: var(--paper-pink-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-pink-900);
- }
- [color-class="orange"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-orange-500);
- --paper-checkbox-checked-ink-color: var(--paper-orange-500);
- --paper-checkbox-unchecked-color: var(--paper-orange-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-orange-900);
- }
- [color-class="brown"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-brown-500);
- --paper-checkbox-checked-ink-color: var(--paper-brown-500);
- --paper-checkbox-unchecked-color: var(--paper-brown-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-brown-900);
- }
- [color-class="indigo"] paper-checkbox {
- --paper-checkbox-checked-color: var(--paper-indigo-500);
- --paper-checkbox-checked-ink-color: var(--paper-indigo-500);
- --paper-checkbox-unchecked-color: var(--paper-indigo-900);
- --paper-checkbox-unchecked-ink-color: var(--paper-indigo-900);
- }
- </style>
- </template>
-</dom-module>
-
-<dom-module id="tf-multi-checkbox" assetpath="../tf-dashboard-common/">
- <style include="scrollbar-style"></style>
- <style include="run-color-style"></style>
-
- <template>
- <paper-input id="runs-regex" no-label-float="" label="Write a regex to filter runs" value="[[regexInput]]" on-bind-value-changed="_debouncedRegexChange"></paper-input>
- <div id="outer-container" class="scrollbar">
- <template is="dom-repeat" items="[[namesMatchingRegex]]">
- <div class="run-row">
- <div class="icon-container checkbox-container vertical-align-container">
- <paper-checkbox class="checkbox vertical-align-center" name="[[item]]" checked$="[[_isChecked(item, runSelectionState.*)]]" on-change="_checkboxChange"></paper-checkbox>
-
- </div>
- <div class="icon-container isolator-container vertical-align-container">
- <paper-icon-button icon="radio-button-unchecked" class="isolator vertical-align-center" on-tap="_isolateRun" name="[[item]]"></paper-icon-button>
- </div>
- <div class="item-label-container">
- <span>[[item]]</span>
- </div>
- </div>
- </template>
- </div>
- <style>
- paper-input {
- --paper-input-container-focus-color: var(--tb-orange-strong);
- --paper-input-container-input: {
- font-size: 14px;
- };
- --paper-input-container-label: {
- font-size: 14px;
- };
- }
- :host {
- display: flex;
- flex-direction: column;
- height: 100%;
- }
- #outer-container {
- overflow-y: auto;
- overflow-x: hidden;
- width: 100%;
- height: 0; /* Quirk to make firefox add scrolling instead of expand div */
- flex-grow: 1;
- flex-shrink: 1;
- word-wrap: break-word;
- }
- .run-row {
- padding-top: 5px;
- padding-bottom: 5px;
- display: flex;
- flex-direction: row;
- font-size: 13px;
- }
- .icon-container {
- flex-grow: 0;
- flex-shrink: 0;
- padding-left: 2px;
- }
- .checkbox {
- padding-left: 2px;
- width: 18px;
- height: 18px;
- }
- .isolator {
- width: 18px;
- height: 18px;
- padding: 0px;
- }
- .isolator-container {
- padding-left: 6px;
- padding-right: 3px;
- }
- .checkbox-container {
- padding-left: 2px;
- }
- .item-label-container {
- padding-left: 5px;
- flex-grow: 1;
- flex-shrink: 1;
- width: 0px; /* hack to get the flex-grow to work properly */
- }
- .tooltip-value-container {
- display: flex;
- justify-content: center;
- flex-grow: 0;
- flex-shrink: 0;
- text-align:right;
- padding-left: 2px;
- }
- .vertical-align-container {
- display: flex;
- justify-content: center;
- }
- .vertical-align-container .vertical-align-center {
- align-self: center;
- }
- .vertical-align-container .vertical-align-top {
- align-self: start;
- }
- </style>
- </template>
-
- <script>
- Polymer({
- is: "tf-multi-checkbox",
- properties: {
- names: {
- type: Array,
- value: function() {return [];},
- }, // All the runs in consideration
- regexInput: {
- type: String,
- value: TF.URIStorage.getStringInitializer("regexInput", ""),
- observer: "_regexInputObserver",
- }, // Regex for filtering the runs
- regex: {
- type: Object,
- computed: "_makeRegex(regexInput)"
- },
- namesMatchingRegex: {
- type: Array,
- computed: "computeNamesMatchingRegex(names.*, regex)"
- }, // Runs that match the regex
- runSelectionState: {
- // if a run is explicitly enabled, True, if explicitly disabled, False.
- // if undefined, default value (enable for first k runs, disable after).
- type: Object,
- value: TF.URIStorage.getObjectInitializer('runSelectionState', {}),
- observer: "_storeRunToIsCheckedMapping",
- },
- // (Allows state to persist across regex filtering)
- outSelected: {
- type: Array,
- notify: true,
- computed: 'computeOutSelected(namesMatchingRegex.*, runSelectionState.*)'
- },
- colorScale: {
- type: Object,
- observer: "synchronizeColors",
- }, // map from run name to css class
- maxRunsToEnableByDefault: {
- // When TB first loads, if it has k or fewer runs, they are all enabled
- // by default. If there are more, then they are all disabled.
- type: Number,
- value: 40,
- },
- _debouncedRegexChange: {
- type: Function,
- // Updating the regex can be slow, because it involves updating styles
- // on a large number of Polymer paper-checkboxes. We don't want to do
- // this while the user is typing, as it may make a bad, laggy UI.
- // So we debounce the updates that come from user typing.
- value: function() {
- _this = this;
- var debounced = _.debounce(function(r) {
- _this.regexInput = r;
- }, 150, {leading: false});
- return function() {
- var r = this.$$("#runs-regex").value;
- if (r == "") {
- // If the user cleared the field, they may be done typing, so
- // update more quickly.
- this.async(function() {
- _this.regexInput = r;
- }, 30);
- } else {
- debounced(r);
- };
- };
- },
- },
- },
- listeners: {
- 'dom-change': 'synchronizeColors',
- },
- observers: [
- "_setIsolatorIcon(runSelectionState, names)",
- ],
- _storeRunToIsCheckedMapping: TF.URIStorage.getObjectObserver('runSelectionState', {}),
- _makeRegex: function(regex) {
- try {
- return new RegExp(regex)
- } catch (e) {
- return null;
- }
- },
- _setIsolatorIcon: function() {
- var runMap = this.runSelectionState;
- var numChecked = _.filter(_.values(runMap)).length;
- var buttons = Array.prototype.slice.call(this.querySelectorAll(".isolator"));
-
- buttons.forEach(function(b) {
- if (numChecked === 1 && runMap[b.name]) {
- b.icon = "radio-button-checked";
- } else {
- b.icon = "radio-button-unchecked";
- }
- });
- },
- computeNamesMatchingRegex: function(__, ___) {
- var regex = this.regex;
- return this.names.filter(function(n) {
- return regex == null || regex.test(n);
- });
- },
- computeOutSelected: function(__, ___) {
- var runSelectionState = this.runSelectionState;
- var num = this.maxRunsToEnableByDefault;
- var allEnabled = this.namesMatchingRegex.length <= num;
- return this.namesMatchingRegex.filter(function(n, i) {
- return runSelectionState[n] == null ? allEnabled : runSelectionState[n];
- });
- },
- synchronizeColors: function(e) {
- if (!this.colorScale) return;
-
- this._setIsolatorIcon();
-
- var checkboxes = Array.prototype.slice.call(this.querySelectorAll("paper-checkbox"));
- var scale = this.colorScale;
- checkboxes.forEach(function(p) {
- var color = scale.scale(p.name);
- p.customStyle['--paper-checkbox-checked-color'] = color;
- p.customStyle['--paper-checkbox-checked-ink-color'] = color;
- p.customStyle['--paper-checkbox-unchecked-color'] = color;
- p.customStyle['--paper-checkbox-unchecked-ink-color'] = color;
- });
- var buttons = Array.prototype.slice.call(this.querySelectorAll(".isolator"));
- buttons.forEach(function(p) {
- var color = scale.scale(p.name);
- p.style['color'] = color;
- });
- // The updateStyles call fails silently if the browser doesn't have focus,
- // e.g. if TensorBoard was opened into a new tab that isn't visible.
- // So we wait for requestAnimationFrame.
- var _this = this;
- window.requestAnimationFrame(function() {_this.updateStyles();});
- },
- _isolateRun: function(e) {
- // If user clicks on the label for one run, enable it and disable all other runs.
-
- var name = Polymer.dom(e).localTarget.name;
- var selectionState = {};
- this.names.forEach(function(n) {
- selectionState[n] = n == name;
- })
- this.runSelectionState = selectionState;
- },
- _checkboxChange: function(e) {
- var target = Polymer.dom(e).localTarget;
- this.runSelectionState[target.name] = target.checked;
- // n.b. notifyPath won't work because run names may have periods.
- this.runSelectionState = _.clone(this.runSelectionState);
- },
- _isChecked: function(item, outSelectedChange) {
- return this.outSelected.indexOf(item) != -1;
- },
- _regexInputObserver: TF.URIStorage.getStringObserver("regexInput", ""),
- toggleAll: function() {
- var _this = this;
- var anyToggledOn = this.namesMatchingRegex
- .some(function(n) {return _this.runSelectionState[n]});
-
-
- var runSelectionStateIsDefault = Object.keys(this.runSelectionState).length == 0;
-
- var defaultOff = this.namesMatchingRegex.length > this.maxRunsToEnableByDefault;
- // We have runs toggled either if some were explicitly toggled on, or if
- // we are in the default state, and there are few enough that we default
- // to toggling on.
- anyToggledOn = anyToggledOn || runSelectionStateIsDefault && !defaultOff;
-
- // If any are toggled on, we turn everything off. Or, if none are toggled
- // on, we turn everything on.
-
- var newRunsDisabled = {};
- this.names.forEach(function(n) {
- newRunsDisabled[n] = !anyToggledOn;
- })
- this.runSelectionState = newRunsDisabled;
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-run-selector" assetpath="../tf-dashboard-common/">
- <template>
- <paper-dialog with-backdrop="" id="logdir-dialog">
- <h2>logdir</h2>
- <div inner-h-t-m-l="{{_breakString(logdir)}}"></div>
- </paper-dialog>
- <div id="top-text">
- <h3 id="tooltip-help" class="tooltip-container">
- Runs
- </h3>
- </div>
- <tf-multi-checkbox id="multiCheckbox" names="[[runs]]" out-selected="{{outSelected}}" color-scale="[[colorScale]]"></tf-multi-checkbox>
- <paper-button class="x-button" id="toggle-all" on-tap="_toggleAll">
- Toggle All Runs
- </paper-button>
- <template is="dom-if" if="[[logdir]]">
- <div id="logdir">
- <span id="clipped-logdir" inner-h-t-m-l="[[_clippedLogdir]]"></span><template is="dom-if" if="[[_shouldShowExpandLogdirButton(logdir, _logdirClipLength)]]"><a href="" on-click="_openLogdirDialog">…</a>
- </template>
- </div>
- </template>
- <style>
- :host {
- display: flex;
- flex-direction: column;
- padding-bottom: 10px;
- box-sizing: border-box;
- }
- #top-text {
- width: 100%;
- flex-grow: 0;
- flex-shrink: 0;
- padding-right: 16px;
- box-sizing: border-box;
- color: var(--paper-grey-800);
- }
- tf-multi-checkbox {
- display: flex;
- flex-grow: 1;
- flex-shrink: 1;
- }
- .x-button {
- font-size: 13px;
- background-color: var(--tb-ui-light-accent);
- color: var(--tb-ui-dark-accent);
- }
- #tooltip-help {
- color: var(--paper-grey-800);
- margin: 0;
- font-weight: normal;
- font-size: 14px;
- margin-bottom: 5px;
- }
- paper-button {
- margin-left: 0;
- }
- #logdir {
- color: var(--tb-ui-dark-accent);
- font-size: 13px;
- margin: 5px 0 0 0;
- max-width: 288px;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-run-selector",
- properties: {
- backend: Object,
- outSelected: {type: Array, notify: true},
- // runs: an array of strings, representing the run names that may be chosen
- runs: Array,
- colorScale: Object, // TF.ColorScale
- logdir: {
- type: String,
- notify: true,
- },
- // This is the potentially clipped portion of the logdir we show at the bottom of the sidebar.
- _clippedLogdir: {
- type: String,
- },
- _logdirClipLength: {
- type: Number,
- value: 250,
- readOnly: true,
- },
- },
- observers: [
- "_onBackendUpdate(backend)",
- "_logdirSet(logdir)",
- ],
- _toggleAll: function() {
- this.$.multiCheckbox.toggleAll();
- },
- // Break the string at natural points, including commas, equals, and slashes
- _breakString: function(originalString) {
- return originalString.replace(/([\/=\-_,])/g, "$1<wbr>");
- },
- _onBackendUpdate: function(backend) {
- if (backend === undefined) {
- return;
- }
-
- // When the backend is set, the selector can request the logdir.
- backend.logdir().then(logdirObject => {
- this.set('logdir', logdirObject.logdir);
- }).catch(e => {
- // Fetching the logdir failed. Prevent the exception from logging to
- // console. The console already logs a 404 network event.
- });
- },
- _logdirSet: function(logdir) {
- if (logdir === undefined) {
- // The logdir has not been set yet.
- return;
- }
-
- var lineBrokenText;
- if (logdir.length > this._logdirClipLength) {
- // Clip the logdir to avoid blocking the runs selector. Let the user view a more full
- // version of the logdir.
- lineBrokenText = this._breakString(logdir.substring(0, this._logdirClipLength));
- } else {
- lineBrokenText = this._breakString(logdir);
- }
- this.set('_clippedLogdir', lineBrokenText);
- },
- _openLogdirDialog: function(event) {
- event.preventDefault();
- this.$$('#logdir-dialog').open();
- },
- _shouldShowExpandLogdirButton(logdir, _logdirClipLength) {
- return logdir && logdir.length > _logdirClipLength;
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-sidebar-helper" assetpath="../tf-dashboard-common/">
- <template>
- <div class="sidebar-section">
- <tf-categorizer id="categorizer" tags="[[tags]]" categories="{{categories}}"></tf-categorizer>
- <content select=".extend-first-section"></content>
- </div>
- <content></content>
- <div class="sidebar-section">
- <tf-run-selector id="runSelector" backend="[[backend]]" runs="[[runs]]" color-scale="[[colorScale]]" out-selected="{{selectedRuns}}"></tf-run-selector>
- </div>
- <style include="dashboard-style"></style>
- <style>
- :host {
- display: flex;
- flex-direction: column;
- height: 100%;
- }
-
- #categorizer {
- flex-shrink: 0;
- }
-
- #runSelector {
- flex-shrink: 1;
- flex-grow: 1;
- }
-
- .sidebar-section {
- border-top: solid 1px rgba(0, 0, 0, 0.12);
- padding: 20px 0px 20px 30px;
- }
-
- .sidebar-section:first-child {
- border: none;
- }
-
- .sidebar-section:last-child {
- flex-grow: 1;
- display: flex;
- }
-
- paper-checkbox {
- --paper-checkbox-checked-color: var(--tb-ui-dark-accent);
- --paper-checkbox-unchecked-color: var(--tb-ui-dark-accent);
- font-size: 14px;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-sidebar-helper",
- properties: {
- /**
- * The backend object used to issue requests.
- */
- backend: Object,
-
- /**
- * This is an output of the categories that the user selected to
- * separate the different tags. Each category here should be given its
- * own collapsible pane.
- */
- categories: {
- type: Array,
- notify: true,
- },
-
- /**
- * Input of the colors that are used for the user's runs.
- */
- colorScale: Object,
-
- /**
- * Map from runs to the valid tags that have them.
- */
- run2tag: Object,
-
- /**
- * Input of all valid runs that can be selected by the user.
- */
- runs: Array,
-
- /**
- * Outputs an array with the runs that are selected by the user (i.e.
- * valid to be displayed).
- */
- selectedRuns: {
- type: Array,
- notify: true,
- },
-
- tags: {
- type: Array,
- computed: "_getTags(run2tag.*)"
- },
- },
- _getTags: function() {
- return _.union.apply(null, _.values(this.run2tag));
- },
- })
- </script>
-</dom-module>
-
-<dom-module id="vz-line-chart" assetpath="../vz-line-chart/">
- <template>
- <div id="tooltip">
- <table>
- <thead>
- <tr>
- <th></th>
- <th>Name</th>
- <template is="dom-if" if="{{smoothingEnabled}}">
- <th>Smoothed</th>
- </template>
- <th>Value</th>
- <th>Step</th>
- <th>Time</th>
- <th>Relative</th>
- </tr>
- </thead>
- <tbody>
- </tbody>
- </table>
- </div>
- <svg id="chartsvg"></svg>
- <style>
- :host {
- -webkit-user-select: none;
- -moz-user-select: none;
- display: flex;
- flex-direction: column;
- flex-grow: 1;
- flex-shrink: 1;
- position: relative;
- outline: none;
- }
- svg {
- -webkit-user-select: none;
- -moz-user-select: none;
- flex-grow: 1;
- flex-shrink: 1;
- }
- td {
- padding-left: 5px;
- padding-right: 5px;
- font-size: 13px;
- opacity: 1;
- }
- #tooltip {
- pointer-events: none;
- position: absolute;
- opacity: 0;
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- font-size: 14px;
- background: rgba(0, 0, 0, 0.8);
- color: white;
- border-radius: 4px;
- line-height: 1.4em;
- padding: 8px;
- z-index: 5;
- cursor: none;
- margin-top: 10px;
- }
- .swatch {
- border-radius: 50%;
- width: 14px;
- height: 14px;
- display: block;
- border: 2px solid rgba(0,0,0,0);
- }
- .closest .swatch {
- border: 2px solid white;
- }
- th {
- padding-left: 5px;
- padding-right: 5px;
- text-align: left;
- }
- .distant td {
- opacity: 0.8;
- }
-
- .distant td.swatch {
- opacity: 1;
- }
-
- .ghost {
- opacity: 0.2;
- stroke-width: 1px;
- }
-
- #chartsvg line.guide-line {
- stroke: #999;
- stroke-width: 1.5px;
- }
-
- </style>
- </template>
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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 () {
- var extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
-})();
-var Plottable;
-(function (Plottable) {
- var DragZoomLayer = (function (_super) {
- __extends(DragZoomLayer, _super);
- /**
- * Constructs a SelectionBoxLayer with an attached DragInteraction and
- * ClickInteraction. On drag, it triggers an animated zoom into the box
- * that was dragged. On double click, it zooms back out to the original
- * view, before any zooming.
- * The zoom animation uses an easing function (default
- * d3.ease('cubic-in-out')) and is customizable.
- * Usage: Construct the selection box layer and attach x and y scales,
- * and then add the layer over the plot you are zooming on using a
- * Component Group.
- * TODO(danmane) - merge this into Plottable
- */
- function DragZoomLayer(xScale, yScale, unzoomMethod) {
- var _this = _super.call(this) || this;
- _this.isZoomed = false;
- _this.easeFn = d3.ease('cubic-in-out');
- _this._animationTime = 750;
- _this.xScale(xScale);
- _this.yScale(yScale);
- _this._dragInteraction = new Plottable.Interactions.Drag();
- _this._dragInteraction.attachTo(_this);
- _this._doubleClickInteraction = new Plottable.Interactions.DoubleClick();
- _this._doubleClickInteraction.attachTo(_this);
- _this.setupCallbacks();
- _this.unzoomMethod = unzoomMethod;
- return _this;
- }
- /**
- * Register a method that calls when the DragZoom interaction starts.
- */
- DragZoomLayer.prototype.interactionStart = function (cb) { this.onStart = cb; };
- /**
- * Register a method that calls when the DragZoom interaction ends.
- */
- DragZoomLayer.prototype.interactionEnd = function (cb) { this.onEnd = cb; };
- DragZoomLayer.prototype.setupCallbacks = function () {
- var _this = this;
- var dragging = false;
- this._dragInteraction.onDragStart(function (startPoint) {
- _this.bounds({
- topLeft: startPoint,
- bottomRight: startPoint,
- });
- _this.onStart();
- });
- this._dragInteraction.onDrag(function (startPoint, endPoint) {
- _this.bounds({ topLeft: startPoint, bottomRight: endPoint });
- _this.boxVisible(true);
- dragging = true;
- });
- this._dragInteraction.onDragEnd(function (startPoint, endPoint) {
- _this.boxVisible(false);
- _this.bounds({ topLeft: startPoint, bottomRight: endPoint });
- if (dragging) {
- _this.zoom();
- }
- else {
- _this.onEnd();
- }
- dragging = false;
- });
- this._doubleClickInteraction.onDoubleClick(this.unzoom.bind(this));
- };
- DragZoomLayer.prototype.animationTime = function (animationTime) {
- if (animationTime == null) {
- return this._animationTime;
- }
- if (animationTime < 0) {
- throw new Error('animationTime cannot be negative');
- }
- this._animationTime = animationTime;
- return this;
- };
- /**
- * Set the easing function, which determines how the zoom interpolates
- * over time.
- */
- DragZoomLayer.prototype.ease = function (fn) {
- if (typeof (fn) !== 'function') {
- throw new Error('ease function must be a function');
- }
- if (fn(0) !== 0 || fn(1) !== 1) {
- Plottable.Utils.Window.warn('Easing function does not maintain invariant ' +
- 'f(0)==0 && f(1)==1. Bad behavior may result.');
- }
- this.easeFn = fn;
- return this;
- };
- // Zoom into extent of the selection box bounds
- DragZoomLayer.prototype.zoom = function () {
- var x0 = this.xExtent()[0].valueOf();
- var x1 = this.xExtent()[1].valueOf();
- var y0 = this.yExtent()[1].valueOf();
- var y1 = this.yExtent()[0].valueOf();
- if (x0 === x1 || y0 === y1) {
- return;
- }
- if (!this.isZoomed) {
- this.isZoomed = true;
- }
- this.interpolateZoom(x0, x1, y0, y1);
- };
- // Restore the scales to their state before any zoom
- DragZoomLayer.prototype.unzoom = function () {
- if (!this.isZoomed) {
- return;
- }
- this.isZoomed = false;
- var xScale = this.xScale();
- xScale._domainMin = null;
- xScale._domainMax = null;
- var xDomain = xScale._getExtent();
- this.xScale().domain(xDomain);
- this.unzoomMethod();
- };
- // If we are zooming, disable interactions, to avoid contention
- DragZoomLayer.prototype.isZooming = function (isZooming) {
- this._dragInteraction.enabled(!isZooming);
- this._doubleClickInteraction.enabled(!isZooming);
- };
- DragZoomLayer.prototype.interpolateZoom = function (x0f, x1f, y0f, y1f) {
- var _this = this;
- var x0s = this.xScale().domain()[0].valueOf();
- var x1s = this.xScale().domain()[1].valueOf();
- var y0s = this.yScale().domain()[0].valueOf();
- var y1s = this.yScale().domain()[1].valueOf();
- // Copy a ref to the ease fn, so that changing ease wont affect zooms in
- // progress.
- var ease = this.easeFn;
- var interpolator = function (a, b, p) {
- return d3.interpolateNumber(a, b)(ease(p));
- };
- this.isZooming(true);
- var start = Date.now();
- var draw = function () {
- var now = Date.now();
- var passed = now - start;
- var p = _this._animationTime === 0 ?
- 1 :
- Math.min(1, passed / _this._animationTime);
- var x0 = interpolator(x0s, x0f, p);
- var x1 = interpolator(x1s, x1f, p);
- var y0 = interpolator(y0s, y0f, p);
- var y1 = interpolator(y1s, y1f, p);
- _this.xScale().domain([x0, x1]);
- _this.yScale().domain([y0, y1]);
- if (p < 1) {
- Plottable.Utils.DOM.requestAnimationFramePolyfill(draw);
- }
- else {
- _this.onEnd();
- _this.isZooming(false);
- }
- };
- draw();
- };
- return DragZoomLayer;
- }(Plottable.Components.SelectionBoxLayer));
- Plottable.DragZoomLayer = DragZoomLayer;
-})(Plottable || (Plottable = {}));
-</script>
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-/* tslint:disable:no-namespace variable-name */
-var VZ;
-(function (VZ) {
- var LineChart = (function () {
- function LineChart(xType, yScaleType, colorScale, tooltip) {
- this.seriesNames = [];
- this.name2datasets = {};
- this.colorScale = colorScale;
- this.tooltip = tooltip;
- this.datasets = [];
- this._ignoreYOutliers = false;
- // lastPointDataset is a dataset that contains just the last point of
- // every dataset we're currently drawing.
- this.lastPointsDataset = new Plottable.Dataset();
- this.nanDataset = new Plottable.Dataset();
- // need to do a single bind, so we can deregister the callback from
- // old Plottable.Datasets. (Deregistration is done by identity checks.)
- this.onDatasetChanged = this._onDatasetChanged.bind(this);
- this.buildChart(xType, yScaleType);
- }
- LineChart.prototype.buildChart = function (xType, yScaleType) {
- if (this.outer) {
- this.outer.destroy();
- }
- var xComponents = VZ.ChartHelpers.getXComponents(xType);
- this.xAccessor = xComponents.accessor;
- this.xScale = xComponents.scale;
- this.xAxis = xComponents.axis;
- this.xAxis.margin(0).tickLabelPadding(3);
- this.yScale = LineChart.getYScaleFromType(yScaleType);
- this.yAxis = new Plottable.Axes.Numeric(this.yScale, 'left');
- var yFormatter = VZ.ChartHelpers.multiscaleFormatter(VZ.ChartHelpers.Y_AXIS_FORMATTER_PRECISION);
- this.yAxis.margin(0).tickLabelPadding(5).formatter(yFormatter);
- this.yAxis.usesTextWidthApproximation(true);
- this.dzl = new Plottable.DragZoomLayer(this.xScale, this.yScale, this.updateSpecialDatasets.bind(this));
- var center = this.buildPlot(this.xAccessor, this.xScale, this.yScale);
- this.gridlines =
- new Plottable.Components.Gridlines(this.xScale, this.yScale);
- var xZeroLine = new Plottable.Components.GuideLineLayer('horizontal');
- xZeroLine.scale(this.yScale).value(0);
- var yZeroLine = new Plottable.Components.GuideLineLayer('vertical');
- yZeroLine.scale(this.xScale).value(0);
- this.center = new Plottable.Components.Group([this.gridlines, xZeroLine, yZeroLine, center, this.dzl]);
- this.outer = new Plottable.Components.Table([
- [this.yAxis, this.center],
- [null, this.xAxis]
- ]);
- };
- LineChart.prototype.buildPlot = function (xAccessor, xScale, yScale) {
- var _this = this;
- this.scalarAccessor = function (d) { return d.scalar; };
- this.smoothedAccessor = function (d) { return d.smoothed; };
- var linePlot = new Plottable.Plots.Line();
- linePlot.x(xAccessor, xScale);
- linePlot.y(this.scalarAccessor, yScale);
- linePlot.attr('stroke', function (d, i, dataset) {
- return _this.colorScale.scale(dataset.metadata().name);
- });
- this.linePlot = linePlot;
- var group = this.setupTooltips(linePlot);
- var smoothLinePlot = new Plottable.Plots.Line();
- smoothLinePlot.x(xAccessor, xScale);
- smoothLinePlot.y(this.smoothedAccessor, yScale);
- smoothLinePlot.attr('stroke', function (d, i, dataset) {
- return _this.colorScale.scale(dataset.metadata().name);
- });
- this.smoothLinePlot = smoothLinePlot;
- // The scatterPlot will display the last point for each dataset.
- // This way, if there is only one datum for the series, it is still
- // visible. We hide it when tooltips are active to keep things clean.
- var scatterPlot = new Plottable.Plots.Scatter();
- scatterPlot.x(xAccessor, xScale);
- scatterPlot.y(this.scalarAccessor, yScale);
- scatterPlot.attr('fill', function (d) { return _this.colorScale.scale(d.name); });
- scatterPlot.attr('opacity', 1);
- scatterPlot.size(VZ.ChartHelpers.TOOLTIP_CIRCLE_SIZE * 2);
- scatterPlot.datasets([this.lastPointsDataset]);
- this.scatterPlot = scatterPlot;
- var nanDisplay = new Plottable.Plots.Scatter();
- nanDisplay.x(xAccessor, xScale);
- nanDisplay.y(function (x) { return x.displayY; }, yScale);
- nanDisplay.attr('fill', function (d) { return _this.colorScale.scale(d.name); });
- nanDisplay.attr('opacity', 1);
- nanDisplay.size(VZ.ChartHelpers.NAN_SYMBOL_SIZE * 2);
- nanDisplay.datasets([this.nanDataset]);
- nanDisplay.symbol(Plottable.SymbolFactories.triangleUp);
- this.nanDisplay = nanDisplay;
- return new Plottable.Components.Group([nanDisplay, scatterPlot, smoothLinePlot, group]);
- };
- /** Updates the chart when a dataset changes. Called every time the data of
- * a dataset changes to update the charts.
- */
- LineChart.prototype._onDatasetChanged = function (dataset) {
- if (this.smoothingEnabled) {
- this.resmoothDataset(dataset);
- }
- this.updateSpecialDatasets();
- };
- LineChart.prototype.ignoreYOutliers = function (ignoreYOutliers) {
- if (ignoreYOutliers !== this._ignoreYOutliers) {
- this._ignoreYOutliers = ignoreYOutliers;
- this.updateSpecialDatasets();
- }
- };
- LineChart.prototype.updateSpecialDatasets = function () {
- if (this.smoothingEnabled) {
- this.updateSpecialDatasetsWithAccessor(this.smoothedAccessor);
- }
- else {
- this.updateSpecialDatasetsWithAccessor(this.scalarAccessor);
- }
- };
- /** Constructs special datasets. Each special dataset contains exceptional
- * values from all of the regular datasets, e.g. last points in series, or
- * NaN values. Those points will have a `name` and `relative` property added
- * (since usually those are context in the surrounding dataset).
- * The accessor will point to the correct data to access.
- */
- LineChart.prototype.updateSpecialDatasetsWithAccessor = function (accessor) {
- var lastPointsData = this.datasets
- .map(function (d) {
- var datum = null;
- // filter out NaNs to ensure last point is a clean one
- var nonNanData = d.data().filter(function (x) { return !isNaN(accessor(x, -1, d)); });
- if (nonNanData.length > 0) {
- var idx = nonNanData.length - 1;
- datum = nonNanData[idx];
- datum.name = d.metadata().name;
- datum.relative =
- VZ.ChartHelpers.relativeAccessor(datum, -1, d);
- }
- return datum;
- })
- .filter(function (x) { return x != null; });
- this.lastPointsDataset.data(lastPointsData);
- // Take a dataset, return an array of NaN data points
- // the NaN points will have a "displayY" property which is the
- // y-value of a nearby point that was not NaN (0 if all points are NaN)
- var datasetToNaNData = function (d) {
- var displayY = null;
- var data = d.data();
- var i = 0;
- while (i < data.length && displayY == null) {
- if (!isNaN(accessor(data[i], -1, d))) {
- displayY = accessor(data[i], -1, d);
- }
- i++;
- }
- if (displayY == null) {
- displayY = 0;
- }
- var nanData = [];
- for (i = 0; i < data.length; i++) {
- if (!isNaN(accessor(data[i], -1, d))) {
- displayY = accessor(data[i], -1, d);
- }
- else {
- data[i].name = d.metadata().name;
- data[i].displayY = displayY;
- data[i].relative = VZ.ChartHelpers.relativeAccessor(data[i], -1, d);
- nanData.push(data[i]);
- }
- }
- return nanData;
- };
- var nanData = _.flatten(this.datasets.map(datasetToNaNData));
- this.nanDataset.data(nanData);
- var datasetToValues = function (d) {
- return d.data().map(function (x) { return accessor(x, -1, d); });
- };
- var vals = _.flatten(this.datasets.map(datasetToValues));
- vals = vals.filter(function (x) { return x === x && x !== Infinity && x !== -Infinity; });
- var domain = VZ.ChartHelpers.computeDomain(vals, this._ignoreYOutliers);
- this.yScale.domain(domain);
- };
- LineChart.prototype.setupTooltips = function (plot) {
- var _this = this;
- var pi = new Plottable.Interactions.Pointer();
- pi.attachTo(plot);
- // PointsComponent is a Plottable Component that will hold the little
- // circles we draw over the closest data points
- var pointsComponent = new Plottable.Component();
- var group = new Plottable.Components.Group([plot, pointsComponent]);
- var hideTooltips = function () {
- _this.tooltip.style('opacity', 0);
- _this.scatterPlot.attr('opacity', 1);
- pointsComponent.content().selectAll('.point').remove();
- };
- var enabled = true;
- var disableTooltips = function () {
- enabled = false;
- hideTooltips();
- };
- var enableTooltips = function () { enabled = true; };
- this.dzl.interactionStart(disableTooltips);
- this.dzl.interactionEnd(enableTooltips);
- pi.onPointerMove(function (p) {
- if (!enabled) {
- return;
- }
- var target = {
- x: p.x,
- y: p.y,
- datum: null,
- dataset: null,
- };
- var bbox = _this.gridlines.content().node().getBBox();
- // pts is the closets point to the tooltip for each dataset
- var pts = plot.datasets()
- .map(function (dataset) { return _this.findClosestPoint(target, dataset); })
- .filter(function (x) { return x != null; });
- var intersectsBBox = Plottable.Utils.DOM.intersectsBBox;
- // We draw tooltips for points that are NaN, or are currently visible
- var ptsForTooltips = pts.filter(function (p) { return intersectsBBox(p.x, p.y, bbox) || isNaN(p.datum.scalar); });
- // Only draw little indicator circles for the non-NaN points
- var ptsToCircle = ptsForTooltips.filter(function (p) { return !isNaN(p.datum.scalar); });
- var ptsSelection = pointsComponent.content().selectAll('.point').data(ptsToCircle, function (p) { return p.dataset.metadata().name; });
- if (pts.length !== 0) {
- ptsSelection.enter().append('circle').classed('point', true);
- ptsSelection.attr('r', VZ.ChartHelpers.TOOLTIP_CIRCLE_SIZE)
- .attr('cx', function (p) { return p.x; })
- .attr('cy', function (p) { return p.y; })
- .style('stroke', 'none')
- .attr('fill', function (p) { return _this.colorScale.scale(p.dataset.metadata().name); });
- ptsSelection.exit().remove();
- _this.drawTooltips(ptsForTooltips, target);
- }
- else {
- hideTooltips();
- }
- });
- pi.onPointerExit(hideTooltips);
- return group;
- };
- LineChart.prototype.drawTooltips = function (points, target) {
- var _this = this;
- // Formatters for value, step, and wall_time
- this.scatterPlot.attr('opacity', 0);
- var valueFormatter = VZ.ChartHelpers.multiscaleFormatter(VZ.ChartHelpers.Y_TOOLTIP_FORMATTER_PRECISION);
- var dist = function (p) {
- return Math.pow(p.x - target.x, 2) + Math.pow(p.y - target.y, 2);
- };
- var closestDist = _.min(points.map(dist));
- var valueSortMethod = this.scalarAccessor;
- if (this.smoothingEnabled) {
- valueSortMethod = this.smoothedAccessor;
- }
- if (this.tooltipSortingMethod === 'ascending') {
- points =
- _.sortBy(points, function (d) { return valueSortMethod(d.datum, -1, d.dataset); });
- }
- else if (this.tooltipSortingMethod === 'descending') {
- points =
- _.sortBy(points, function (d) { return valueSortMethod(d.datum, -1, d.dataset); })
- .reverse();
- }
- else if (this.tooltipSortingMethod === 'nearest') {
- points = _.sortBy(points, dist);
- }
- else {
- // The 'default' sorting method maintains the order of names passed to
- // setVisibleSeries(). However we reverse that order when defining the
- // datasets. So we must call reverse again to restore the order.
- points = points.slice(0).reverse();
- }
- var rows = this.tooltip.select('tbody')
- .html('')
- .selectAll('tr')
- .data(points)
- .enter()
- .append('tr');
- // Grey out the point if any of the following are true:
- // - The cursor is outside of the x-extent of the dataset
- // - The point's y value is NaN
- rows.classed('distant', function (d) {
- var firstPoint = d.dataset.data()[0];
- var lastPoint = _.last(d.dataset.data());
- var firstX = _this.xScale.scale(_this.xAccessor(firstPoint, 0, d.dataset));
- var lastX = _this.xScale.scale(_this.xAccessor(lastPoint, 0, d.dataset));
- var s = _this.smoothingEnabled ? d.datum.smoothed : d.datum.scalar;
- return target.x < firstX || target.x > lastX || isNaN(s);
- });
- rows.classed('closest', function (p) { return dist(p) === closestDist; });
- // It is a bit hacky that we are manually applying the width to the swatch
- // and the nowrap property to the text here. The reason is as follows:
- // the style gets updated asynchronously by Polymer scopeSubtree observer.
- // Which means we would get incorrect sizing information since the text
- // would wrap by default. However, we need correct measurements so that
- // we can stop the text from falling off the edge of the screen.
- // therefore, we apply the size-critical styles directly.
- rows.style('white-space', 'nowrap');
- rows.append('td')
- .append('span')
- .classed('swatch', true)
- .style('background-color', function (d) { return _this.colorScale.scale(d.dataset.metadata().name); });
- rows.append('td').text(function (d) { return d.dataset.metadata().name; });
- if (this.smoothingEnabled) {
- rows.append('td').text(function (d) { return isNaN(d.datum.smoothed) ? 'NaN' :
- valueFormatter(d.datum.smoothed); });
- }
- rows.append('td').text(function (d) {
- return isNaN(d.datum.scalar) ? 'NaN' : valueFormatter(d.datum.scalar);
- });
- rows.append('td').text(function (d) { return VZ.ChartHelpers.stepFormatter(d.datum.step); });
- rows.append('td').text(function (d) { return VZ.ChartHelpers.timeFormatter(d.datum.wall_time); });
- rows.append('td').text(function (d) { return VZ.ChartHelpers.relativeFormatter(VZ.ChartHelpers.relativeAccessor(d.datum, -1, d.dataset)); });
- // compute left position
- var documentWidth = document.body.clientWidth;
- var node = this.tooltip.node();
- var parentRect = node.parentElement.getBoundingClientRect();
- var nodeRect = node.getBoundingClientRect();
- // prevent it from falling off the right side of the screen
- var left = documentWidth - parentRect.left - nodeRect.width - 60, top = 0;
- if (this.tooltipPosition === 'right') {
- left = Math.min(parentRect.width, left);
- }
- else {
- left = Math.min(0, left);
- top = parentRect.height + VZ.ChartHelpers.TOOLTIP_Y_PIXEL_OFFSET;
- }
- this.tooltip.style('transform', 'translate(' + left + 'px,' + top + 'px)');
- this.tooltip.style('opacity', 1);
- };
- LineChart.prototype.findClosestPoint = function (target, dataset) {
- var _this = this;
- var points = dataset.data().map(function (d, i) {
- var x = _this.xAccessor(d, i, dataset);
- var y = _this.smoothingEnabled ? _this.smoothedAccessor(d, i, dataset) :
- _this.scalarAccessor(d, i, dataset);
- return {
- x: _this.xScale.scale(x),
- y: _this.yScale.scale(y),
- datum: d,
- dataset: dataset,
- };
- });
- var idx = _.sortedIndex(points, target, function (p) { return p.x; });
- if (idx === points.length) {
- return points[points.length - 1];
- }
- else if (idx === 0) {
- return points[0];
- }
- else {
- var prev = points[idx - 1];
- var next = points[idx];
- var prevDist = Math.abs(prev.x - target.x);
- var nextDist = Math.abs(next.x - target.x);
- return prevDist < nextDist ? prev : next;
- }
- };
- LineChart.prototype.resmoothDataset = function (dataset) {
- var data = dataset.data();
- var smoothingWeight = this.smoothingWeight;
- var last = data.length > 0 ? data[0].scalar : NaN;
- data.forEach(function (d) {
- if (!_.isFinite(last)) {
- d.smoothed = d.scalar;
- }
- else {
- // 1st-order IIR low-pass filter to attenuate the higher-
- // frequency components of the time-series.
- d.smoothed =
- last * smoothingWeight + (1 - smoothingWeight) * d.scalar;
- }
- last = d.smoothed;
- });
- };
- LineChart.prototype.getDataset = function (name) {
- if (this.name2datasets[name] === undefined) {
- this.name2datasets[name] = new Plottable.Dataset([], { name: name });
- }
- return this.name2datasets[name];
- };
- LineChart.getYScaleFromType = function (yScaleType) {
- if (yScaleType === 'log') {
- return new Plottable.Scales.ModifiedLog();
- }
- else if (yScaleType === 'linear') {
- return new Plottable.Scales.Linear();
- }
- else {
- throw new Error('Unrecognized yScale type ' + yScaleType);
- }
- };
- /**
- * Update the selected series on the chart.
- */
- LineChart.prototype.setVisibleSeries = function (names) {
- var _this = this;
- names = names.sort();
- this.seriesNames = names;
- names.reverse(); // draw first series on top
- this.datasets.forEach(function (d) { return d.offUpdate(_this.onDatasetChanged); });
- this.datasets = names.map(function (r) { return _this.getDataset(r); });
- this.datasets.forEach(function (d) { return d.onUpdate(_this.onDatasetChanged); });
- this.linePlot.datasets(this.datasets);
- if (this.smoothingEnabled) {
- this.smoothLinePlot.datasets(this.datasets);
- }
- this.updateSpecialDatasets();
- };
- /**
- * Set the data of a series on the chart.
- */
- LineChart.prototype.setSeriesData = function (name, data) {
- this.getDataset(name).data(data);
- };
- LineChart.prototype.smoothingUpdate = function (weight) {
- var _this = this;
- this.smoothingWeight = weight;
- this.datasets.forEach(function (d) { return _this.resmoothDataset(d); });
- if (!this.smoothingEnabled) {
- this.linePlot.addClass('ghost');
- this.scatterPlot.y(this.smoothedAccessor, this.yScale);
- this.smoothingEnabled = true;
- this.smoothLinePlot.datasets(this.datasets);
- }
- this.updateSpecialDatasetsWithAccessor(this.smoothedAccessor);
- };
- LineChart.prototype.smoothingDisable = function () {
- if (this.smoothingEnabled) {
- this.linePlot.removeClass('ghost');
- this.scatterPlot.y(this.scalarAccessor, this.yScale);
- this.smoothLinePlot.datasets([]);
- this.smoothingEnabled = false;
- this.updateSpecialDatasetsWithAccessor(this.scalarAccessor);
- }
- };
- LineChart.prototype.setTooltipSortingMethod = function (method) {
- this.tooltipSortingMethod = method;
- };
- LineChart.prototype.setTooltipPosition = function (position) {
- this.tooltipPosition = position;
- };
- LineChart.prototype.renderTo = function (targetSVG) {
- this.targetSVG = targetSVG;
- this.setViewBox();
- this.outer.renderTo(targetSVG);
- };
- /** There's an issue in Chrome where the svg overflow is a bit
- * "flickery". There is a border on the gridlines on the extreme edge of the
- * chart, which behaves inconsistently and causes the screendiffing tests to
- * flake. We can solve this by creating 1px effective margin for the svg by
- * setting the viewBox on the containing svg.
- */
- LineChart.prototype.setViewBox = function () {
- // There's an issue in Firefox where if we measure with the old viewbox
- // set, we get horrible results.
- this.targetSVG.attr('viewBox', null);
- var parent = this.targetSVG.node().parentNode;
- var w = parent.clientWidth;
- var h = parent.clientHeight;
- this.targetSVG.attr({
- 'height': h,
- 'viewBox': "0 0 " + (w + 1) + " " + (h + 1),
- });
- };
- LineChart.prototype.redraw = function () {
- this.outer.redraw();
- this.setViewBox();
- };
- LineChart.prototype.destroy = function () { this.outer.destroy(); };
- return LineChart;
- }());
- VZ.LineChart = LineChart;
-})(VZ || (VZ = {}));
-</script>
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-/* tslint:disable:no-namespace variable-name */
-var VZ;
-(function (VZ) {
- var ChartHelpers;
- (function (ChartHelpers) {
- ChartHelpers.Y_TOOLTIP_FORMATTER_PRECISION = 4;
- ChartHelpers.STEP_FORMATTER_PRECISION = 4;
- ChartHelpers.Y_AXIS_FORMATTER_PRECISION = 3;
- ChartHelpers.TOOLTIP_Y_PIXEL_OFFSET = 20;
- ChartHelpers.TOOLTIP_CIRCLE_SIZE = 4;
- ChartHelpers.NAN_SYMBOL_SIZE = 6;
- /* Create a formatter function that will switch between exponential and
- * regular display depending on the scale of the number being formatted,
- * and show `digits` significant digits.
- */
- function multiscaleFormatter(digits) {
- return function (v) {
- var absv = Math.abs(v);
- if (absv < 1E-15) {
- // Sometimes zero-like values get an annoying representation
- absv = 0;
- }
- var f;
- if (absv >= 1E4) {
- f = d3.format('.' + digits + 'e');
- }
- else if (absv > 0 && absv < 0.01) {
- f = d3.format('.' + digits + 'e');
- }
- else {
- f = d3.format('.' + digits + 'g');
- }
- return f(v);
- };
- }
- ChartHelpers.multiscaleFormatter = multiscaleFormatter;
- /* Compute an appropriate domain given an array of all the values that are
- * going to be displayed. If ignoreOutliers is true, it will ignore the
- * lowest 10% and highest 10% of the data when computing a domain.
- * It has n log n performance when ignoreOutliers is true, as it needs to
- * sort the data.
- */
- function computeDomain(values, ignoreOutliers) {
- if (values.length === 0) {
- return [-0.1, 1.1];
- }
- var a;
- var b;
- if (ignoreOutliers) {
- var sorted = _.sortBy(values);
- a = d3.quantile(sorted, 0.05);
- b = d3.quantile(sorted, 0.95);
- }
- else {
- a = d3.min(values);
- b = d3.max(values);
- }
- var padding;
- var span = b - a;
- if (span === 0) {
- // If b===a, we would create an empty range. We instead select the range
- // [0, 2*a] if a > 0, or [-2*a, 0] if a < 0, plus a little bit of
- // extra padding on the top and bottom of the plot.
- padding = Math.abs(a) * 1.1 + 1.1;
- }
- else {
- padding = span * 0.2;
- }
- var lower;
- if (a >= 0 && a < span) {
- // We include the intercept (y = 0) if doing so less than doubles the span
- // of the y-axis. (We actually select a lower bound that's slightly less
- // than 0 so that 0.00 will clearly be written on the lower edge of the
- // chart. The label on the lowest tick is often filtered out.)
- lower = -0.1 * b;
- }
- else {
- lower = a - padding;
- }
- var domain = [lower, b + padding];
- domain = d3.scale.linear().domain(domain).nice().domain();
- return domain;
- }
- ChartHelpers.computeDomain = computeDomain;
- function accessorize(key) {
- return function (d, index, dataset) { return d[key]; };
- }
- ChartHelpers.accessorize = accessorize;
- ChartHelpers.stepFormatter = Plottable.Formatters.siSuffix(ChartHelpers.STEP_FORMATTER_PRECISION);
- function stepX() {
- var scale = new Plottable.Scales.Linear();
- var axis = new Plottable.Axes.Numeric(scale, 'bottom');
- axis.formatter(ChartHelpers.stepFormatter);
- return {
- scale: scale,
- axis: axis,
- accessor: function (d) { return d.step; },
- };
- }
- ChartHelpers.stepX = stepX;
- ChartHelpers.timeFormatter = Plottable.Formatters.time('%a %b %e, %H:%M:%S');
- function wallX() {
- var scale = new Plottable.Scales.Time();
- return {
- scale: scale,
- axis: new Plottable.Axes.Time(scale, 'bottom'),
- accessor: function (d) { return d.wall_time; },
- };
- }
- ChartHelpers.wallX = wallX;
- ChartHelpers.relativeAccessor = function (d, index, dataset) {
- // We may be rendering the final-point datum for scatterplot.
- // If so, we will have already provided the 'relative' property
- if (d.relative != null) {
- return d.relative;
- }
- var data = dataset.data();
- // I can't imagine how this function would be called when the data is
- // empty (after all, it iterates over the data), but lets guard just
- // to be safe.
- var first = data.length > 0 ? +data[0].wall_time : 0;
- return (+d.wall_time - first) / (60 * 60 * 1000); // ms to hours
- };
- ChartHelpers.relativeFormatter = function (n) {
- // we will always show 2 units of precision, e.g days and hours, or
- // minutes and seconds, but not hours and minutes and seconds
- var ret = '';
- var days = Math.floor(n / 24);
- n -= (days * 24);
- if (days) {
- ret += days + 'd ';
- }
- var hours = Math.floor(n);
- n -= hours;
- n *= 60;
- if (hours || days) {
- ret += hours + 'h ';
- }
- var minutes = Math.floor(n);
- n -= minutes;
- n *= 60;
- if (minutes || hours || days) {
- ret += minutes + 'm ';
- }
- var seconds = Math.floor(n);
- return ret + seconds + 's';
- };
- function relativeX() {
- var scale = new Plottable.Scales.Linear();
- return {
- scale: scale,
- axis: new Plottable.Axes.Numeric(scale, 'bottom'),
- accessor: ChartHelpers.relativeAccessor,
- };
- }
- ChartHelpers.relativeX = relativeX;
- // a very literal definition of NaN: true for NaN for a non-number type
- // or null, etc. False for Infinity or -Infinity
- ChartHelpers.isNaN = function (x) { return +x !== x; };
- function getXComponents(xType) {
- switch (xType) {
- case 'step':
- return stepX();
- case 'wall_time':
- return wallX();
- case 'relative':
- return relativeX();
- default:
- throw new Error('invalid xType: ' + xType);
- }
- }
- ChartHelpers.getXComponents = getXComponents;
- })(ChartHelpers = VZ.ChartHelpers || (VZ.ChartHelpers = {}));
-})(VZ || (VZ = {}));
-</script>
- <script>
- Polymer({
- is: "vz-line-chart",
- properties: {
- /**
- * Scale that maps series names to colors. The default colors are from
- * d3.scale.category10() scale. Use this property to replace the default
- * line colors with colors of your own choice.
- * @type {Plottable.Scales.Color}
- * @required
- */
- colorScale: {
- type: Object,
- value: function() {
- return new Plottable.Scales.Color()
- .range(d3.scale.category10().range());
- }
- },
-
- /**
- * Whether smoothing is enabled or not. If true, smoothed lines will be
- * plotted in the chart while the unsmoothed lines will be ghosted in
- * the background.
- *
- * The smoothing algorithm is a simple moving average, which, given a
- * point p and a window w, replaces p with a simple average of the
- * points in the [p - floor(w/2), p + floor(w/2)] range. If there
- * aren't enough points to cover the entire window to the left, the
- * window is reduced to fit exactly the amount of elements available.
- * This means that the smoothed line will be less in and gradually
- * become more smooth until the desired window is reached. However when
- * there aren't enough points on the right, the line stops being
- * rendered at all.
- */
- smoothingEnabled: {
- type: Boolean,
- value: false
- },
-
- /**
- * Weight (between 0.0 and 1.0) of the smoothing. This weight controls
- * the window size, and a weight of 1.0 means using 50% of the entire
- * dataset as the window, while a weight of 0.0 means using a window of
- * 0 (and thus replacing each point with themselves).
- *
- * The growth between 0.0 and 1.0 is not linear though. Because
- * changing the window from 0% to 30% of the dataset smooths the line a
- * lot more than changing the window from 70% to 100%, an exponential
- * function is used instead: http://i.imgur.com/bDrhEZU.png. This
- * function increases the size of the window slowly at the beginning
- * and gradually speeds up the growth, but 0.0 still means a window of
- * 0 and 1.0 still means a window of the dataset's length.
- */
- smoothingWeight: {
- type: Number,
- value: 0.6
- },
-
- /**
- * The way to display the X values. Allows:
- * - "step" - Linear scale using the "step" property of the datum.
- * - "wall_time" - Temporal scale using the "wall_time" property of the
- * datum.
- * - "relative" - Temporal scale using the "relative" property of the
- * datum if it is present or calculating from "wall_time" if it isn't.
- */
- xType: {
- type: String,
- value: 'step'
- },
-
- /**
- * The scale for the y-axis. Allows:
- * - "linear" - linear scale (Plottable.Scales.Linear)
- * - "log" - modified-log scale (Plottable.Scales.ModifiedLog)
- */
- yScaleType: {
- type: String,
- value: 'linear'
- },
-
- /**
- * Whether to ignore outlier data when computing the yScale domain.
- */
-
- ignoreYOutliers: {
- type: Boolean,
- value: false,
- },
-
- /**
- * Change how the tooltip is sorted. Allows:
- * - "default" - Sort the tooltip by input order.
- * - "ascending" - Sort the tooltip by ascending value.
- * - "descending" - Sort the tooltip by descending value.
- * - "nearest" - Sort the tooltip by closest to cursor.
- */
- tooltipSortingMethod: {
- type: String,
- value: 'default'
- },
-
- /**
- * Change how the tooltip is positioned. Allows:
- * - "bottom" - Position the tooltip on the bottom of the chart.
- * - "right" - Position the tooltip to the right of the chart.
- */
- tooltipPosition: {
- type: String,
- value: 'bottom'
- },
-
- _attached: Boolean,
- _chart: Object,
- _visibleSeriesCache: {
- type: Array,
- value: function() { return [] }
- },
- _seriesDataCache: {
- type: Object,
- value: function() { return {} }
- },
- _makeChartAsyncCallbackId: {
- type: Number,
- value: null
- }
- },
- observers: [
- "_makeChart(xType, yScaleType, colorScale, _attached)",
- "_reloadFromCache(_chart)",
- "_smoothingChanged(smoothingEnabled, smoothingWeight, _chart)",
- "_tooltipSortingMethodChanged(tooltipSortingMethod, _chart)",
- "_tooltipPositionChanged(tooltipPosition, _chart)",
- "_outliersChanged(ignoreYOutliers, _chart)"
- ],
-
- /**
- * Sets the series that the chart displays. Series with other names will
- * not be displayed.
- *
- * @param {String[]} names Array with the names of the series to
- * display.
- */
- setVisibleSeries: function(names) {
- this._visibleSeriesCache = names;
- if (this._chart) {
- this._chart.setVisibleSeries(names);
- this.redraw();
- }
- },
-
- /**
- * Sets the data of one of the series. Note that to display this series
- * its name must be in the setVisibleSeries() array.
- *
- * @param {String} name Name of the series.
- * @param {VZ.ChartHelpers.ScalarDatum[]} data Data of the series. This is
- * an array of objects with at least the following properties:
- * - step: (Number) - index of the datum.
- * - wall_time: (Date) - Date object with the datum's time.
- * - scalar: (Number) - Value of the datum.
- */
- setSeriesData: function(name, data) {
- this._seriesDataCache[name] = data;
- if (this._chart) {
- this._chart.setSeriesData(name, data);
- }
- },
-
- /**
- * Re-renders the chart. Useful if e.g. the container size changed.
- */
- redraw: function() {
- this._chart.redraw();
- },
- attached: function() {
- this._attached = true;
- },
- detached: function() {
- this._attached = false;
- },
- ready: function() {
- this.scopeSubtree(this.$.tooltip, true);
- this.scopeSubtree(this.$.chartsvg, true);
- },
- _makeChart: function(xType, yScaleType, colorScale, _attached) {
- if (this._makeChartAsyncCallbackId !== null) {
- this.cancelAsync(this._makeChartAsyncCallbackId);
- this._makeChartAsyncCallbackId = null;
- }
-
- this._makeChartAsyncCallbackId = this.async(function() {
- this._makeChartAsyncCallbackId = null;
- if (!this._attached) return;
- if (this._chart) this._chart.destroy();
- var tooltip = d3.select(this.$.tooltip);
- var chart = new VZ.LineChart(xType, yScaleType, colorScale, tooltip);
- var svg = d3.select(this.$.chartsvg);
- chart.renderTo(svg);
- this._chart = chart;
- }, 350);
- },
- _reloadFromCache: function() {
- if(this._chart) {
- this._chart.setVisibleSeries(this._visibleSeriesCache);
- this._visibleSeriesCache.forEach(function(name) {
- this._chart.setSeriesData(name, this._seriesDataCache[name] || []);
- }.bind(this));
- }
- },
- _smoothingChanged: function() {
- if(!this._chart) {
- return;
- }
- if(this.smoothingEnabled) {
- this._chart.smoothingUpdate(this.smoothingWeight);
- }
- else {
- this._chart.smoothingDisable();
- }
- },
- _outliersChanged: function() {
- if (!this._chart) {
- return;
- }
- this._chart.ignoreYOutliers(this.ignoreYOutliers);
- },
- _tooltipSortingMethodChanged: function() {
- if(this._chart) {
- this._chart.setTooltipSortingMethod(this.tooltipSortingMethod);
- }
- },
- _tooltipPositionChanged: function() {
- if (this._chart) {
- this._chart.setTooltipPosition(this.tooltipPosition);
- }
- }
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-scalar-dashboard" assetpath="../tf-scalar-dashboard/">
- <template>
- <div id="plumbing">
- <tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{_colorScale}}"></tf-color-scale>
- </div>
-
- <tf-dashboard-layout>
- <div class="sidebar">
- <tf-sidebar-helper backend="[[backend]]" categories="{{_categories}}" color-scale="[[_colorScale]]" run2tag="[[run2tag]]" runs="[[runs]]" selected-runs="{{_selectedRuns}}">
- <div class="extend-first-section">
- <div class="line-item">
- <paper-checkbox id="download-option" checked="{{_showDownloadLinks}}">Show data download links</paper-checkbox>
- </div>
- <div class="line-item">
- <paper-checkbox id="outliersCheckbox" checked="{{_ignoreYOutliers}}">Ignore outliers in chart scaling</paper-checkbox>
- </div>
- <div id="tooltip-sorting">
- <div id="tooltip-sorting-label">Tooltip sorting method:</div>
- <paper-dropdown-menu no-label-float="" selected-item-label="{{_tooltipSortingMethod}}">
- <paper-menu class="dropdown-content" selected="0">
- <paper-item>default</paper-item>
- <paper-item>descending</paper-item>
- <paper-item>ascending</paper-item>
- <paper-item>nearest</paper-item>
- </paper-menu>
- </paper-dropdown-menu>
- </div>
- </div>
- <div class="sidebar-section">
- <tf-smoothing-input weight="{{_smoothingWeight}}" step="0.001" min="0" max="1"></tf-smoothing-input>
- </div>
- <div class="sidebar-section">
- <tf-option-selector id="xTypeSelector" name="Horizontal Axis" selected-id="{{_xType}}">
- <paper-button id="step">step</paper-button>
- <paper-button id="relative">relative</paper-button>
- <paper-button id="wall_time">wall</paper-button>
- </tf-option-selector>
- </div>
- </tf-sidebar-helper>
- </div>
- <div class="center">
- <tf-panes-helper categories="[[_categories]]" color-scale="[[_colorScale]]" data-type="[[dataType]]" data-provider="[[dataProvider]]" data-not-found="[[dataNotFound]]" run2tag="[[run2tag]]" selected-runs="[[_selectedRuns]]" show-download-links="[[_showDownloadLinks]]" download-link-url-function="[[scalarUrl]]">
- <template>
- <vz-line-chart x-type="[[_xType]]" color-scale="[[_colorScale]]" smoothing-enabled="[[_smoothingEnabled]]" smoothing-weight="[[_smoothingWeight]]" tooltip-sorting-method="[[_tooltipSortingMethod]]" ignore-y-outliers="[[_ignoreYOutliers]]"></vz-line-chart>
- <paper-icon-button class="log-button" icon="line-weight" on-tap="toggleLogScale" title="Toggle y-axis log scale"></paper-icon-button>
- </template>
- </tf-panes-helper>
- </div>
- </tf-dashboard-layout>
-
- <style include="dashboard-style"></style>
- <style>
- .log-button {
- position: absolute;
- left: 35px;
- bottom: -35px;
- color: #2196F3;
- background: #fff;
- width: 32px;
- height: 32px;
- padding: 4px;
- border-radius: 100%;
- }
-
- .log-button-selected {
- background: var(--tb-ui-light-accent);
- }
-
- #tooltip-sorting {
- display: flex;
- font-size: 14px;
- margin-top: 5px;
- }
-
- #tooltip-sorting-label {
- margin-top: 13px;
- margin-left: 28px;
- }
-
- #tooltip-sorting paper-dropdown-menu {
- margin-left: 10px;
- --paper-input-container-focus-color: var(--tb-orange-strong);
- width: 105px;
- }
- .line-item {
- display: block;
- padding-top: 5px;
- }
- </style>
-
- </template>
-
- <script>
- TF.Dashboard.TfScalarDashboard = Polymer({
- is: "tf-scalar-dashboard",
- factoryImpl: function(backend, router) {
- this.backend = backend;
- this.router = router;
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("scalars"),
- TF.Dashboard.ReloadBehavior("tf-chart-scaffold"),
- TF.Backend.Behavior,
- ],
- properties: {
- backend: Object,
- dataType: {
- type: String,
- value: "scalar"
- },
- router: Object,
- scalarUrl: {
- type: Function,
- computed: "_getScalarUrl(router)"
- },
- _showDownloadLinks: {
- type: Boolean,
- notify: true,
- value: TF.URIStorage.getBooleanInitializer('_showDownloadLinks',
- false, true),
- observer: '_showDownloadLinksObserver'
- },
- _smoothingWeight: {
- type: Number,
- notify: true,
- value: TF.URIStorage.getNumberInitializer('_smoothingWeight', 0.6),
- observer: '_smoothingWeightObserver'
- },
- _smoothingEnabled: {
- type: Boolean,
- computed: '_computeSmoothingEnabled(_smoothingWeight)'
- },
- _ignoreYOutliers: {
- type: Boolean,
- value: TF.URIStorage.getBooleanInitializer('_ignoreYOutliers', true, true),
- observer: '_ignoreYOutliersObserver',
- },
- _xType: {
- type: String,
- value: "step"
- }
- },
- attached: function() {
- this.async(function() {
- this.fire("rendered");
- });
- },
- _getScalarUrl: function() {
- return this.router.scalars;
- },
- _showDownloadLinksObserver: TF.URIStorage.getBooleanObserver(
- '_showDownloadLinks', /*default=*/ false, /*useLocalStorage=*/ true),
- _smoothingWeightObserver: TF.URIStorage.getNumberObserver(
- '_smoothingWeight', 0.6),
- _ignoreYOutliersObserver: TF.URIStorage.getBooleanObserver(
- '_ignoreYOutliers', /*default=*/ true, /*useLocalStorage=*/true),
- _computeSmoothingEnabled: function(_smoothingWeight) {
- return _smoothingWeight > 0;
- },
- toggleLogScale: function(e) {
- var currentTarget = Polymer.dom(e.currentTarget);
- var button = currentTarget.parentNode.querySelector('.log-button');
- var chart = currentTarget.parentNode.querySelector('vz-line-chart');
-
- button.classList.toggle("log-button-selected");
- chart.yScaleType = chart.yScaleType === 'log' ? 'linear' : 'log';
- chart.redraw();
- },
- });
- </script>
-</dom-module>
-<dom-module id="vz-distribution-chart" assetpath="../vz-distribution-chart/">
- <template>
- <svg id="chartsvg"></svg>
- <style>
- :host {
- -webkit-user-select: none;
- -moz-user-select: none;
- display: flex;
- flex-direction: column;
- flex-grow: 1;
- flex-shrink: 1;
- position: relative;
- }
- svg {
- -webkit-user-select: none;
- -moz-user-select: none;
- flex-grow: 1;
- flex-shrink: 1;
- }
-
- </style>
- </template>
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-/* tslint:disable:no-namespace variable-name */
-var VZ;
-(function (VZ) {
- var DistributionChart = (function () {
- function DistributionChart(xType, colorScale) {
- this.run2datasets = {};
- this.colorScale = colorScale;
- this.buildChart(xType);
- }
- DistributionChart.prototype.getDataset = function (run) {
- if (this.run2datasets[run] === undefined) {
- this.run2datasets[run] = new Plottable.Dataset([], { run: run });
- }
- return this.run2datasets[run];
- };
- DistributionChart.prototype.buildChart = function (xType) {
- if (this.outer) {
- this.outer.destroy();
- }
- var xComponents = VZ.ChartHelpers.getXComponents(xType);
- this.xAccessor = xComponents.accessor;
- this.xScale = xComponents.scale;
- this.xAxis = xComponents.axis;
- this.xAxis.margin(0).tickLabelPadding(3);
- this.yScale = new Plottable.Scales.Linear();
- this.yAxis = new Plottable.Axes.Numeric(this.yScale, 'left');
- var yFormatter = VZ.ChartHelpers.multiscaleFormatter(VZ.ChartHelpers.Y_AXIS_FORMATTER_PRECISION);
- this.yAxis.margin(0).tickLabelPadding(5).formatter(yFormatter);
- this.yAxis.usesTextWidthApproximation(true);
- var center = this.buildPlot(this.xAccessor, this.xScale, this.yScale);
- this.gridlines =
- new Plottable.Components.Gridlines(this.xScale, this.yScale);
- this.center = new Plottable.Components.Group([this.gridlines, center]);
- this.outer = new Plottable.Components.Table([[this.yAxis, this.center], [null, this.xAxis]]);
- };
- DistributionChart.prototype.buildPlot = function (xAccessor, xScale, yScale) {
- var _this = this;
- var percents = [0, 228, 1587, 3085, 5000, 6915, 8413, 9772, 10000];
- var opacities = _.range(percents.length - 1)
- .map(function (i) { return (percents[i + 1] - percents[i]) / 2500; });
- var accessors = percents.map(function (p, i) { return function (datum) { return datum[i][1]; }; });
- var median = 4;
- var medianAccessor = accessors[median];
- var plots = _.range(accessors.length - 1).map(function (i) {
- var p = new Plottable.Plots.Area();
- p.x(xAccessor, xScale);
- var y0 = i > median ? accessors[i] : accessors[i + 1];
- var y = i > median ? accessors[i + 1] : accessors[i];
- p.y(y, yScale);
- p.y0(y0);
- p.attr('fill', function (d, i, dataset) {
- return _this.colorScale.scale(dataset.metadata().run);
- });
- p.attr('stroke', function (d, i, dataset) {
- return _this.colorScale.scale(dataset.metadata().run);
- });
- p.attr('stroke-weight', function (d, i, m) { return '0.5px'; });
- p.attr('stroke-opacity', function () { return opacities[i]; });
- p.attr('fill-opacity', function () { return opacities[i]; });
- return p;
- });
- var medianPlot = new Plottable.Plots.Line();
- medianPlot.x(xAccessor, xScale);
- medianPlot.y(medianAccessor, yScale);
- medianPlot.attr('stroke', function (d, i, m) { return _this.colorScale.scale(m.run); });
- this.plots = plots;
- return new Plottable.Components.Group(plots);
- };
- DistributionChart.prototype.setVisibleSeries = function (runs) {
- var _this = this;
- this.runs = runs;
- var datasets = runs.map(function (r) { return _this.getDataset(r); });
- this.plots.forEach(function (p) { return p.datasets(datasets); });
- };
- /**
- * Set the data of a series on the chart.
- */
- DistributionChart.prototype.setSeriesData = function (name, data) {
- this.getDataset(name).data(data);
- };
- DistributionChart.prototype.renderTo = function (targetSVG) {
- this.targetSVG = targetSVG;
- this.setViewBox();
- this.outer.renderTo(targetSVG);
- };
- /** There's an issue in Chrome where the svg overflow is a bit
- * "flickery". There is a border on the gridlines on the extreme edge of the
- * chart, which behaves inconsistently and causes the screendiffing tests to
- * flake. We can solve this by creating 1px effective margin for the svg by
- * setting the viewBox on the containing svg.
- */
- DistributionChart.prototype.setViewBox = function () {
- // There's an issue in Firefox where if we measure with the old viewbox
- // set, we get horrible results.
- this.targetSVG.attr('viewBox', null);
- var parent = this.targetSVG.node().parentNode;
- var w = parent.clientWidth;
- var h = parent.clientHeight;
- this.targetSVG.attr({
- 'height': h,
- 'viewBox': "0 0 " + (w + 1) + " " + (h + 1),
- });
- };
- DistributionChart.prototype.redraw = function () {
- this.outer.redraw();
- this.setViewBox();
- };
- DistributionChart.prototype.destroy = function () { this.outer.destroy(); };
- return DistributionChart;
- }());
- VZ.DistributionChart = DistributionChart;
-})(VZ || (VZ = {}));
-</script>
- <script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-/* tslint:disable:no-namespace variable-name */
-var VZ;
-(function (VZ) {
- var ChartHelpers;
- (function (ChartHelpers) {
- ChartHelpers.Y_TOOLTIP_FORMATTER_PRECISION = 4;
- ChartHelpers.STEP_FORMATTER_PRECISION = 4;
- ChartHelpers.Y_AXIS_FORMATTER_PRECISION = 3;
- ChartHelpers.TOOLTIP_Y_PIXEL_OFFSET = 20;
- ChartHelpers.TOOLTIP_CIRCLE_SIZE = 4;
- ChartHelpers.NAN_SYMBOL_SIZE = 6;
- /* Create a formatter function that will switch between exponential and
- * regular display depending on the scale of the number being formatted,
- * and show `digits` significant digits.
- */
- function multiscaleFormatter(digits) {
- return function (v) {
- var absv = Math.abs(v);
- if (absv < 1E-15) {
- // Sometimes zero-like values get an annoying representation
- absv = 0;
- }
- var f;
- if (absv >= 1E4) {
- f = d3.format('.' + digits + 'e');
- }
- else if (absv > 0 && absv < 0.01) {
- f = d3.format('.' + digits + 'e');
- }
- else {
- f = d3.format('.' + digits + 'g');
- }
- return f(v);
- };
- }
- ChartHelpers.multiscaleFormatter = multiscaleFormatter;
- /* Compute an appropriate domain given an array of all the values that are
- * going to be displayed. If ignoreOutliers is true, it will ignore the
- * lowest 10% and highest 10% of the data when computing a domain.
- * It has n log n performance when ignoreOutliers is true, as it needs to
- * sort the data.
- */
- function computeDomain(values, ignoreOutliers) {
- if (values.length === 0) {
- return [-0.1, 1.1];
- }
- var a;
- var b;
- if (ignoreOutliers) {
- var sorted = _.sortBy(values);
- a = d3.quantile(sorted, 0.05);
- b = d3.quantile(sorted, 0.95);
- }
- else {
- a = d3.min(values);
- b = d3.max(values);
- }
- var padding;
- var span = b - a;
- if (span === 0) {
- // If b===a, we would create an empty range. We instead select the range
- // [0, 2*a] if a > 0, or [-2*a, 0] if a < 0, plus a little bit of
- // extra padding on the top and bottom of the plot.
- padding = Math.abs(a) * 1.1 + 1.1;
- }
- else {
- padding = span * 0.2;
- }
- var lower;
- if (a >= 0 && a < span) {
- // We include the intercept (y = 0) if doing so less than doubles the span
- // of the y-axis. (We actually select a lower bound that's slightly less
- // than 0 so that 0.00 will clearly be written on the lower edge of the
- // chart. The label on the lowest tick is often filtered out.)
- lower = -0.1 * b;
- }
- else {
- lower = a - padding;
- }
- var domain = [lower, b + padding];
- domain = d3.scale.linear().domain(domain).nice().domain();
- return domain;
- }
- ChartHelpers.computeDomain = computeDomain;
- function accessorize(key) {
- return function (d, index, dataset) { return d[key]; };
- }
- ChartHelpers.accessorize = accessorize;
- ChartHelpers.stepFormatter = Plottable.Formatters.siSuffix(ChartHelpers.STEP_FORMATTER_PRECISION);
- function stepX() {
- var scale = new Plottable.Scales.Linear();
- var axis = new Plottable.Axes.Numeric(scale, 'bottom');
- axis.formatter(ChartHelpers.stepFormatter);
- return {
- scale: scale,
- axis: axis,
- accessor: function (d) { return d.step; },
- };
- }
- ChartHelpers.stepX = stepX;
- ChartHelpers.timeFormatter = Plottable.Formatters.time('%a %b %e, %H:%M:%S');
- function wallX() {
- var scale = new Plottable.Scales.Time();
- return {
- scale: scale,
- axis: new Plottable.Axes.Time(scale, 'bottom'),
- accessor: function (d) { return d.wall_time; },
- };
- }
- ChartHelpers.wallX = wallX;
- ChartHelpers.relativeAccessor = function (d, index, dataset) {
- // We may be rendering the final-point datum for scatterplot.
- // If so, we will have already provided the 'relative' property
- if (d.relative != null) {
- return d.relative;
- }
- var data = dataset.data();
- // I can't imagine how this function would be called when the data is
- // empty (after all, it iterates over the data), but lets guard just
- // to be safe.
- var first = data.length > 0 ? +data[0].wall_time : 0;
- return (+d.wall_time - first) / (60 * 60 * 1000); // ms to hours
- };
- ChartHelpers.relativeFormatter = function (n) {
- // we will always show 2 units of precision, e.g days and hours, or
- // minutes and seconds, but not hours and minutes and seconds
- var ret = '';
- var days = Math.floor(n / 24);
- n -= (days * 24);
- if (days) {
- ret += days + 'd ';
- }
- var hours = Math.floor(n);
- n -= hours;
- n *= 60;
- if (hours || days) {
- ret += hours + 'h ';
- }
- var minutes = Math.floor(n);
- n -= minutes;
- n *= 60;
- if (minutes || hours || days) {
- ret += minutes + 'm ';
- }
- var seconds = Math.floor(n);
- return ret + seconds + 's';
- };
- function relativeX() {
- var scale = new Plottable.Scales.Linear();
- return {
- scale: scale,
- axis: new Plottable.Axes.Numeric(scale, 'bottom'),
- accessor: ChartHelpers.relativeAccessor,
- };
- }
- ChartHelpers.relativeX = relativeX;
- // a very literal definition of NaN: true for NaN for a non-number type
- // or null, etc. False for Infinity or -Infinity
- ChartHelpers.isNaN = function (x) { return +x !== x; };
- function getXComponents(xType) {
- switch (xType) {
- case 'step':
- return stepX();
- case 'wall_time':
- return wallX();
- case 'relative':
- return relativeX();
- default:
- throw new Error('invalid xType: ' + xType);
- }
- }
- ChartHelpers.getXComponents = getXComponents;
- })(ChartHelpers = VZ.ChartHelpers || (VZ.ChartHelpers = {}));
-})(VZ || (VZ = {}));
-</script>
- <script>
- Polymer({
- is: "vz-distribution-chart",
- properties: {
- /**
- * Scale that maps series names to colors. The default colors are from
- * d3.scale.category10() scale. Use this property to replace the default
- * line colors with colors of your own choice.
- * @type {Plottable.Scales.Color}
- * @required
- */
- colorScale: {
- type: Object,
- value: function() {
- return new Plottable.Scales.Color()
- .range(d3.scale.category10().range());
- }
- },
- /**
- * The way to display the X values. Allows:
- * - "step" - Linear scale using the "step" property of the datum.
- * - "wall_time" - Temporal scale using the "wall_time" property of the
- * datum.
- * - "relative" - Temporal scale using the "relative" property of the
- * datum if it is present or calculating from "wall_time" if it isn't.
- */
- xType: {
- type: String,
- value: 'step'
- },
- _attached: Boolean,
- _chart: Object,
- _visibleSeriesCache: {
- type: Array,
- value: function() { return [] }
- },
- _seriesDataCache: {
- type: Object,
- value: function() { return {} }
- },
- _makeChartAsyncCallbackId: { type: Number, value: null }
- },
- observers: [
- "_makeChart(xType, colorScale, _attached)",
- "_reloadFromCache(_chart)",
- ],
- setVisibleSeries: function(names) {
- this._visibleSeriesCache = names;
- if (this._chart) {
- this._chart.setVisibleSeries(names);
- this.redraw();
- }
- },
- setSeriesData: function(name, data) {
- this._seriesDataCache[name] = data;
- if (this._chart) {
- this._chart.setSeriesData(name, data);
- }
- },
- redraw: function() {
- this._chart.redraw();
- },
- ready: function() {
- this.scopeSubtree(this.$.chartsvg, true);
- },
- _makeChart: function(xType, colorScale, _attached) {
- if (this._makeChartAsyncCallbackId === null) {
- this.cancelAsync(this._makeChartAsyncCallbackId);
- }
-
- this._makeChartAsyncCallbackId = this.async(function() {
- this._makeChartAsyncCallbackId = null;
- if (!_attached) return;
- if (this._chart) this._chart.destroy();
- var chart = new VZ.DistributionChart(xType, colorScale);
- var svg = d3.select(this.$.chartsvg);
- chart.renderTo(svg);
- this._chart = chart;
- }, 350);
- },
- _reloadFromCache: function() {
- if(this._chart) {
- this._chart.setVisibleSeries(this._visibleSeriesCache);
- this._visibleSeriesCache.forEach(function(name) {
- this._chart.setSeriesData(name, this._seriesDataCache[name] || []);
- }.bind(this));
- }
- },
- attached: function() {
- this._attached = true;
- },
- detached: function() {
- this._attached = false;
- }
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-distribution-dashboard" assetpath="../tf-distribution-dashboard/">
- <template>
- <div id="plumbing">
- <tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{_colorScale}}"></tf-color-scale>
- </div>
-
- <tf-dashboard-layout>
- <div class="sidebar">
- <tf-sidebar-helper backend="[[backend]]" categories="{{_categories}}" color-scale="[[_colorScale]]" run2tag="[[run2tag]]" runs="[[runs]]" selected-runs="{{_selectedRuns}}">
- <div class="sidebar-section">
- <tf-option-selector id="xTypeSelector" name="Horizontal Axis" selected-id="{{_xType}}">
- <paper-button id="step">step</paper-button>
- <paper-button id="relative">relative</paper-button>
- <paper-button id="wall_time">wall</paper-button>
- </tf-option-selector>
- </div>
- </tf-sidebar-helper>
- </div>
-
- <div class="center">
- <tf-panes-helper categories="[[_categories]]" color-scale="[[_colorScale]]" data-type="[[dataType]]" data-provider="[[dataProvider]]" data-not-found="[[dataNotFound]]" run2tag="[[run2tag]]" selected-runs="[[_selectedRuns]]" repeat-for-runs="">
- <template>
- <vz-distribution-chart x-type="[[_xType]]" color-scale="[[_colorScale]]"></vz-distribution-chart>
- </template>
- </tf-panes-helper>
- </div>
- </tf-dashboard-layout>
-
- <style include="dashboard-style"></style>
- </template>
-
- <script>
- TF.Dashboard.TfDistributionDashboard = Polymer({
- is: "tf-distribution-dashboard",
- factoryImpl: function(backend) {
- this.backend = backend;
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("distributions"),
- TF.Dashboard.ReloadBehavior("tf-chart-scaffold"),
- TF.Backend.Behavior,
- ],
- properties: {
- backend: Object,
- _xType: {
- type: String,
- value: "step"
- },
- dataType: {value: "compressedHistogram"},
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="vz-histogram-timeseries" assetpath="../vz-histogram-timeseries/">
- <template>
- <div id="tooltip"><span></span></div>
- <svg id="svg">
- <g>
- <g class="axis x"></g>
- <g class="axis y"></g>
- <g class="axis y slice"></g>
- <g class="stage">
- <rect class="background"></rect>
- </g>
- <g class="x-axis-hover"></g>
- <g class="y-axis-hover"></g>
- <g class="y-slice-axis-hover"></g>
- </g>
- </svg>
-
- <style>
- :host {
- display: flex;
- flex-direction: column;
- flex-grow: 1;
- flex-shrink: 1;
- position: relative;
- }
-
- svg {
- font-family: roboto, sans-serif;
- overflow: visible;
- display: block;
- width: 100%;
- flex-grow: 1;
- flex-shrink: 1;
- }
-
- #tooltip {
- position: absolute;
- display: block;
- opacity: 0;
- font-weight: bold;
- font-size: 11px;
- }
-
- .background {
- fill-opacity: 0;
- fill: red;
- }
-
- .histogram {
- pointer-events: none;
- }
-
- .hover {
- font-size: 9px;
- dominant-baseline: middle;
- opacity: 0;
- }
-
- .hover circle {
- stroke: white;
- stroke-opacity: 0.5;
- stroke-width: 1px;
- }
-
- .hover text {
- fill: black;
- opacity: 0;
- }
-
- .hover.hover-closest circle {
- fill: black!important;
- }
-
- .hover.hover-closest text {
- opacity: 1;
- }
-
- .baseline {
- stroke: black;
- stroke-opacity: 0.1;
- }
-
- .outline {
- fill: none;
- stroke: white;
- stroke-opacity: 0.5;
- }
-
- .outline.outline-hover {
- stroke: black!important;
- stroke-opacity: 1;
- }
-
- .x-axis-hover,
- .y-axis-hover,
- .y-slice-axis-hover {
- pointer-events: none;
- }
-
- .x-axis-hover .label,
- .y-axis-hover .label,
- .y-slice-axis-hover .label {
- opacity: 0;
- font-weight: bold;
- font-size: 11px;
- text-anchor: end;
- }
-
- .x-axis-hover text {
- text-anchor: middle;
- }
-
- .y-axis-hover text,
- .y-slice-axis-hover text {
- text-anchor: start;
- }
-
- .x-axis-hover line,
- .y-axis-hover line,
- .y-slice-axis-hover line {
- stroke: black;
- }
-
- .x-axis-hover rect,
- .y-axis-hover rect,
- .y-slice-axis-hover rect {
- fill: white;
- }
-
- .axis {
- font-size: 10px;
- fill: #aaa;
- }
-
- .axis path.domain {
- fill: none;
- }
-
- .axis .tick line {
- stroke: #ddd;
- }
-
- .axis.slice {
- opacity: 0;
- }
-
- .axis.slice .tick line {
- stroke-dasharray: 2;
- }
-
- .small .axis text { display: none; }
- .small .axis .tick:first-of-type text { display: block; }
- .small .axis .tick:last-of-type text { display: block; }
- .medium .axis text { display: none; }
- .medium .axis .tick:nth-child(2n + 1) text { display: block; }
- .large .axis text { display: none; }
- .large .axis .tick:nth-child(2n + 1) text { display: block; }
-
- </style>
- </template>
-
- <script>
- Polymer({
- is: "vz-histogram-timeseries",
- properties: {
- /**
- * Defines which view mode is being used by the chart. Supported values
- * are:
- * - "offset" - Offset view of the data showing all timesteps.
- * - "overlay" - Overlays all timesteps into one 2D view, with the
- * brighter lines representing the newer timesteps.
- */
- mode: {
- type: String,
- value: "offset"
- },
-
- /*
- * The name of the datum's property that contains the time values.
- * Allows:
- * - "step" - Linear scale using the "step" property of the datum.
- * - "wall_time" - Temporal scale using the "wall_time" property of the
- * datum.
- * - "relative" - Temporal scale starting at 0 created by using
- * the "wall_time" property of the datum.
- */
- timeProperty: {
- type: String,
- value: "step"
- },
-
- /**
- * The name of the data's property that contains the bins.
- */
- bins: {
- type: String,
- value: "bins"
- },
-
- /**
- * The name of the datum's property that contains the x values.
- */
- x: {
- type: String,
- value: "x"
- },
-
- /**
- * The name of the datum's property that contains the bin width values.
- */
- dx: {
- type: String,
- value: "dx"
- },
-
- /**
- * The name of the datum's property that contains the bin height.
- */
- y: {
- type: String,
- value: "y"
- },
-
- /**
- * Scale that maps series names to colors. The default colors are from
- * d3.scale.category10() scale. Use this property to replace the default
- * line colors with colors of your own choice.
- */
- colorScale: {
- type: Object,
- value: function() {
- return d3.scale.category10();
- }
- },
-
- /**
- * Duration of the transition between histogram modes.
- */
- modeTransitionDuration: {
- type: Number,
- value: 500
- },
-
- _attached: Boolean,
- _name: { type: String, value: null },
- _data: { type: Array, value: null },
- },
- observers: [
- 'redraw(timeProperty, _attached)',
- '_modeRedraw(mode)'
- ],
- ready: function() {
- // Polymer's way of scoping styles on nodes that d3 created
- this.scopeSubtree(this.$.svg, true);
- },
- attached: function() {
- this._attached = true;
- },
- detached: function() {
- this._attached = false;
- },
- setVisibleSeries: function(names) {
- // Do nothing.
- },
- setSeriesData: function(name, data) {
- this._name = name;
- this._data = data;
- this.redraw();
- },
-
- /**
- * Redraws the chart. This is only called if the chart is attached to the
- * screen and if the chart has data.
- */
- redraw: function() {
- this._draw(0);
- },
-
- _modeRedraw: function() {
- this._draw(this.modeTransitionDuration);
- },
-
- _draw: function(duration) {
- if (!this._attached || !this._data) {
- return;
- }
-
- //
- // Data verification
- //
- if (duration === undefined) throw(new Error("vz-histogram-timeseries _draw needs duration"));
- if (this._data.length <= 0) throw(new Error("Not enough steps in the data"));
- if (!this._data[0].hasOwnProperty(this.bins)) throw(new Error("No bins property of '" + this.bins + "' in data"));
- if (this._data[0][this.bins].length <= 0) throw(new Error("Must have at least one bin in bins in data"));
- if (!this._data[0][this.bins][0].hasOwnProperty(this.x)) throw(new Error("No x property '" + this.x + "' on bins data"));
- if (!this._data[0][this.bins][0].hasOwnProperty(this.dx)) throw(new Error("No dx property '" + this.dx + "' on bins data"));
- if (!this._data[0][this.bins][0].hasOwnProperty(this.y)) throw(new Error("No y property '" + this.y + "' on bins data"));
-
- //
- // Initialization
- //
- var timeProp = this.timeProperty;
- var xProp = this.x;
- var binsProp = this.bins;
- var dxProp = this.dx;
- var yProp = this.y;
-
- var data = this._data;
- var name = this._name;
- var mode = this.mode;
- var color = d3.hcl(this.colorScale(name));
- var tooltip = d3.select(this.$.tooltip);
-
- var xAccessor = function(d) { return d[xProp] };
- var yAccessor = function(d) { return d[yProp] };
- var dxAccessor = function(d) { return d[dxProp] };
- var xRightAccessor = function(d) { return d[xProp] + d[dxProp] };
- var timeAccessor = function(d) { return d[timeProp] };
-
- if (timeProp === "relative") {
- timeAccessor = function(d) { return d.wall_time - data[0].wall_time };
- }
-
- var brect = this.$.svg.getBoundingClientRect();
- var outerWidth = brect.width,
- outerHeight = brect.height;
-
- var sliceHeight,
- margin = {top: 5, right: 60, bottom: 20, left: 24};
-
- if (mode === "offset") {
- sliceHeight = outerHeight / 2.5;
- margin.top = sliceHeight + 5;
- } else {
- sliceHeight = outerHeight - margin.top - margin.bottom;
- }
-
- var width = outerWidth - margin.left - margin.right,
- height = outerHeight - margin.top - margin.bottom;
-
- var leftMin = d3.min(data, xAccessor),
- rightMax = d3.max(data, xRightAccessor);
-
- //
- // Text formatters
- //
- var format = d3.format(".3n");
- var yAxisFormat = d3.format(".0f");
-
- if (timeProp === "wall_time") {
- yAxisFormat = d3.time.format("%m/%d %X");
- } else if (timeProp === "relative") {
- yAxisFormat = function(d) {
- return d3.format(".1r")(d / 3.6e6) + 'h'; // Convert to hours.
- };
- }
-
- //
- // Calculate the extents
- //
- var xExtents = data.map(function(d, i) {
- return [
- d3.min(d[binsProp], xAccessor),
- d3.max(d[binsProp], xRightAccessor)
- ];
- });
- var yExtents = data.map(function(d) {
- return d3.extent(d[binsProp], yAccessor);
- });
-
- //
- // Scales and axis
- //
- var outlineCanvasSize = 500;
-
- var extent = d3.extent(data, timeAccessor);
-
- var yScale = (timeProp === "wall_time" ? d3.time.scale() : d3.scale.linear())
- .domain(extent)
- .range([0, (mode === "offset" ? height : 0)]);
-
- var ySliceScale = d3.scale.linear()
- .domain([0, d3.max(data, function(d, i) { return yExtents[i][1]; })])
- .range([sliceHeight, 0]);
-
- var yLineScale = d3.scale.linear()
- .domain(ySliceScale.domain())
- .range([outlineCanvasSize, 0]);
-
- var xScale = d3.scale.linear()
- .domain([
- d3.min(data, function(d, i) { return xExtents[i][0]; }),
- d3.max(data, function(d, i) { return xExtents[i][1]; })
- ])
- .nice()
- .range([0, width]);
-
- var xLineScale = d3.scale.linear()
- .domain(xScale.domain())
- .range([0, outlineCanvasSize]);
-
- var outlineColor = d3.scale.linear()
- .domain(d3.extent(data, timeAccessor))
- .range([color.darker(), color.brighter()])
- .interpolate(d3.interpolateHcl);
-
- var xAxis = d3.svg.axis()
- .scale(xScale)
- .ticks(Math.max(2, width / 20))
- .orient("bottom");
-
- var yAxis = d3.svg.axis()
- .scale(yScale)
- .ticks(Math.max(2, height / 15))
- .tickFormat(yAxisFormat)
- .orient("right");
-
- var ySliceAxis = d3.svg.axis()
- .scale(ySliceScale)
- .ticks(Math.max(2, height / 15))
- .tickSize(width + 5)
- .tickFormat(format)
- .orient("right");
-
- var xBinCentroid = function(d) {
- return d[xProp] + d[dxProp] / 2;
- };
-
- var linePath = d3.svg.line()
- .interpolate("linear")
- .x(function(d) { return xLineScale(xBinCentroid(d)); })
- .y(function(d) { return yLineScale(d[yProp]); });
-
- var path = function(d) {
- // Draw a line from 0 to the first point and from the last point to 0.
- return 'M' + xLineScale(xBinCentroid(d[0])) + ',' + yLineScale(0) +
- 'L' + linePath(d).slice(1) +
- "L" + xLineScale(xBinCentroid(d[d.length - 1])) + "," + yLineScale(0);
- };
-
- //
- // Render
- //
- var svgNode = this.$.svg;
-
- var svg = d3.select(svgNode)
-
- var svgTransition = svg.transition().duration(duration);
-
- var g = svg.select("g")
- .classed("small", function() { return (width > 0 && width <= 150); })
- .classed("medium", function() { return (width > 150 && width <= 300); })
- .classed("large", function() { return (width > 300); })
-
- var gTransition = svgTransition.select("g")
- .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
-
- var bisect = d3.bisector(xRightAccessor).left;
- var stage = g.select(".stage")
- .on("mouseover", function() {
- hoverUpdate.style("opacity", 1);
- xAxisHoverUpdate.style("opacity", 1);
- yAxisHoverUpdate.style("opacity", 1);
- ySliceAxisHoverUpdate.style("opacity", 1);
- tooltip.style("opacity", 1);
- })
- .on("mouseout", function() {
- hoverUpdate.style("opacity", 0);
- xAxisHoverUpdate.style("opacity", 0);
- yAxisHoverUpdate.style("opacity", 0);
- ySliceAxisHoverUpdate.style("opacity", 0);
- hoverUpdate.classed("hover-closest", false);
- outlineUpdate.classed("outline-hover", false);
- tooltip.style("opacity", 0);
- })
- .on("mousemove", onMouseMove);
-
- var background = stage.select(".background")
- .attr("transform", "translate(" + -margin.left + "," + -margin.top + ")")
- .attr("width", outerWidth)
- .attr("height", outerHeight);
-
- var histogram = stage.selectAll(".histogram").data(data),
- histogramExit = histogram.exit().remove(),
- histogramEnter = histogram.enter().append("g").attr("class", "histogram"),
- histogramUpdate = histogram
- .sort(function(a, b) { return timeAccessor(a) - timeAccessor(b); }),
- histogramTransition = gTransition.selectAll(".histogram")
- .attr("transform", function(d) {
- return "translate(0, " +
- (mode === "offset" ? (yScale(timeAccessor(d)) - sliceHeight) : 0) + ")";
- });
-
- var baselineEnter = histogramEnter.append("line").attr("class", "baseline"),
- baselineUpdate = histogramTransition.select(".baseline")
- .style("stroke-opacity", function(d) { return (mode === "offset" ? 0.1 : 0); })
- .attr("y1", sliceHeight)
- .attr("y2", sliceHeight)
- .attr("x2", width);
-
- var outlineEnter = histogramEnter.append("path").attr("class", "outline"),
- outlineUpdate = histogramUpdate.select(".outline")
- .attr("vector-effect", "non-scaling-stroke")
- .attr("d", function(d) { return path(d[binsProp]); })
- .style("stroke-width", 1),
- outlineTransition = histogramTransition.select(".outline")
- .attr("transform", "scale(" + width / outlineCanvasSize + ", " +
- sliceHeight / outlineCanvasSize + ")")
- .style("stroke", function(d) {
- return (mode === "offset" ? "white" : outlineColor(timeAccessor(d)));
- })
- .style("fill-opacity", function(d) { return (mode === "offset" ? 1 : 0); })
- .style("fill", function(d) { return outlineColor(timeAccessor(d)); });
-
- var hoverEnter = histogramEnter.append("g")
- .attr("class", "hover")
- .style("fill", function(d) { return outlineColor(timeAccessor(d)); }),
- hoverUpdate = histogramUpdate.select(".hover");
-
- hoverEnter.append("circle")
- .attr("r", 2);
-
- hoverEnter.append("text")
- .style("display", "none")
- .attr("dx", 4);
-
- var xAxisHover = g.select(".x-axis-hover").selectAll(".label").data(["x"]),
- xAxisHoverEnter = xAxisHover.enter().append("g").attr("class", "label"),
- xAxisHoverUpdate = xAxisHover;
-
- xAxisHoverEnter.append("rect")
- .attr("x", -20)
- .attr("y", 6)
- .attr("width", 40)
- .attr("height", 14)
-
- xAxisHoverEnter.append("line")
- .attr("x1", 0)
- .attr("x2", 0)
- .attr("y1", 0)
- .attr("y2", 6);
-
- xAxisHoverEnter.append("text")
- .attr("dy", 18);
-
- var yAxisHover = g.select(".y-axis-hover").selectAll(".label").data(["y"]),
- yAxisHoverEnter = yAxisHover.enter().append("g").attr("class", "label"),
- yAxisHoverUpdate = yAxisHover;
-
- yAxisHoverEnter.append("rect")
- .attr("x", 8)
- .attr("y", -6)
- .attr("width", 40)
- .attr("height", 14)
-
- yAxisHoverEnter.append("line")
- .attr("x1", 0)
- .attr("x2", 6)
- .attr("y1", 0)
- .attr("y2", 0);
-
- yAxisHoverEnter.append("text")
- .attr("dx", 8)
- .attr("dy", 4);
-
- var ySliceAxisHover = g.select(".y-slice-axis-hover").selectAll(".label").data(["y"]),
- ySliceAxisHoverEnter = ySliceAxisHover.enter().append("g").attr("class", "label"),
- ySliceAxisHoverUpdate = ySliceAxisHover;
-
- ySliceAxisHoverEnter.append("rect")
- .attr("x", 8)
- .attr("y", -6)
- .attr("width", 40)
- .attr("height", 14)
-
- ySliceAxisHoverEnter.append("line")
- .attr("x1", 0)
- .attr("x2", 6)
- .attr("y1", 0)
- .attr("y2", 0);
-
- ySliceAxisHoverEnter.append("text")
- .attr("dx", 8)
- .attr("dy", 4);
-
- gTransition.select(".y.axis.slice")
- .style("opacity", mode === "offset" ? 0 : 1)
- .attr("transform", "translate(0, " + (mode === "offset" ? -sliceHeight : 0) + ")")
- .call(ySliceAxis);
-
- gTransition.select(".x.axis")
- .attr("transform", "translate(0, " + height + ")")
- .call(xAxis);
-
- gTransition.select(".y.axis")
- .style("opacity", mode === "offset" ? 1 : 0)
- .attr("transform", "translate(" + width + ", " + (mode === "offset" ? 0 : height) + ")")
- .call(yAxis);
-
- function onMouseMove() {
- var m = d3.mouse(this),
- v = xScale.invert(m[0]),
- t = yScale.invert(m[1]);
-
- function hoverXIndex(d) {
- return Math.min(d[binsProp].length - 1, bisect(d[binsProp], v));
- }
- var closestSliceData;
- var closestSliceDistance = Infinity;
- var lastSliceData;
- hoverUpdate
- .attr("transform", function(d, i) {
- var index = hoverXIndex(d);
- lastSliceData = d;
- var x = xScale(d[binsProp][index][xProp] + d[binsProp][index][dxProp] / 2);
- var y = ySliceScale(d[binsProp][index][yProp]);
- var globalY = (mode === "offset" ? yScale(timeAccessor(d)) - (sliceHeight - y) : y);
- var dist = Math.abs(m[1] - globalY);
- if (dist < closestSliceDistance) {
- closestSliceDistance = dist;
- closestSliceData = d;
- }
- return "translate(" + x + "," + y + ")";
- });
- hoverUpdate.select("text").text(function(d) {
- var index = hoverXIndex(d);
- return d[binsProp][index][yProp];
- })
- hoverUpdate.classed("hover-closest", function(d) { return d === closestSliceData; });
- outlineUpdate.classed("outline-hover", function(d) { return d === closestSliceData; });
-
- var index = hoverXIndex(lastSliceData);
-
- xAxisHoverUpdate
- .attr("transform", function(d) {
- return "translate(" +
- xScale(lastSliceData[binsProp][index][xProp] +
- lastSliceData[binsProp][index][dxProp] / 2) + ", " +
- height + ")";
- })
- .select("text")
- .text(function(d) { return format(lastSliceData[binsProp][index][xProp] +
- lastSliceData[binsProp][index][dxProp] / 2); });
-
- var fy = yAxis.tickFormat();
- yAxisHoverUpdate
- .attr("transform", function(d) {
- return "translate(" + width + ", " +
- (mode === "offset" ? yScale(timeAccessor(closestSliceData)) : 0) + ")";
- })
- .style("display", mode === "offset" ? "" : "none")
- .select("text")
- .text(function(d) { return fy(timeAccessor(closestSliceData));});
-
- var fsy = ySliceAxis.tickFormat();
- ySliceAxisHoverUpdate
- .attr("transform", function(d) {
- return "translate(" + width + ", " +
- (mode === "offset" ? 0 : ySliceScale(closestSliceData[binsProp][index][yProp])) +
- ")";
- })
- .style("display", mode === "offset" ? "none" : "")
- .select("text")
- .text(function(d) { return fsy(closestSliceData[binsProp][index][yProp]); });
-
- var svgMouse = d3.mouse(svgNode);
- tooltip.style("transform", "translate(" + (svgMouse[0] + 15) + "px," +
- (svgMouse[1] - 15) + "px)")
- .select('span')
- .text(mode === "offset" ?
- fsy(closestSliceData[binsProp][index][yProp]) :
- (timeProp === "step" ? "step " : "") +
- fy(timeAccessor(closestSliceData)));
- }
- }
- });
- </script>
-
- </dom-module>
-
-<dom-module id="tf-histogram-dashboard" assetpath="../tf-histogram-dashboard/">
- <template>
- <div id="plumbing">
- <tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{_colorScale}}"></tf-color-scale>
- </div>
-
- <tf-dashboard-layout>
- <div class="sidebar">
- <tf-sidebar-helper backend="[[backend]]" categories="{{_categories}}" color-scale="[[_colorScale]]" run2tag="[[run2tag]]" runs="[[runs]]" selected-runs="{{_selectedRuns}}" show-download-links="{{_showDownloadLinks}}">
- <div class="sidebar-section">
- <tf-option-selector id="histogramModeSelector" name="Histogram Mode" selected-id="{{_histogramMode}}">
- <paper-button id="overlay">overlay</paper-button>
- <paper-button id="offset">offset</paper-button>
- </tf-option-selector>
- </div>
- <div class="sidebar-section">
- <tf-option-selector id="timePropertySelector" name="Offset Time Axis" selected-id="{{_timeProperty}}">
- <paper-button id="step">step</paper-button>
- <paper-button id="relative">relative</paper-button>
- <paper-button id="wall_time">wall</paper-button>
- </tf-option-selector>
-
- </div>
- </tf-sidebar-helper></div>
-
- <div class="center">
- <tf-panes-helper categories="[[_categories]]" color-scale="[[_colorScale]]" data-type="[[dataType]]" data-provider="[[dataProvider]]" data-not-found="[[dataNotFound]]" run2tag="[[run2tag]]" selected-runs="[[_selectedRuns]]" repeat-for-runs="">
- <template>
- <vz-histogram-timeseries time-property="[[_timeProperty]]" mode="[[_histogramMode]]" color-scale="[[_colorScaleFunction]]"></vz-histogram-timeseries>
- </template>
- </tf-panes-helper>
- </div>
- </tf-dashboard-layout>
-
- <style include="dashboard-style"></style>
- <style>
- tf-panes-helper {
- --card-expanded-height: 500px;
- --card-expanded-width: 700px;
- }
- </style>
- </template>
-
- <script>
- TF.Dashboard.TfHistogramDashboard = Polymer({
- is: "tf-histogram-dashboard",
- factoryImpl: function(backend) {
- this.backend = backend;
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("histograms"),
- TF.Dashboard.ReloadBehavior("tf-chart-scaffold"),
- TF.Backend.Behavior,
- ],
- properties: {
- backend: Object,
- dataType: {
- type: String,
- value: "histogram"
- },
- _histogramMode: {
- type: String,
- value: "offset"
- },
- _timeProperty: {
- type: String,
- value: "step"
- },
- _colorScaleFunction: {
- type: Function,
- computed: "_getColorScaleFunction(_colorScale)"
- },
- },
- attached: function() {
- this.async(function() {
- this.fire("rendered");
- });
- },
- _getColorScaleFunction: function() {
- return this._colorScale.scale.bind(this._colorScale);
- },
- });
- </script>
-</dom-module>
-<link rel="import" href="../paper-spinner/paper-spinner-lite.html">
-
-<dom-module id="tf-image-loader" assetpath="../tf-image-dashboard/">
- <template>
- <div id="image-annotation">
- <template is="dom-if" if="[[_hasAtLeastOneStep]]">
- step
- <span class="step-value">
- [[_stepValue]]
- </span>
- <template is="dom-if" if="[[_currentWallTime]]">
- ([[_currentWallTime]])
- </template>
- <paper-spinner-lite active="" hidden$="[[!_isImageLoading]]"></paper-spinner-lite>
- </template>
- <template is="dom-if" if="[[_hasMultipleSteps]]">
- <paper-slider id="steps" immediate-value="{{_stepIndex}}" max="[[_maxStepIndex]]" max-markers="[[_maxStepIndex]]" snaps="" step="1" value="{{_stepIndex}}"></paper-slider>
- </template>
- </div>
-
- <div id="main-image-container"></div>
-
- <style>
- :host {
- display: block;
- width: 100%;
- height: auto;
- position: relative;
- --step-slider-knob-color: #424242;
- }
-
- #image-annotation {
- border-left: 4px solid;
- padding-left: 5px;
- font-size: 12px;
- margin: -10px 0 10px 0;
- }
-
- #image-annotation .step-value {
- font-weight: bold;
- }
-
- #image-annotation paper-spinner-lite {
- width: 14px;
- height: 14px;
- vertical-align: text-bottom;
- --paper-spinner-color: var(--tb-orange-strong)
- }
-
- #steps {
- height: 15px;
- margin: 0 0 0 -15px;
- /* 31 comes from adding a padding of 15px from both sides of the paper-slider, subtracting
- * 1px so that the slider width aligns with the image (the last slider marker takes up 1px),
- * and adding 2px to account for a border of 1px on both sides of the image. 30 - 1 + 2. */
- width: calc(100% + 31px);
- --paper-slider-active-color: var(--step-slider-knob-color);
- --paper-slider-knob-color: var(--step-slider-knob-color);
- --paper-slider-pin-color: var(--step-slider-knob-color);
- --paper-slider-knob-start-color: var(--step-slider-knob-color);
- --paper-slider-knob-start-border-color: var(--step-slider-knob-color);
- --paper-slider-pin-start-color: var(--step-slider-knob-color);
- }
-
- #main-image-container img {
- border: 1px solid #f5f5f5;
- image-rendering: -moz-crisp-edges;
- image-rendering: pixelated;
- display: block;
- width: 100%;
- height: auto;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-image-loader",
- properties: {
- colorScale: Object,
- run: String,
- // This is an array of Tensorboard Image&Datum objects (See backend.ts for details). The
- // properties of objects in this array are
- // {
- // width: number,
- // height: number,
- // wall_time: Date,
- // step: number,
- // url: string,
- // }
- _steps: {
- type: Array,
- value: [],
- notify: true,
- },
- _stepIndex: {
- type: Number,
- notify: true,
- },
- _hasAtLeastOneStep: {
- type: Boolean,
- computed: "_computeHasAtLeastOneStep(_steps)",
- },
- _hasMultipleSteps: {
- type: Boolean,
- computed: "_computeHasMultipleSteps(_steps)",
- },
- _stepValue: {
- type: Number,
- computed: "_computeStepValue(_stepIndex)",
- },
- _currentWallTime: {
- type: Number,
- computed: "_computeCurrentWallTime(_stepIndex)",
- },
- _maxStepIndex: {
- type: Number,
- computed: "_computeMaxStepIndex(_steps)",
- },
- // We use a strictly increasing index to make sure that we don't settle on a stale image.
- _currentImageLoadIndex: {
- type: Number,
- value: 1,
- },
- _isImageLoading: {
- type: Boolean,
- value: false,
- },
- },
- observers: [
- "_updateImageUrl(_steps, _stepIndex)",
- ],
- redraw: function() {
- // Other dashboards logic requires a redraw method to be defined. redraw is called at
- // various places such as when the image is expanded.
- this.setSeriesData(this.run, this._steps);
- },
- setVisibleSeries: function(runs) {
- // Do nothing.
- },
- setSeriesData: function(run, steps) {
- this.set("run", run);
- this.set("_steps", steps);
- this.set("_stepIndex", steps.length - 1);
-
- // Update the border color based on the run.
- var color = this.colorScale.scale(run);
- this.$$("#image-annotation").style.borderColor = color;
- },
- _updateImageUrl: function(steps, stepIndex) {
- // We manually change the image URL (instead of binding to the image's src attribute)
- // because we would like to manage what happens when the image starts to / finishes loading.
- if (!steps.length) {
- return;
- }
-
- let img = new Image();
- img.id = "img"; // '#img' used to select the image in tf-image-dashboard.
-
- const loadIndex = ++this._currentImageLoadIndex;
- img.onload = img.onerror = (function() {
- if (loadIndex != this._currentImageLoadIndex) {
- // This load is no longer relevant.
- return;
- }
-
- // The new image has finished loading. Remove the old image. Add the new one.
- let mainImageContainer = this.$$("#main-image-container");
- mainImageContainer.innerHTML = "";
- Polymer.dom(mainImageContainer).appendChild(img);
-
- // The image has finished loading (or has erred and failed to load).
- this.set("_isImageLoading", false);
- }).bind(this);
-
- // Load the new image.
- this.set("_isImageLoading", true);
- img.src = steps[stepIndex].url;
- },
- _computeHasAtLeastOneStep: function(steps) {
- return !!steps && steps.length > 0;
- },
- _computeHasMultipleSteps: function(steps) {
- return !!steps && steps.length > 1;
- },
- _computeStepValue: function(stepIndex) {
- return this._steps[stepIndex].step;
- },
- _computeCurrentWallTime: function(stepIndex) {
- return this._steps[stepIndex].wall_time.toString();
- },
- _computeMaxStepIndex: function(steps) {
- return steps.length - 1;
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-image-dashboard" assetpath="../tf-image-dashboard/">
- <template>
- <paper-dialog with-backdrop="" id="actual-image-size-dialog"></paper-dialog>
- <div id="plumbing">
- <tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{_colorScale}}"></tf-color-scale>
- </div>
-
- <tf-dashboard-layout>
- <div class="sidebar">
- <tf-sidebar-helper backend="[[backend]]" categories="{{_categories}}" color-scale="[[_colorScale]]" run2tag="[[run2tag]]" runs="[[runs]]" selected-runs="{{_selectedRuns}}">
- </tf-sidebar-helper>
- </div>
- <div class="center">
- <tf-panes-helper categories="[[_categories]]" color-scale="[[_colorScale]]" data-type="[[dataType]]" data-provider="[[dataProvider]]" data-not-found="[[dataNotFound]]" run2tag="[[run2tag]]" selected-runs="[[_selectedRuns]]" repeat-for-runs="">
- <template>
- <tf-image-loader color-scale="[[_colorScale]]"></tf-image-loader>
- <paper-icon-button class="actual-size-button" icon="aspect-ratio" on-tap="_showActualSize" title="Show the image at its true pixel size"></paper-icon-button>
- </template>
- </tf-panes-helper>
- </div>
- </tf-dashboard-layout>
- <style include="dashboard-style"></style>
- <style>
- tf-panes-helper {
- --card-width: 340px;
- --card-height: auto;
- --card-expanded-width: 700px;
- --card-expanded-height: auto;
- }
-
- .actual-size-button {
- background: #fff;
- border-radius: 100%;
- bottom: -35px;
- color: #2196f3;
- height: 32px;
- left: 35px;
- padding: 4px;
- pointer-events: auto;
- position: absolute;
- width: 32px;
- }
-
- .actual-size-button-selected {
- background: var(--tb-ui-light-accent);
- }
-
- #actual-image-size-dialog {
- overflow: auto;
- }
- </style>
- </template>
- <script>
- TF.Dashboard.TfImageDashboard = Polymer({
- is: "tf-image-dashboard",
- factoryImpl: function(backend) {
- this.backend = backend;
- },
- properties: {
- backend: Object,
- dataType: {
- type: String,
- value: "image"
- },
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("images"),
- TF.Dashboard.ReloadBehavior("tf-chart-scaffold"),
- TF.Backend.Behavior,
- ],
- attached: function() {
- this.async(function() {
- this.fire("rendered");
- });
- },
- _showActualSize: function(e) {
- var currentTarget = Polymer.dom(e.currentTarget);
- var card = currentTarget.node.closest('.card');
-
- // Create a full-size copy of the image.
- var newImage = card.querySelector('#img').cloneNode();
- newImage.style.height = 'auto';
- newImage.style.width = 'auto';
- newImage.style.margin = 0;
- newImage.style.padding = 0;
- newImage.classList.add("actual-size-image");
-
- // When the user clicks on the image, empty and close the dialog.
- var dialog = this.$$('#actual-image-size-dialog');
- newImage.addEventListener('click', function() {
- dialog.close();
- });
-
- // Update dialog content. Show the dialog.
- dialog.innerHTML = '';
- dialog.appendChild(newImage);
- dialog.open();
- }
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-audio-loader" assetpath="../tf-audio-dashboard/">
- <style>
- :host {
- display: block;
- --step-slider-knob-color: #424242;
- }
-
- img {
- width: 100%;
- height: 100%;
- image-rendering: pixelated;
- }
-
- .step-description {
- font-size: 12px;
- }
-
- .step-value {
- font-weight: bold;
- }
-
- #audio-loading-spinner {
- width: 14px;
- height: 14px;
- vertical-align: text-bottom;
- --paper-spinner-color: var(--tb-orange-strong)
- }
-
- #steps {
- height: 15px;
- margin: 0 0 0 -15px;
- /* 31 comes from adding a padding of 15px from both sides of the paper-slider, subtracting
- * 1px so that the slider width aligns with the image (the last slider marker takes up 1px),
- * and adding 2px to account for a border of 1px on both sides of the image. 30 - 1 + 2. */
- width: calc(100% + 31px);
- --paper-slider-active-color: var(--step-slider-knob-color);
- --paper-slider-knob-color: var(--step-slider-knob-color);
- --paper-slider-pin-color: var(--step-slider-knob-color);
- --paper-slider-knob-start-color: var(--step-slider-knob-color);
- --paper-slider-knob-start-border-color: var(--step-slider-knob-color);
- --paper-slider-pin-start-color: var(--step-slider-knob-color);
- }
-
- #individual-audio-container audio {
- margin: 5px 0 0 -10px;
- width: calc(100% + 20px);
- }
- </style>
- <template>
- <template is="dom-if" if="[[_metadatas]]">
- <template is="dom-if" if="[[_hasAtLeastOneStep(_metadatas)]]">
- <div class="step-description">
- step
- <span class="step-value">
- [[_stepValue]]
- </span><br>
- <template is="dom-if" if="[[_stepWallTime]]">
- [[_stepWallTime]]
- </template>
- <paper-spinner-lite active="" id="audio-loading-spinner" hidden$="[[!_isAudioLoading]]"></paper-spinner-lite>
- </div>
- </template>
- <template is="dom-if" if="[[_maxStepIndex]]">
- <paper-slider id="steps" immediate-value="{{_stepIndex}}" max="[[_maxStepIndex]]" max-markers="[[_maxStepIndex]]" snaps="" step="1" value="{{_stepIndex}}"></paper-slider>
- </template>
- <div id="individual-audio-container"></div>
- </template>
- </template>
- <script>
- Polymer({
- is: "tf-audio-loader",
- properties: {
- run: String,
- tag: String,
- audioGenerator: Function,
- // todo: document.
- _metadatas: Array,
- _stepIndex: Number,
- _stepValue: {
- type: Number,
- computed: "_computeStepValue(_metadatas, _stepIndex)",
- value: 0,
- },
- _stepWallTime: {
- type: Number,
- computed: "_computeStepWallTime(_metadatas, _stepIndex)",
- value: 0,
- },
- _maxStepIndex: {
- type: Number,
- computed: "_computeMaxStepIndex(_metadatas)",
- value: 0,
- },
- _isAudioLoading: Boolean,
- // Used to identify stale requests for audio.
- _audioRequestId: {
- type: Number,
- value: 1
- },
- },
- observers: [
- "_updateAudio(_metadatas, _stepIndex)",
- ],
- reload: function() {
- this.audioGenerator(this.tag, this.run).then(function(metadatas) {
- // Set the list of available metadata.
- this.set("_metadatas", metadatas);
-
- // Set the index to be the last one.
- this.set("_stepIndex", this._maxStepIndex);
- }.bind(this));
- },
- ready: function() {
- // Need to test so that it will not error if it is constructed w/o
- // all properties (so that it's possible to use stub to mock it out)
- if (this.run != null && this.tag != null && this.audioGenerator != null) {
- this.reload();
- }
- },
- _updateAudio: function(metadatas, stepIndex) {
- if (!metadatas || stepIndex >= metadatas.length) {
- // No audio to show. The audio section should be hidden.
- return;
- }
-
- // Load new audio.
- const requestId = ++this._audioRequestId;
- this.set("_isAudioLoading", true);
-
- // Create a new audio element. Only replace the previous one once the new audio loads.
- let audioElement = document.createElement("audio");
- audioElement.setAttribute("controls", true);
- audioElement.setAttribute("loop", "loop");
- let canPlayHandler = function() {
- if (requestId !== this._audioRequestId) {
- // This request is no longer relevant.
- return;
- }
-
- // Remove this event listener: "canplay" apparently fires in Chrome every time playing
- // begins again on loop. So, if we create a new audio element every time that happens, we
- // don't actually loop.
- audioElement.removeEventListener("canplay", canPlayHandler);
-
- let individualAudioContainer = this.$$("#individual-audio-container");
- individualAudioContainer.innerHTML = "";
- Polymer.dom(individualAudioContainer).appendChild(audioElement);
- this.set("_isAudioLoading", false);
- }.bind(this);
- audioElement.addEventListener("canplay", canPlayHandler);
- audioElement.addEventListener("error", function() {
- if (requestId !== this._audioRequestId) {
- // This request is no longer relevant.
- return;
- }
-
- // The audio could not be loaded.
- this.$$("#individual-audio-container").innerHTML = "";
- this.set("_isAudioLoading", false);
- }.bind(this));
-
- // Initiate the request for new audio.
- var sourceElement = document.createElement("source");
- let metadata = metadatas[stepIndex];
- sourceElement.setAttribute("src", metadata.url);
- sourceElement.setAttribute("type", metadata.content_type);
- audioElement.appendChild(sourceElement);
- },
- _computeStepValue: function(metadatas, stepIndex) {
- if (!metadatas || stepIndex >= metadatas.length) {
- // No audio to show. The audio section should be hidden.
- return 0;
- }
- return metadatas[stepIndex].step;
- },
- _computeStepWallTime: function(metadatas, stepIndex) {
- if (!metadatas || stepIndex >= metadatas.length) {
- // No audio to show. The audio section should be hidden.
- return 0;
- }
- return metadatas[stepIndex].wall_time.toString();
- },
- _computeMaxStepIndex: function(metadatas) {
- if (!metadatas || metadatas.length === 0) {
- // No audio to show. The audio section should be hidden.
- return 0;
- }
- return metadatas.length - 1;
- },
- _hasAtLeastOneStep: function(metadatas) {
- return metadatas && metadatas.length > 0;
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-audio-grid" assetpath="../tf-audio-dashboard/">
- <template>
- <style include="scrollbar-style"></style>
- <div id="fullContainer" class="container scrollbar">
- <div id="topRow" class="container">
- <div class="noshrink" id="paddingCell"></div>
- <template is="dom-if" if="[[_tagsExist(tags)]]">
- <template is="dom-repeat" items="[[runs]]" as="run">
- <div class="run-name-cell noshrink">
- <span>[[run]]</span>
- </div>
- </template>
- </template>
- </div>
- <div id="bottomContainer" class="container">
- <template is="dom-repeat" items="[[tags]]" as="tag">
- <div class="audio-row container noshrink">
- <div class="tag-name-cell noshrink">
- <span class="tag-name">[[tag]]</span>
- </div>
- <template is="dom-repeat" items="[[runs]]" as="run">
- <div class="audio-cell noshrink">
- <template is="dom-if" if="[[_exists(run, tag, runToAudio.*)]]">
- <tf-audio-loader id="loader" run="[[run]]" tag="[[tag]]" audio-generator="[[audioGenerator]]">
- </tf-audio-loader>
- </template>
- </div>
- </template>
- </div>
- </template>
- </div>
- </div>
- <style>
- :host {
- display: block;
- height: 100%;
- --audio-cell-min-height: 105px;
- }
- .container {
- display: flex;
- flex-wrap: nowrap;
- }
- #fullContainer {
- width: 100%;
- height: 100%;
- flex-direction: column;
- padding-top: 20px;
- overflow: auto;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- #topRow {
- flex-direction: row;
- }
- #bottomContainer {
- flex-direction: column;
- height: 100%;
- width: 100%;
- }
- .audio-row {
- flex-direction: row;
- padding-top: 5px;
- }
- .audio-cell {
- background: #FAFAFA;
- width: 300px;
- min-height: var(--audio-cell-min-height);
- border: 1px solid black;
- margin-right: 3px;
- padding: 10px;
- box-sizing: border-box;
- }
- .tag-name-cell {
- width: 300px;
- height: var(--audio-cell-min-height);
- display:flex;
- flex-direction: column;
- justify-content: center;
- }
- .tag-name {
- word-wrap: break-word;
- text-align: center;
- white-space: nowrap;
- }
- .run-name-cell {
- width: 300px;
- text-align: center;
- margin-right: 5px;
- }
- .noshrink {
- flex-shrink: 0;
- }
- #paddingCell {
- width: 300px;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-audio-grid",
- properties: {
- runToAudio: Object,
- tags: Array,
- runs: Array,
- audioGenerator: Function,
- },
- _tagsExist: function(tags) {
- return tags && tags.length > 0;
- },
- _exists: function (run, tag) {
- return this.runToAudio[run].indexOf(tag) !== -1;
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-audio-dashboard" assetpath="../tf-audio-dashboard/">
- <template>
- <div class="center">
- <tf-no-data-warning data-type="audio" show-warning="[[dataNotFound]]"></tf-no-data-warning>
- <tf-audio-grid id="audioGrid" run-to-audio="[[run2tag]]" audio-generator="[[dataProvider]]" tags="[[tags]]" runs="[[runs]]"></tf-audio-grid>
- </div>
-
- <style>
- .center {
- height: 100%;
- width: 100%;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- }
- :host {
- height: 100%;
- display: block;
- }
-
- </style>
- </template>
- <script>
- TF.Dashboard.TfAudioDashboard = Polymer({
- is: "tf-audio-dashboard",
- factoryImpl: function(backend) {
- this.backend = backend;
- },
- properties: {
- dataType: {value: "audio"},
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("audio"),
- TF.Dashboard.ReloadBehavior("tf-audio-loader"),
- TF.Backend.Behavior
- ],
- attached: function() {
- this.async(function() {
- this.fire("rendered");
- });
- },
- _hasAudio: function(runToAudioChange) {
- return _.values(runToAudioChange.base).some(function(arr) {
- return arr.length > 0;
- });
- },
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-graph-loader" assetpath="../tf-graph-loader/">
-</dom-module>
-
-<script>
-Polymer({
-
- is: 'tf-graph-loader',
-
- properties: {
- /**
- * @type {value: number, msg: string}
- *
- * A number between 0 and 100 denoting the % of progress
- * for the progress bar and the displayed message.
- */
- progress: {
- type: Object,
- notify: true,
- },
- datasets: Array,
- selectedDataset: Number,
- selectedFile: {
- type: Object,
- observer: '_selectedFileChanged'
- },
- outGraphHierarchy: {
- type: Object,
- readOnly: true, //readonly so outsider can't change this via binding
- notify: true
- },
- outGraph: {
- type: Object,
- readOnly: true, //readonly so outsider can't change this via binding
- notify: true
- },
- outHierarchyParams: {
- type: Object,
- readOnly: true,
- notify: true
- },
- outStats: {
- type: Object,
- readOnly: true, // This property produces data.
- notify: true
- }
- },
- observers: [
- '_selectedDatasetChanged(selectedDataset, datasets)',
- '_readAndParseMetadata(selectedMetadataTag)'
- ],
- _readAndParseMetadata: function(metadataIndex) {
- if (metadataIndex == -1 || this.datasets[this.selectedDataset] == null ||
- this.datasets[this.selectedDataset].runMetadata == null ||
- this.datasets[this.selectedDataset].runMetadata[metadataIndex] == null) {
- this._setOutStats(null);
- return;
- }
- var path = this.datasets[this.selectedDataset].runMetadata[metadataIndex].path;
- // Reset the progress bar to 0.
- this.set('progress', {
- value: 0,
- msg: ''
- });
- var tracker = tf.graph.util.getTracker(this);
- tf.graph.parser.fetchAndParseMetadata(path, tracker)
- .then(function(stats) {
- this._setOutStats(stats);
- }.bind(this));
- },
- _parseAndConstructHierarchicalGraph: function(path, pbTxtFile) {
- // Reset the progress bar to 0.
- this.set('progress', {
- value: 0,
- msg: ''
- });
- var tracker = tf.graph.util.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 dataTracker = tf.graph.util.getSubtaskTracker(tracker, 30, 'Data');
- tf.graph.parser.fetchAndParseGraphData(path, pbTxtFile, dataTracker)
- .then(function(graph) {
- if (!graph) {
- throw 'The graph is empty. Make sure that the graph is passed to the ' +
- 'SummaryWriter after the graph is defined.';
- }
-
- // Build the flat graph (consists only of Op nodes).
-
- // This is the whitelist of inputs on op types that are considered
- // reference edges. "Assign 0" indicates that the first input to
- // an OpNode with operation type "Assign" is a reference edge.
- var refEdges = {};
- refEdges["Assign 0"] = true;
- refEdges["AssignAdd 0"] = true;
- refEdges["AssignSub 0"] = true;
- refEdges["assign 0"] = true;
- refEdges["assign_add 0"] = true;
- refEdges["assign_sub 0"] = true;
- refEdges["count_up_to 0"] = true;
- refEdges["ScatterAdd 0"] = true;
- refEdges["ScatterSub 0"] = true;
- refEdges["ScatterUpdate 0"] = true;
- refEdges["scatter_add 0"] = true;
- refEdges["scatter_sub 0"] = true;
- refEdges["scatter_update 0"] = true;
- var buildParams = {
- enableEmbedding: true,
- inEmbeddingTypes: ['Const'],
- outEmbeddingTypes: ['^[a-zA-Z]+Summary$'],
- refEdges: refEdges
- };
- var graphTracker = tf.graph.util.getSubtaskTracker(tracker, 20, 'Graph');
- return tf.graph.build(graph, buildParams, graphTracker);
- })
- .then(function(graph) {
- this._setOutGraph(graph);
- var hierarchyTracker = tf.graph.util.getSubtaskTracker(tracker, 50,
- 'Namespace hierarchy');
- return tf.graph.hierarchy.build(graph, hierarchyParams, hierarchyTracker);
- }.bind(this))
- .then(function(graphHierarchy) {
- // Update the properties which notify the parent with the
- // graph hierarchy and whether the data has live stats or not.
- this._setOutGraphHierarchy(graphHierarchy);
- }.bind(this))
- .catch(function(e) {
- // Generic error catch, for errors that happened outside
- // asynchronous tasks.
- tracker.reportError("Graph visualization failed: " + e, e);
- });
- },
- _selectedDatasetChanged: function(datasetIndex, datasets) {
- this._parseAndConstructHierarchicalGraph(datasets[datasetIndex].path);
- },
- _selectedFileChanged: function(e) {
- if (!e) {
- return;
- }
- var file = e.target.files[0];
- if (!file) {
- return;
- }
-
- // Clear out the value of the file chooser. This ensures that if the user
- // selects the same file, we'll re-read it.
- e.target.value = '';
-
- this._parseAndConstructHierarchicalGraph(null, file);
- }
-});
-</script>
-<script src="../graphlib/dist/graphlib.core.js"></script>
-<script src="../dagre/dist/dagre.core.js"></script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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) {
- /**
- * Mapping from color palette name to color palette, which contains
- * exact colors for multiple states of a single color palette.
- */
- tf.COLORS = [
- {
- 'name': 'Google Blue',
- 'color': '#4184f3',
- 'active': '#3a53c5',
- 'disabled': '#cad8fc'
- },
- {
- 'name': 'Google Red',
- 'color': '#db4437',
- 'active': '#8f2a0c',
- 'disabled': '#e8c6c1'
- },
- {
- 'name': 'Google Yellow',
- 'color': '#f4b400',
- 'active': '#db9200',
- 'disabled': '#f7e8b0'
- },
- {
- 'name': 'Google Green',
- 'color': '#0f9d58',
- 'active': '#488046',
- 'disabled': '#c2e1cc'
- },
- {
- 'name': 'Purple',
- 'color': '#aa46bb',
- 'active': '#5c1398',
- 'disabled': '#d7bce6'
- },
- {
- 'name': 'Teal',
- 'color': '#00abc0',
- 'active': '#47828e',
- 'disabled': '#c2eaf2'
- },
- {
- 'name': 'Deep Orange',
- 'color': '#ff6f42',
- 'active': '#ca4a06',
- 'disabled': '#f2cbba'
- },
- {
- 'name': 'Lime',
- 'color': '#9d9c23',
- 'active': '#7f771d',
- 'disabled': '#f1f4c2'
- },
- {
- 'name': 'Indigo',
- 'color': '#5b6abf',
- 'active': '#3e47a9',
- 'disabled': '#c5c8e8'
- },
- {
- 'name': 'Pink',
- 'color': '#ef6191',
- 'active': '#ca1c60',
- 'disabled': '#e9b9ce'
- },
- {
- 'name': 'Deep Teal',
- 'color': '#00786a',
- 'active': '#2b4f43',
- 'disabled': '#bededa'
- },
- {
- 'name': 'Deep Pink',
- 'color': '#c1175a',
- 'active': '#75084f',
- 'disabled': '#de8cae'
- },
- {
- 'name': 'Gray',
- 'color': '#9E9E9E',
- 'active': '#424242',
- 'disabled': 'F5F5F5' // 100
- }
- ].reduce(function (m, c) {
- m[c.name] = c;
- return m;
- }, {});
- /**
- * Mapping from op category to color palette name
- * e.g., OP_GROUP_COLORS['state_ops'] = 'Google Blue';
- */
- tf.OP_GROUP_COLORS = [
- {
- color: 'Google Red',
- groups: [
- 'gen_legacy_ops', 'legacy_ops', 'legacy_flogs_input',
- 'legacy_image_input', 'legacy_input_example_input',
- 'legacy_sequence_input', 'legacy_seti_input_input'
- ]
- },
- { color: 'Deep Orange', groups: ['constant_ops'] },
- { color: 'Indigo', groups: ['state_ops'] },
- { color: 'Purple', groups: ['nn_ops', 'nn'] },
- { color: 'Google Green', groups: ['math_ops'] },
- { color: 'Lime', groups: ['array_ops'] },
- { color: 'Teal', groups: ['control_flow_ops', 'data_flow_ops'] },
- { color: 'Pink', groups: ['summary_ops'] },
- { color: 'Deep Pink', groups: ['io_ops'] }
- ].reduce(function (m, c) {
- c.groups.forEach(function (group) { m[group] = c.color; });
- return m;
- }, {});
-})(tf || (tf = {}));
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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.
-==============================================================================*/
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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.
-==============================================================================*/
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph_1) {
- /** 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 = '--';
- var GraphType;
- (function (GraphType) {
- GraphType[GraphType["FULL"] = 0] = "FULL";
- GraphType[GraphType["EMBEDDED"] = 1] = "EMBEDDED";
- GraphType[GraphType["META"] = 2] = "META";
- GraphType[GraphType["SERIES"] = 3] = "SERIES";
- GraphType[GraphType["CORE"] = 4] = "CORE";
- GraphType[GraphType["SHADOW"] = 5] = "SHADOW";
- GraphType[GraphType["BRIDGE"] = 6] = "BRIDGE";
- GraphType[GraphType["EDGE"] = 7] = "EDGE";
- })(GraphType = graph_1.GraphType || (graph_1.GraphType = {}));
- ;
- var NodeType;
- (function (NodeType) {
- NodeType[NodeType["META"] = 0] = "META";
- NodeType[NodeType["OP"] = 1] = "OP";
- NodeType[NodeType["SERIES"] = 2] = "SERIES";
- NodeType[NodeType["BRIDGE"] = 3] = "BRIDGE";
- NodeType[NodeType["ELLIPSIS"] = 4] = "ELLIPSIS";
- })(NodeType = graph_1.NodeType || (graph_1.NodeType = {}));
- ;
- /** Indicates if a node is to be included in the main graph when rendered. */
- var InclusionType;
- (function (InclusionType) {
- InclusionType[InclusionType["INCLUDE"] = 0] = "INCLUDE";
- InclusionType[InclusionType["EXCLUDE"] = 1] = "EXCLUDE";
- InclusionType[InclusionType["UNSPECIFIED"] = 2] = "UNSPECIFIED";
- })(InclusionType = graph_1.InclusionType || (graph_1.InclusionType = {}));
- ;
- /** Indicates if a series is to be grouped in the graph when rendered. */
- var SeriesGroupingType;
- (function (SeriesGroupingType) {
- SeriesGroupingType[SeriesGroupingType["GROUP"] = 0] = "GROUP";
- SeriesGroupingType[SeriesGroupingType["UNGROUP"] = 1] = "UNGROUP";
- })(SeriesGroupingType = graph_1.SeriesGroupingType || (graph_1.SeriesGroupingType = {}));
- ;
- /** Attribute key reserved for the shapes of the output tensors. */
- var OUTPUT_SHAPES_KEY = '_output_shapes';
- /** Attribute key reserved for the XLA cluster that an op runs on. */
- var _XLA_CLUSTER_KEY = '_XlaCluster';
- /**
- * A SlimGraph is inspired by graphlib.Graph, but having only the functionality
- * that we need.
- */
- var SlimGraph = (function () {
- function SlimGraph() {
- this.nodes = {};
- this.edges = [];
- }
- return SlimGraph;
- }());
- graph_1.SlimGraph = SlimGraph;
- var EllipsisNodeImpl = (function () {
- /**
- * Constructs a new ellipsis annotation node.
- *
- * @param numNodes The number of additional annotations this node represents.
- */
- function EllipsisNodeImpl(numNodes) {
- this.type = NodeType.ELLIPSIS;
- this.isGroupNode = false;
- this.cardinality = 1;
- this.parentNode = null;
- this.stats = null;
- this.setNumMoreNodes(numNodes);
- this.include = InclusionType.UNSPECIFIED;
- }
- EllipsisNodeImpl.prototype.setNumMoreNodes = function (numNodes) {
- this.numMoreNodes = numNodes;
- this.name = '... ' + numNodes + ' more';
- };
- return EllipsisNodeImpl;
- }());
- graph_1.EllipsisNodeImpl = EllipsisNodeImpl;
- ;
- /**
- * A label object for nodes in the full graph and leaf nodes in the render
- * graph.
- */
- var OpNodeImpl = (function () {
- /**
- * Constructs a new Op node.
- *
- * @param rawNode The raw node.
- */
- function OpNodeImpl(rawNode) {
- this.op = rawNode.op;
- this.name = rawNode.name;
- this.device = rawNode.device;
- this.attr = rawNode.attr;
- // 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);
- this.xlaCluster = extractXlaCluster(rawNode.attr);
- // additional properties
- this.type = NodeType.OP;
- this.isGroupNode = false;
- this.cardinality = 1;
- this.inEmbeddings = [];
- this.outEmbeddings = [];
- this.parentNode = null;
- this.include = InclusionType.UNSPECIFIED;
- this.owningSeries = null;
- }
- return OpNodeImpl;
- }());
- graph_1.OpNodeImpl = OpNodeImpl;
- ;
- function createMetanode(name, opt) {
- if (opt === void 0) { opt = {}; }
- return new MetanodeImpl(name, opt);
- }
- graph_1.createMetanode = createMetanode;
- /**
- * Joins the information from the stats file (memory, compute time) with the
- * graph information.
- */
- function joinStatsInfoWithGraph(graph, stats, devicesForStats) {
- // Reset stats for each node.
- _.each(graph.nodes, function (node) { node.stats = null; });
- _.each(stats.dev_stats, function (devStats) {
- // Ignore devices that are not selected.
- if (devicesForStats && !devicesForStats[devStats.device]) {
- return;
- }
- _.each(devStats.node_stats, function (nodeStats) {
- // Lookup the node in the graph by its original name, e.g. A. If not
- // found, lookup by the rewritten name A/(A) in case the name is both
- // a namespace and a node name.
- var nodeName = nodeStats.node_name in graph.nodes ? nodeStats.node_name :
- nodeStats.node_name +
- graph_1.NAMESPACE_DELIM + '(' + nodeStats.node_name + ')';
- // Couldn't find a matching node.
- if (!(nodeName in graph.nodes)) {
- return;
- }
- // Compute the total bytes used.
- var totalBytes = 0;
- if (nodeStats.memory) {
- _.each(nodeStats.memory, function (alloc) {
- if (alloc.total_bytes) {
- if (alloc.total_bytes > 0) {
- totalBytes += Number(alloc.total_bytes);
- }
- else {
- /* tslint:disable */
- console.log('ignoring negative memory allocation for ' + nodeName);
- /* tslint:enable */
- }
- }
- });
- }
- var outputSize = null;
- if (nodeStats.output) {
- outputSize = _.map(nodeStats.output, function (output) {
- return _.map(output.tensor_description.shape.dim, function (dim) { return Number(dim.size); });
- });
- }
- graph.nodes[nodeName].device = devStats.device;
- if (graph.nodes[nodeName].stats == null) {
- graph.nodes[nodeName].stats = new NodeStats(outputSize);
- }
- graph.nodes[nodeName].stats.addBytesAllocation(totalBytes);
- if (nodeStats.all_end_rel_micros) {
- if (nodeStats.all_end_rel_micros > 0) {
- graph.nodes[nodeName].stats.addExecutionTime(nodeStats.all_start_micros, nodeStats.all_start_micros + nodeStats.all_end_rel_micros);
- }
- else {
- /* tslint:disable */
- console.log('ignoring negative runtime for ' + nodeName);
- /* tslint:enable */
- }
- }
- });
- });
- }
- graph_1.joinStatsInfoWithGraph = joinStatsInfoWithGraph;
- /**
- * Execution stats for the node.
- */
- var NodeStats = (function () {
- function NodeStats(outputSize) {
- /**
- * Total number of bytes used for the node. Sum of all children
- * if it is a Group node.
- */
- this.totalBytes = 0;
- this.outputSize = outputSize;
- }
- /**
- * Add the start and end time for a particular kernel execution of this op.
- * Ops can have multiple kernel executions within the same session run.
- */
- NodeStats.prototype.addExecutionTime = function (startTime, endTime) {
- if (this.startTime != null) {
- this.startTime = Math.min(this.startTime, startTime);
- }
- else {
- this.startTime = startTime;
- }
- if (this.endTime != null) {
- this.endTime = Math.max(this.endTime, endTime);
- }
- else {
- this.endTime = endTime;
- }
- };
- /**
- * Add the bytes allocated for a particular kernel execution of this op.
- * Ops can have multiple kernel executions within the same session run.
- */
- NodeStats.prototype.addBytesAllocation = function (totalBytes) {
- if (this.totalBytes != null) {
- this.totalBytes = Math.max(this.totalBytes, totalBytes);
- }
- else {
- this.totalBytes = totalBytes;
- }
- };
- Object.defineProperty(NodeStats.prototype, "totalMicros", {
- /**
- * Total number of compute time in microseconds used for the node.
- * Sum of all children if it is a Group node. Null if it is unknown.
- */
- get: function () {
- if (this.startTime == null || this.endTime == null) {
- return null;
- }
- return this.endTime - this.startTime;
- },
- enumerable: true,
- configurable: true
- });
- /**
- * Combines the specified stats with the current stats.
- * Modifies the current object. This method is used to
- * compute aggregate stats for group nodes.
- */
- NodeStats.prototype.combine = function (stats) {
- if (stats.totalBytes != null) {
- this.totalBytes += stats.totalBytes;
- }
- if (stats.totalMicros != null) {
- this.addExecutionTime(stats.startTime, stats.endTime);
- }
- };
- return NodeStats;
- }());
- graph_1.NodeStats = NodeStats;
- var MetanodeImpl = (function () {
- /** A label object for meta-nodes in the graph hierarchy */
- function MetanodeImpl(name, opt) {
- if (opt === void 0) { opt = {}; }
- this.name = name;
- this.type = NodeType.META;
- /** number of levels under this group */
- this.depth = 1;
- this.isGroupNode = true;
- /** # of leaf nodes (including embedded ones) */
- this.cardinality = 0;
- /** graph contains metanodes, nodes, edges
- * and metaedges for main items within this metanode
- */
- this.metagraph =
- createGraph(name, GraphType.META, opt);
- /** bridgegraph must be constructed lazily-see hierarchy.getBridgegraph() */
- this.bridgegraph = null;
- /**
- * A dictionary that count ops type of nodes in this metanode
- * (op type => count).
- */
- this.opHistogram = {};
- this.deviceHistogram = {};
- /** unique id for a metanode of similar subgraph */
- this.templateId = null;
- /** Metanode which contains this node, if any */
- this.parentNode = null;
- this.hasNonControlEdges = false;
- this.include = InclusionType.UNSPECIFIED;
- }
- MetanodeImpl.prototype.getFirstChild = function () {
- return this.metagraph.node(this.metagraph.nodes()[0]);
- };
- /**
- * Returns the op node associated with the metanode.
- * For example, if the metanode is 'sgd', the associated
- * op node is sgd/(sgd).
- */
- MetanodeImpl.prototype.getRootOp = function () {
- var nameSplit = this.name.split('/');
- var rootOpName = this.name + '/(' + nameSplit[nameSplit.length - 1] + ')';
- return this.metagraph.node(rootOpName);
- };
- /**
- * Return an array of the names of all the leaves (non-GroupNodes) inside
- * this metanode. This performs a breadth-first search of the tree, so
- * immediate child leaves will appear earlier in the output array than
- * descendant leaves.
- */
- MetanodeImpl.prototype.leaves = function () {
- var leaves = [];
- var queue = [this];
- var metagraph; // Defined here due to a limitation of ES6->5 compilation.
- while (queue.length) {
- var node = queue.shift();
- if (node.isGroupNode) {
- metagraph = node.metagraph;
- _.each(metagraph.nodes(), function (name) { return queue.push(metagraph.node(name)); });
- }
- else {
- leaves.push(node.name);
- }
- }
- return leaves;
- };
- return MetanodeImpl;
- }());
- graph_1.MetanodeImpl = MetanodeImpl;
- ;
- function createMetaedge(v, w) {
- return new MetaedgeImpl(v, w);
- }
- graph_1.createMetaedge = createMetaedge;
- /**
- * A label object for edges between metanodes of subgraphs in the render graph.
- */
- var MetaedgeImpl = (function () {
- function MetaedgeImpl(v, w) {
- this.v = v;
- this.w = w;
- this.baseEdgeList = [];
- this.inbound = null;
- this.numRegularEdges = 0;
- this.numControlEdges = 0;
- this.numRefEdges = 0;
- this.totalSize = 0;
- }
- MetaedgeImpl.prototype.addBaseEdge = function (edge, h) {
- this.baseEdgeList.push(edge);
- if (edge.isControlDependency) {
- this.numControlEdges += 1;
- }
- else {
- this.numRegularEdges += 1;
- }
- if (edge.isReferenceEdge) {
- this.numRefEdges += 1;
- }
- // Compute the size of the tensor flowing through this
- // base edge.
- this.totalSize += MetaedgeImpl.computeSizeOfEdge(edge, h);
- h.maxMetaEdgeSize = Math.max(h.maxMetaEdgeSize, this.totalSize);
- };
- 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;
- }
- h.hasShapeInfo = true;
- // 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;
- }());
- graph_1.MetaedgeImpl = MetaedgeImpl;
- function createSeriesNode(prefix, suffix, parent, clusterId, name) {
- return new SeriesNodeImpl(prefix, suffix, parent, clusterId, name);
- }
- graph_1.createSeriesNode = createSeriesNode;
- function getSeriesNodeName(prefix, suffix, parent, startId, endId) {
- var numRepresentation = (typeof startId !== 'undefined' && typeof endId !== 'undefined') ?
- '[' + startId + '-' + endId + ']' :
- '#';
- var pattern = prefix + numRepresentation + suffix;
- return (parent ? parent + '/' : '') + pattern;
- }
- graph_1.getSeriesNodeName = getSeriesNodeName;
- var SeriesNodeImpl = (function () {
- function SeriesNodeImpl(prefix, suffix, parent, clusterId, name) {
- this.name = name || getSeriesNodeName(prefix, suffix, parent);
- this.type = NodeType.SERIES;
- this.hasLoop = false;
- this.prefix = prefix;
- this.suffix = suffix;
- this.clusterId = clusterId;
- this.ids = [];
- this.parent = parent;
- this.isGroupNode = true;
- this.cardinality = 0;
- this.metagraph = createGraph(name, GraphType.SERIES);
- // bridgegraph must be constructed lazily-see hierarchy.getBridgegraph()
- this.bridgegraph = null;
- this.parentNode = null;
- this.deviceHistogram = {};
- this.hasNonControlEdges = false;
- this.include = InclusionType.UNSPECIFIED;
- }
- return SeriesNodeImpl;
- }());
- /**
- * Extracts the shapes of the output tensors from the attr property in the
- * node proto.
- */
- // tslint:disable-next-line:no-any
- 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) {
- if (!value.list.shape) {
- // The OUTPUT_SHAPES_KEY lacks a value. We know nothing about the shape.
- return null;
- }
- // 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 null;
- }
- /**
- * Extracts the XLA Cluster that an op runs on from the attrs of the OpNode.
- * @param attr The attr property.
- * @return A string that is the name of the cluster. Or null if it could not be
- * determined.
- */
- // tslint:disable-next-line:no-any
- function extractXlaCluster(attr) {
- if (!attr) {
- return null;
- }
- // Find the attribute for XLA cluster if there is one.
- for (var i = 0; i < attr.length; i++) {
- if (attr[i].key === _XLA_CLUSTER_KEY) {
- return attr[i].value['s'] || null;
- }
- }
- return null;
- }
- /**
- * Normalizes the inputs and extracts associated metadata:
- * 1) Inputs can contain a colon followed by a number at the end
- * (e.g. inputName:1) and we remove this from the input name, and take note
- * that the input was numbered.
- * 2) Control dependency inputs contain caret at the beginning and we
- * remove this and annotate the edge as a control dependency.
- * @param inputs Array of unnormalized names of input nodes.
- */
- function normalizeInputs(inputs) {
- var normalizedInputs = [];
- _.each(inputs, function (inputName) {
- var start = inputName[0] === '^';
- var colon = inputName.lastIndexOf(':');
- var end = colon !== -1 &&
- inputName.length - colon > 1 &&
- !(/\D/).test(inputName.substring(colon + 1)) ?
- colon : inputName.length;
- var name = inputName.substring(start ? 1 : 0, end);
- if (normalizedInputs.length === 0 ||
- name !== normalizedInputs[normalizedInputs.length - 1].name) {
- normalizedInputs.push({
- name: name,
- outputTensorIndex: end === inputName.length ? 0 : Number(inputName.slice(colon + 1)),
- isControlDependency: start
- });
- }
- });
- return normalizedInputs;
- }
- function addEdgeToGraph(graph, inputName, outputNode, input, params, index) {
- // Don't allow loops in the graph.
- if (inputName === outputNode.name) {
- return;
- }
- // Check if this op type and input number corresponds to a
- // reference edge using the refEdges dictionary in the params.
- var isRefEdge = params.refEdges[outputNode.op + ' ' + index] === true;
- graph.edges.push({
- v: inputName,
- w: outputNode.name,
- outputTensorIndex: input.outputTensorIndex,
- isControlDependency: input.isControlDependency,
- isReferenceEdge: isRefEdge
- });
- }
- function build(rawNodes, params, tracker) {
- /**
- * A dictionary that maps each in-embedding node name to the node
- * object.
- */
- var inEmbedding = {};
- /**
- * A dictionary that maps each out-embedding node name to the node
- * object.
- */
- var outEmbedding = {};
- /**
- * A dictionary that maps each node name to an array of the node's
- * out-embedding node label objects.
- */
- var outEmbeddings = {};
- var isInEmbeddedPred = getEmbedPredicate(params.inEmbeddingTypes);
- var isOutEmbeddedPred = getEmbedPredicate(params.outEmbeddingTypes);
- var embeddingNodeNames = [];
- /**
- * A list of all the non-embedding node names which appear in the processed
- * list of raw nodes. Here we pre-allocate enough room for all the rawNodes,
- * even though there will some number of embeddings. The excess array length
- * is spliced off later.
- *
- * Experimentation shows that around 30% of the array will go unused, and
- * even for very large networks that amounts to less than 10k spaces.
- */
- var nodeNames = new Array(rawNodes.length);
- return tf.graph.util
- .runAsyncTask('Normalizing names', 30, function () {
- var opNodes = new Array(rawNodes.length);
- var index = 0;
- _.each(rawNodes, function (rawNode) {
- var opNode = new OpNodeImpl(rawNode);
- if (isInEmbeddedPred(opNode)) {
- embeddingNodeNames.push(opNode.name);
- inEmbedding[opNode.name] = opNode;
- return;
- }
- if (isOutEmbeddedPred(opNode)) {
- embeddingNodeNames.push(opNode.name);
- outEmbedding[opNode.name] = opNode;
- _.each(opNode.inputs, function (input) {
- var inputName = input.name;
- outEmbeddings[inputName] = outEmbeddings[inputName] || [];
- outEmbeddings[inputName].push(opNode);
- });
- return;
- }
- // The node is not an embedding, so add it to the names and nodes
- // lists.
- opNodes[index] = opNode;
- nodeNames[index] = opNode.name;
- index++;
- });
- opNodes.splice(index);
- nodeNames.splice(index);
- return opNodes;
- }, tracker)
- .then(function (opNodes) {
- // Create the graph data structure from the graphlib library.
- return tf.graph.util.runAsyncTask('Building the data structure', 70, function () {
- var normalizedNameDict = mapStrictHierarchy(nodeNames, embeddingNodeNames);
- var graph = new SlimGraph;
- // Add the nodes to the graph.
- _.each(opNodes, function (opNode) {
- var normalizedName = normalizedNameDict[opNode.name] || opNode.name;
- graph.nodes[normalizedName] = opNode;
- // Check if the node has out-embeddings. If yes, add them to the
- // node.
- if (opNode.name in outEmbeddings) {
- opNode.outEmbeddings = outEmbeddings[opNode.name];
- // Normalize the names of the out-embeddings.
- _.each(opNode.outEmbeddings, function (node) {
- node.name = normalizedNameDict[node.name] || node.name;
- });
- }
- // Update the name of the node.
- opNode.name = normalizedName;
- });
- // Visit each node's inputs to add the edges to the graph. If the
- // input
- // is an in-embedding, then add it to the node's in-embeddings
- // instead.
- _.each(opNodes, function (opNode) {
- _.each(opNode.inputs, function (input, i) {
- var inputName = input.name;
- if (inputName in inEmbedding) {
- var inEmbedNode = inEmbedding[inputName];
- opNode.inEmbeddings.push(inEmbedNode);
- // Move the inputs of the in-embedding node into incoming
- // edges of
- // the main node. E.g. the control dependency of a constant
- // node
- // should be moved to the op node where the constant is
- // embedded.
- for (var _i = 0, _a = inEmbedNode.inputs; _i < _a.length; _i++) {
- var embedInput = _a[_i];
- addEdgeToGraph(graph, normalizedNameDict[embedInput.name] ||
- embedInput.name, opNode, embedInput, params, i);
- }
- }
- else if (inputName in outEmbedding) {
- // Move the inputs of the out-embedding node into inputs of
- // the main node where the out-embedding points to.
- var outEmbedNode = outEmbedding[inputName];
- for (var _b = 0, _c = outEmbedNode.inputs; _b < _c.length; _b++) {
- var embedInput = _c[_b];
- addEdgeToGraph(graph, normalizedNameDict[embedInput.name] ||
- embedInput.name, opNode, input, params, i);
- }
- }
- else {
- addEdgeToGraph(graph, normalizedNameDict[inputName] || inputName, opNode, input, params, i);
- }
- });
- });
- // Normalize the names of in-embeddings.
- _.each(inEmbedding, function (node, name) {
- node.name = normalizedNameDict[node.name] || node.name;
- });
- return graph;
- }, tracker);
- });
- }
- graph_1.build = build;
- ;
- /**
- * Create a new graphlib.Graph() instance with default parameters
- */
- function createGraph(name, type, opt) {
- if (opt === void 0) { opt = {}; }
- var graph = new graphlib.Graph(opt);
- graph.setGraph({
- name: name,
- rankdir: 'BT',
- type: type
- });
- return graph;
- }
- graph_1.createGraph = createGraph;
- ;
- /**
- * Create a predicate for checking whether a node should be embedded based on
- * the specified types.
- */
- function getEmbedPredicate(types) {
- return function (node) {
- // check types
- for (var i = 0; i < types.length; i++) {
- var regExp = new RegExp(types[i]);
- if (node.op.match(regExp)) {
- return true;
- }
- }
- return false;
- };
- }
- ;
- /**
- * Returns a strict node name (name => name/(name)) to avoid conflicts
- * where the node name is also a namespace.
- */
- function getStrictName(name) {
- var parts = name.split(graph_1.NAMESPACE_DELIM);
- return name + graph_1.NAMESPACE_DELIM + '(' + parts[parts.length - 1] + ')';
- }
- graph_1.getStrictName = getStrictName;
- /**
- * For each op node (embedding or non-embedding), rename it if there is a
- * non-embedding node under its namespace. For example, assume node name 'A'.
- * If there is a non-embedding node under its namespace (e.g. 'A/B'), 'A' will
- * be renamed to 'A/(A)'. Then the namespace 'A' will contain 2 nodes: '(A)'
- * and 'B'. If all the nodes under 'A' are embedding nodes (e.g. constant and
- * summary), keep 'A' as an Op node and don't create a namespace.
- *
- * @param nodeNames An array of regular (non-embedding) node names.
- * @param embeddingNodeNames An array of embedding node names.
- * @return Dictionary object mapping names that need to be renamed to
- * new names.
- */
- function mapStrictHierarchy(nodeNames, embeddingNodeNames) {
- /** Dictionary that maps the old new to the new name */
- var newNameDictionary = {};
- /** Set used to store all namespaces. */
- var namespaceSet = {};
- // sort the nodes to make prefix check faster
- nodeNames.sort();
- // look for nodes with a prefix a,a/b -> a/(a),a/b
- for (var i = 0; i < nodeNames.length - 1; ++i) {
- var a = nodeNames[i];
- // Get all the parent namespaces of the current node
- // and add them in the namespace set.
- _.each(getHierarchicalPath(a).slice(0, -1), function (ns) {
- namespaceSet[ns] = true;
- });
- for (var j = i + 1; j < nodeNames.length; ++j) {
- var b = nodeNames[j];
- if (_.startsWith(b, a)) {
- if (b.length > a.length && b.charAt(a.length) === graph_1.NAMESPACE_DELIM) {
- newNameDictionary[a] = getStrictName(a);
- break;
- }
- }
- else {
- break;
- }
- }
- }
- // Go through all the embedding node names and rename them in case they
- // collide with namespaces.
- _.each(embeddingNodeNames, function (embeddingName) {
- if (embeddingName in namespaceSet) {
- // Rename to follow strict hierarchy.
- newNameDictionary[embeddingName] = getStrictName(embeddingName);
- }
- });
- return newNameDictionary;
- }
- ;
- /**
- * Returns a list of the degrees of each node in the graph.
- */
- function degreeSequence(graph) {
- var degrees = graph.nodes().map(function (name) {
- return graph.neighbors(name).length;
- });
- degrees.sort();
- return degrees;
- }
- ;
- /**
- * Returns if the degree sequence of the two graphs is the same.
- */
- function hasSimilarDegreeSequence(graph1, graph2) {
- var dg1 = degreeSequence(graph1);
- var dg2 = degreeSequence(graph2);
- for (var i = 0; i < dg1.length; i++) {
- if (dg1[i] !== dg2[i]) {
- return false;
- }
- }
- return true;
- }
- graph_1.hasSimilarDegreeSequence = hasSimilarDegreeSequence;
- ;
- /**
- * Returns the hierarchical path of the current node, based on the node's name.
- * For example, if the name is 'a/b/c', the returned path is
- * ['a', 'a/b', 'a/b/c'].
- */
- function getHierarchicalPath(name, seriesNames) {
- var path = [];
- var i = name.indexOf(graph_1.NAMESPACE_DELIM);
- // Push all parent portions of the path.
- while (i >= 0) {
- path.push(name.substring(0, i));
- i = name.indexOf(graph_1.NAMESPACE_DELIM, i + 1);
- }
- // If the node's path is under a series, then add the series node name to the
- // hierarchical path as the parent of the leaf.
- if (seriesNames) {
- var seriesName = seriesNames[name];
- if (seriesName) {
- path.push(seriesName);
- }
- }
- // Push the leaf of the path.
- path.push(name);
- return path;
- }
- graph_1.getHierarchicalPath = getHierarchicalPath;
- ;
- /**
- * Returns the string for the node inclusion toggle button, dependant
- * on the provided current InclusionType.
- */
- function getIncludeNodeButtonString(include) {
- if (include === tf.graph.InclusionType.EXCLUDE) {
- return 'Add to main graph';
- }
- else {
- return 'Remove from main graph';
- }
- }
- 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>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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.
- */
-var tf;
-(function (tf) {
- var graph;
- (function (graph_1) {
- var hierarchy;
- (function (hierarchy_1) {
- /**
- * Class for the Graph Hierarchy for TensorFlow graph.
- */
- var HierarchyImpl = (function () {
- function HierarchyImpl() {
- this.hasShapeInfo = false;
- this.maxMetaEdgeSize = 1;
- this.root = graph_1.createMetanode(graph_1.ROOT_NAME, { compound: true });
- this.templates = null;
- this.devices = null;
- /**
- * @type {Object} Dictionary object that maps node name to the node
- * (could be op-node, metanode, or series-node)
- */
- this.index = {};
- this.index[graph_1.ROOT_NAME] = this.root;
- this.orderings = {};
- }
- HierarchyImpl.prototype.getNodeMap = function () {
- return this.index;
- };
- HierarchyImpl.prototype.node = function (name) {
- return this.index[name];
- };
- HierarchyImpl.prototype.setNode = function (name, node) {
- this.index[name] = node;
- };
- /**
- * Given the name of a node in this hierarchy, get its bridgegraph, creating
- * it on the fly if necessary. If the node is not a GroupNode, then this
- * method returns null. If the provided name does not map to a node in the
- * hierarchy, an error will be thrown.
- */
- HierarchyImpl.prototype.getBridgegraph = function (nodeName) {
- var _this = this;
- var node = this.index[nodeName];
- if (!node) {
- throw Error('Could not find node in hierarchy: ' + nodeName);
- }
- if (!('metagraph' in node)) {
- return null;
- }
- var groupNode = node;
- if (groupNode.bridgegraph) {
- return groupNode.bridgegraph;
- }
- var bridgegraph = groupNode.bridgegraph =
- graph_1.createGraph('BRIDGEGRAPH', graph_1.GraphType.BRIDGE);
- if (!node.parentNode || !('metagraph' in node.parentNode)) {
- return bridgegraph;
- }
- var parentNode = node.parentNode;
- var parentMetagraph = parentNode.metagraph;
- var parentBridgegraph = this.getBridgegraph(parentNode.name);
- // For each of the parent node's two Metaedge containing graphs, process
- // each Metaedge involving this node.
- _.each([parentMetagraph, parentBridgegraph], function (parentGraph) {
- _(parentGraph.edges())
- .filter(function (e) { return e.v === nodeName || e.w === nodeName; })
- .each(function (parentEdgeObj) {
- var inbound = parentEdgeObj.w === nodeName;
- var parentMetaedge = parentGraph.edge(parentEdgeObj);
- // The parent's Metaedge represents some number of underlying
- // BaseEdges from the original full graph. For each of those, we need
- // to determine which immediate child is involved and make sure
- // there's a Metaedge in the bridgegraph that covers it.
- _.each(parentMetaedge.baseEdgeList, function (baseEdge) {
- // Based on the direction, figure out which is the descendant node
- // and which is the 'other' node (sibling of parent or ancestor).
- var _a = inbound ?
- [baseEdge.w, parentEdgeObj.v] :
- [baseEdge.v, parentEdgeObj.w], descendantName = _a[0], otherName = _a[1];
- // Determine the immediate child containing this descendant node.
- var childName = _this.getChildName(nodeName, descendantName);
- // Look for an existing Metaedge in the bridgegraph (or create a
- // new one) that covers the relationship between child and other.
- var bridgeEdgeObj = {
- v: inbound ? otherName : childName,
- w: inbound ? childName : otherName,
- };
- var bridgeMetaedge = bridgegraph.edge(bridgeEdgeObj);
- if (!bridgeMetaedge) {
- bridgeMetaedge = graph_1.createMetaedge(bridgeEdgeObj.v, bridgeEdgeObj.w);
- bridgeMetaedge.inbound = inbound;
- bridgegraph.setEdge(bridgeEdgeObj.v, bridgeEdgeObj.w, bridgeMetaedge);
- }
- // Copy the BaseEdge from the parent's Metaedge into this
- // bridgegraph Metaedge.
- bridgeMetaedge.addBaseEdge(baseEdge, _this);
- });
- })
- .value(); // force lodash chain execution.
- });
- return bridgegraph;
- };
- /**
- * Utility function for determining the name of the immediate child under a
- * node for a given descendant path. If the descendant corresponds to no
- * immediate child, an error is thrown.
- */
- HierarchyImpl.prototype.getChildName = function (nodeName, descendantName) {
- // Walk up the hierarchy from the descendant to find the child.
- var currentNode = this.index[descendantName];
- while (currentNode) {
- if (currentNode.parentNode && currentNode.parentNode.name === nodeName) {
- return currentNode.name;
- }
- currentNode = currentNode.parentNode;
- }
- throw Error('Could not find immediate child for descendant: ' + descendantName);
- };
- ;
- /** Given the name of a node, return its incoming metaedges. */
- HierarchyImpl.prototype.getPredecessors = function (nodeName) {
- var _this = this;
- var node = this.index[nodeName];
- if (!node) {
- throw Error('Could not find node with name: ' + nodeName);
- }
- var predecessors = this.getOneWayEdges(node, true);
- // Add embedded predecessors, such as constants.
- if (!node.isGroupNode) {
- _.each(node.inEmbeddings, function (embeddedNode) {
- _.each(node.inputs, function (input) {
- if (input.name === embeddedNode.name) {
- // Make a new metaedge holding the edge between the
- // node and the in-embedding.
- var metaedge = new graph_1.MetaedgeImpl(embeddedNode.name, nodeName);
- metaedge.addBaseEdge({
- isControlDependency: input.isControlDependency,
- outputTensorIndex: input.outputTensorIndex,
- isReferenceEdge: false,
- v: embeddedNode.name,
- w: nodeName
- }, _this);
- predecessors.regular.push(metaedge);
- }
- });
- });
- }
- return predecessors;
- };
- /**
- * Given the name of a node, return its outgoing metaedges.
- *
- * This is the inverse of getPredecessors(). See that method's documentation
- * for an in-depth example.
- */
- HierarchyImpl.prototype.getSuccessors = function (nodeName) {
- var _this = this;
- var node = this.index[nodeName];
- if (!node) {
- throw Error('Could not find node with name: ' + nodeName);
- }
- var successors = this.getOneWayEdges(node, false);
- // Add embedded successors, such as summaries.
- if (!node.isGroupNode) {
- _.each(node.outEmbeddings, function (embeddedNode) {
- _.each(embeddedNode.inputs, function (input) {
- if (input.name === nodeName) {
- // Make a new metaedge holding the edge between the
- // node and the out-embedding.
- var metaedge = new graph_1.MetaedgeImpl(nodeName, embeddedNode.name);
- metaedge.addBaseEdge({
- isControlDependency: input.isControlDependency,
- outputTensorIndex: input.outputTensorIndex,
- isReferenceEdge: false,
- v: nodeName,
- w: embeddedNode.name
- }, _this);
- successors.regular.push(metaedge);
- }
- });
- });
- }
- return successors;
- };
- /** Helper method for getPredecessors and getSuccessors */
- HierarchyImpl.prototype.getOneWayEdges = function (node, inEdges) {
- var edges = { control: [], regular: [] };
- // A node with no parent cannot have any edges.
- if (!node.parentNode || !node.parentNode.isGroupNode) {
- return 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;
- };
- /**
- * For a given GroupNode, get or calculate an object which describes a
- * topological ordering of child nodes within that GroupNode's metagraph.
- *
- * This ordering is used when rendering bridge control edges which are
- * sometimes backwards relative to the dataflow.
- *
- * For example, say we have a graph with two edges A->B and A->C, and we're
- * interested in the ordering under ROOT. In this case, any of the following
- * would be legitimate return values:
- *
- * - { 'A': 0, 'B': 1, 'C': 2 } -- most likely
- * - { 'A': 0, 'B': 2, 'C': 1 } -- less likely
- * - { 'A': 12, 'B': 100, 'C': 99 } -- unlikely, but still OK
- *
- * The algorithm does not guarantee that all numbers from 0-N (where N is
- * the number of nodes) appear exactly once. Rather it guarantees that if
- * there is a path between two nodes, the earlier one will have a lower
- * number in the ordering hash.
- *
- * When generating the ordering, we ignore control Metaedges (those which
- * represent only BaseEdges that have isControlDependency set to true).
- *
- * If there is no node with the specified name, an error is thrown. If the
- * node with the specified name is not a group node, null is returned.
- */
- HierarchyImpl.prototype.getTopologicalOrdering = function (nodeName) {
- var node = this.index[nodeName];
- if (!node) {
- throw Error('Could not find node with name: ' + nodeName);
- }
- if (!node.isGroupNode) {
- return null;
- }
- if (nodeName in this.orderings) {
- return this.orderings[nodeName];
- }
- // Mapping of a child node names to lists of their successors.
- var successors = {};
- // Set of node names which have appeared as a destination.
- var destinations = {};
- var metagraph = node.metagraph;
- _.each(metagraph.edges(), function (e) {
- if (!metagraph.edge(e).numRegularEdges) {
- return; // Skip control edges.
- }
- // Keep track of successors and destinations.
- if (!(e.v in successors)) {
- successors[e.v] = [];
- }
- successors[e.v].push(e.w);
- destinations[e.w] = true;
- });
- // Seed the queue with true sources (those that are not destinations).
- var queue = _.difference(_.keys(successors), _.keys(destinations));
- // Produce an ordering by traversing the graph breadth first.
- var ordering = this.orderings[nodeName] = {};
- var index = 0;
- while (queue.length) {
- var childName = queue.shift();
- ordering[childName] = index++;
- _.each(successors[childName], function (succName) { return queue.push(succName); });
- delete successors[childName]; // Prevent cycles from infinite looping.
- }
- return ordering;
- };
- /**
- * Returns a d3 Ordinal function that can be used to look up the index of
- * a node based on its template id.
- */
- HierarchyImpl.prototype.getTemplateIndex = function () {
- var templateNames = d3.keys(this.templates);
- var templateIndex = d3.scale.ordinal()
- .domain(templateNames)
- .range(d3.range(0, templateNames.length));
- 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
- * the other ends of edges that involve that node in the direction specified
- * by whether it's inbound.
- *
- * For example if you wanted to find the predecessors of a node, you'd call
- * this method for the parent's metagraph and bridgegraph, specifying inbound
- * as true (look at the source of inbound edges to the specified node).
- *
- * Discovered target names are appended to the targets array.
- */
- function findEdgeTargetsInGraph(graph, node, inbound, targets) {
- var edges = inbound ? graph.inEdges(node.name) : graph.outEdges(node.name);
- _.each(edges, function (e) {
- var metaedge = graph.edge(e);
- var targetList = metaedge.numRegularEdges ? targets.regular : targets.control;
- targetList.push(metaedge);
- });
- }
- /**
- * @param graph The raw graph.
- * @param params Parameters used when building a hierarchy.
- */
- function build(graph, params, tracker) {
- var h = new HierarchyImpl();
- var seriesNames = {};
- return tf.graph.util
- .runAsyncTask('Adding nodes', 20, function () {
- // Get all the possible device and XLA cluster names.
- var deviceNames = {};
- var xlaClusterNames = {};
- _.each(graph.nodes, function (node, nodeName) {
- if (node.device) {
- deviceNames[node.device] = true;
- }
- if (node.xlaCluster) {
- xlaClusterNames[node.xlaCluster] = true;
- }
- });
- h.devices = _.keys(deviceNames);
- h.xlaClusters = _.keys(xlaClusterNames);
- addNodes(h, graph);
- }, tracker)
- .then(function () {
- return tf.graph.util.runAsyncTask('Detect series', 20, function () {
- if (params.seriesNodeMinSize > 0) {
- groupSeries(h.root, h, seriesNames, params.seriesNodeMinSize, params.seriesMap);
- }
- }, tracker);
- })
- .then(function () {
- return tf.graph.util.runAsyncTask('Adding edges', 30, function () {
- addEdges(h, graph, seriesNames);
- }, tracker);
- })
- .then(function () {
- return tf.graph.util.runAsyncTask('Finding similar subgraphs', 30, function () {
- h.templates = graph_1.template.detect(h, params.verifyTemplate);
- }, tracker);
- })
- .then(function () {
- return h;
- });
- }
- hierarchy_1.build = build;
- ;
- function joinAndAggregateStats(h, stats) {
- // Get all the possible device names.
- var deviceNames = {};
- _.each(h.root.leaves(), function (nodeName) {
- var leaf = h.node(nodeName);
- if (leaf.device != null) {
- deviceNames[leaf.device] = true;
- }
- });
- h.devices = _.keys(deviceNames);
- // Reset stats for each group node.
- _.each(h.getNodeMap(), function (node, nodeName) {
- if (node.isGroupNode) {
- node.stats = new graph_1.NodeStats(null);
- node.deviceHistogram = {};
- }
- });
- // Bubble-up the stats and device distribution from leaves to parents.
- _.each(h.root.leaves(), function (nodeName) {
- var leaf = h.node(nodeName);
- var node = leaf;
- while (node.parentNode != null) {
- if (leaf.device != null) {
- var deviceHistogram = node.parentNode.deviceHistogram;
- deviceHistogram[leaf.device] = (deviceHistogram[leaf.device] || 0) + 1;
- }
- if (leaf.stats != null) {
- node.parentNode.stats.combine(leaf.stats);
- }
- node = node.parentNode;
- }
- });
- }
- hierarchy_1.joinAndAggregateStats = joinAndAggregateStats;
- /**
- * Creates the metanodes in the hierarchical graph and assigns parent-child
- * relationship between them.
- */
- function addNodes(h, graph) {
- _.each(graph.nodes, function (node, nodeName) {
- var path = graph_1.getHierarchicalPath(node.name);
- var parent = h.root;
- parent.depth = Math.max(path.length, parent.depth);
- // Create parent metanodes for each depth. For example if the node name
- // is 'a/b/c', then create metanodes 'a' and 'a/b', where 'a/b' is a child
- // of a.
- for (var i = 0; i < path.length; i++) {
- parent.depth = Math.max(parent.depth, path.length - i);
- parent.cardinality += node.cardinality;
- parent.opHistogram[node.op] = (parent.opHistogram[node.op] || 0) + 1;
- if (node.device != null) {
- parent.deviceHistogram[node.device] =
- (parent.deviceHistogram[node.device] || 0) + 1;
- }
- if (i === path.length - 1) {
- break;
- }
- var name_1 = path[i];
- var child = h.node(name_1);
- if (!child) {
- child = graph_1.createMetanode(name_1);
- child.parentNode = parent;
- h.setNode(name_1, child);
- parent.metagraph.setNode(name_1, child);
- }
- parent = child;
- }
- // Assuming node name is 'a/b/c', assign the OpNode as a child of the
- // metanode 'a/b'.
- h.setNode(node.name, node);
- node.parentNode = parent;
- parent.metagraph.setNode(node.name, node);
- // Add each of the in-embeddings and out-embeddings in the hierarchy.
- _.each(node.inEmbeddings, function (embedding) {
- h.setNode(embedding.name, embedding);
- embedding.parentNode = node;
- });
- _.each(node.outEmbeddings, function (embedding) {
- h.setNode(embedding.name, embedding);
- embedding.parentNode = node;
- });
- });
- }
- ;
- /**
- * For each metanode in the hierarchical graph, this method adds:
- * the edges in the metagraph. These are edges between nodes
- * that share the same parent.
- */
- function addEdges(h, graph, seriesNames) {
- var nodeIndex = h.getNodeMap();
- // Ancestor paths for the source and destination nodes of an edge. These are
- // reused for each edge rather than allocating new ones. It's about 10% faster
- // than allocating new ones on each pass through the loop.
- var sourcePath = [];
- var destPath = [];
- // Insert the ancestor path for a node into the provided array, including the
- // node itself. Return the index of the last node inserted (always ROOT).
- var getPath = function (node, path) {
- var i = 0;
- while (node) {
- path[i++] = node.name;
- node = node.parentNode;
- }
- return i - 1;
- };
- _.each(graph.edges, function (baseEdge) {
- // Get the hierarchical paths for the source and destination of the edge.
- var sourceAncestorIndex = getPath(graph.nodes[baseEdge.v], sourcePath);
- var destAncestorIndex = getPath(graph.nodes[baseEdge.w], destPath);
- // If the hierarchical path cannot be found for either endpoint, then we
- // cannot create the edge. This happens for example when a node has a
- // control dependency on a summary node, which are embedded.
- if (sourceAncestorIndex === -1 || destAncestorIndex === -1) {
- return;
- }
- // Find the lowest shared ancestor between source and dest by looking for
- // the highest nodes that differ between their ancestor paths.
- while (sourcePath[sourceAncestorIndex] === destPath[destAncestorIndex]) {
- sourceAncestorIndex--;
- destAncestorIndex--;
- if (sourceAncestorIndex < 0 || destAncestorIndex < 0) {
- // This would only occur if the two nodes were the same (a cycle in the
- // graph), or if one endpoint was a strict ancestor of the other. The
- // latter shouldn't happen because we rename nodes which are both
- // metanodes and op nodes. E.g. 'A/B' becomes 'A/B/(B)'.
- throw Error('No difference found between ancestor paths.');
- }
- }
- var sharedAncestorNode = nodeIndex[sourcePath[sourceAncestorIndex + 1]];
- var sourceAncestorName = sourcePath[sourceAncestorIndex];
- var destAncestorName = destPath[destAncestorIndex];
- // Find or create the Metaedge which should contain this BaseEdge inside
- // the shared ancestor.
- var metaedge = sharedAncestorNode.metagraph.edge(sourceAncestorName, destAncestorName);
- if (!metaedge) {
- metaedge = graph_1.createMetaedge(sourceAncestorName, destAncestorName);
- sharedAncestorNode.metagraph
- .setEdge(sourceAncestorName, destAncestorName, metaedge);
- }
- if (!sharedAncestorNode.hasNonControlEdges &&
- !baseEdge.isControlDependency) {
- sharedAncestorNode.hasNonControlEdges = true;
- }
- metaedge.addBaseEdge(baseEdge, h);
- });
- }
- ;
- /**
- * Using the hierarchy template information, detect series in the provided
- * metanode. For each detected series, create a new SeriesNode
- * and remove series members from the metanode's metagraph and move them to
- * the new series node's metagraph.
- *
- * @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, 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, map);
- }
- });
- var clusters = clusterNodes(metagraph);
- var seriesDict = detectSeries(clusters, metagraph);
- // Add each series node to the graph and add its grouped children to its own
- // metagraph.
- _.each(seriesDict, function (seriesNode, seriesName) {
- var nodeMemberNames = seriesNode.metagraph.nodes();
- _.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
- metagraph.setNode(seriesName, seriesNode);
- _.each(nodeMemberNames, function (n) {
- var child = metagraph.node(n);
- seriesNode.metagraph.setNode(n, child);
- seriesNode.parentNode = child.parentNode;
- seriesNode.cardinality++;
- if (child.device != null) {
- seriesNode.deviceHistogram[child.device] =
- (seriesNode.deviceHistogram[child.device] || 0) + 1;
- }
- child.parentNode = seriesNode;
- seriesNames[n] = seriesName;
- // Remove now-grouped node from its original parent's metagraph.
- metagraph.removeNode(n);
- });
- });
- }
- ;
- /** cluster op-nodes with similar op */
- function clusterNodes(metagraph) {
- var result = {};
- return _.reduce(metagraph.nodes(), function (clusters, n) {
- var child = metagraph.node(n);
- if (child.type === graph_1.NodeType.META) {
- // skip metanodes
- return clusters;
- }
- var template = child.op;
- if (template) {
- clusters[template] = clusters[template] || [];
- clusters[template].push(child.name);
- }
- return clusters;
- }, result);
- }
- /**
- * For each cluster of op-nodes based op type, try to detect groupings.
- * Infer series name using by trying to find pattern '<number>' in the node
- * name.
- *
- * @param clusters Dictionary output from clusterNodes().
- * @param metagraph
- * @return A dictionary from series name => seriesNode
- */
- function detectSeries(clusters, metagraph) {
- var seriesDict = {};
- _.each(clusters, function (members, clusterId) {
- if (members.length <= 1) {
- return;
- } // isolated clusters can't make series
- /** @type {Object} A dictionary mapping seriesName to seriesInfoArray,
- * which is an array that contains objects with name, id, prefix, suffix,
- * and parent properties.
- */
- var candidatesDict = {};
- // Group all nodes that have the same name, with the exception of a
- // number at the end of the name after an underscore, which is allowed to
- // vary.
- _.each(members, function (name) {
- var isGroup = name.charAt(name.length - 1) === '*';
- var namepath = name.split('/');
- var leaf = namepath[namepath.length - 1];
- var parent = namepath.slice(0, namepath.length - 1).join('/');
- var matches = leaf.match(/^(\D*)_(\d+)$/);
- var prefix;
- var id;
- var suffix = '';
- if (matches) {
- prefix = matches[1]; // the front non-numeric characters
- id = matches[2]; // the digits
- }
- else {
- prefix = isGroup ? leaf.substr(0, leaf.length - 1) : leaf;
- id = 0;
- suffix = isGroup ? '*' : '';
- }
- var seriesName = graph_1.getSeriesNodeName(prefix, suffix, parent);
- candidatesDict[seriesName] = candidatesDict[seriesName] || [];
- var seriesNode = graph_1.createSeriesNode(prefix, suffix, parent, +id, name);
- candidatesDict[seriesName].push(seriesNode);
- });
- // In each group of nodes, group nodes in bunches that have monotonically
- // increasing numbers in their names. Each of these bunches is a series.
- _.each(candidatesDict, function (seriesInfoArray, seriesName) {
- if (seriesInfoArray.length < 2) {
- return;
- }
- seriesInfoArray.sort(function (a, b) {
- return (+a.clusterId) - (+b.clusterId);
- });
- // Loop through the nodes sorted by its detected series number, grouping
- // all nodes with monotonically-increasing series numbers.
- var seriesNodes = [seriesInfoArray[0]];
- for (var index = 1; index < seriesInfoArray.length; index++) {
- var nextNode = seriesInfoArray[index];
- if (nextNode.clusterId === seriesNodes[seriesNodes.length - 1].clusterId
- + 1) {
- seriesNodes.push(nextNode);
- continue;
- }
- addSeriesToDict(seriesNodes, seriesDict, +clusterId, metagraph);
- seriesNodes = [nextNode];
- }
- addSeriesToDict(seriesNodes, seriesDict, +clusterId, metagraph);
- });
- });
- return seriesDict;
- }
- /**
- * Add a series to the provided dictionary mapping series names to series.
- *
- * @param seriesNodes the nodes in the series. Contains
- * name, id, prefix, suffix and parent properties of the node.
- * @param seriesDict the dictionary of series
- * @param clusterId ID of the template of the nodes of the series
- * @param metagraph
- */
- 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_1 = graph_1.createSeriesNode(seriesNodes[0].prefix, seriesNodes[0].suffix, seriesNodes[0].parent, clusterId, curSeriesName);
- _.each(seriesNodes, function (node) {
- curSeriesNode_1.ids.push(node.clusterId);
- curSeriesNode_1.metagraph.setNode(node.name, metagraph.node(node.name));
- });
- 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 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph_1) {
- var layout;
- (function (layout) {
- /** Set of parameters that define the look and feel of the graph. */
- layout.PARAMS = {
- animation: {
- /** Default duration for graph animations in ms. */
- duration: 250
- },
- graph: {
- /** Graph parameter for metanode. */
- meta: {
- /**
- * Dagre's nodesep param - number of pixels that
- * separate nodes horizontally in the layout.
- *
- * See https://github.com/cpettitt/dagre/wiki#configuring-the-layout
- */
- nodeSep: 5,
- /**
- * Dagre's ranksep param - number of pixels
- * between each rank in the layout.
- *
- * See https://github.com/cpettitt/dagre/wiki#configuring-the-layout
- */
- rankSep: 25,
- /**
- * Dagre's edgesep param - number of pixels that separate
- * edges horizontally in the layout.
- */
- edgeSep: 5,
- },
- /** Graph parameter for metanode. */
- series: {
- /**
- * Dagre's nodesep param - number of pixels that
- * separate nodes horizontally in the layout.
- *
- * See https://github.com/cpettitt/dagre/wiki#configuring-the-layout
- */
- nodeSep: 5,
- /**
- * Dagre's ranksep param - number of pixels
- * between each rank in the layout.
- *
- * See https://github.com/cpettitt/dagre/wiki#configuring-the-layout
- */
- rankSep: 25,
- /**
- * Dagre's edgesep param - number of pixels that separate
- * edges horizontally in the layout.
- */
- edgeSep: 5
- },
- /**
- * Padding is used to correctly position the graph SVG inside of its parent
- * element. The padding amounts are applied using an SVG transform of X and
- * Y coordinates.
- */
- padding: { paddingTop: 40, paddingLeft: 20 }
- },
- subscene: {
- meta: {
- paddingTop: 10,
- paddingBottom: 10,
- paddingLeft: 10,
- paddingRight: 10,
- /**
- * Used to leave room for the label on top of the highest node in
- * the core graph.
- */
- labelHeight: 20,
- /** X-space between each extracted node and the core graph. */
- extractXOffset: 15,
- /** Y-space between each extracted node. */
- extractYOffset: 20
- },
- series: {
- paddingTop: 10,
- paddingBottom: 10,
- paddingLeft: 10,
- paddingRight: 10,
- labelHeight: 10
- }
- },
- nodeSize: {
- /** Size of meta nodes. */
- meta: {
- radius: 5,
- width: 60,
- maxLabelWidth: 52,
- /** A scale for the node's height based on number of nodes inside */
- height: d3.scale.linear().domain([1, 200]).range([15, 60]).clamp(true),
- /** The radius of the circle denoting the expand button. */
- expandButtonRadius: 3
- },
- /** Size of op nodes. */
- op: {
- width: 15,
- height: 6,
- radius: 3,
- labelOffset: -8,
- maxLabelWidth: 30
- },
- /** Size of series nodes. */
- series: {
- expanded: {
- // For expanded series nodes, width and height will be
- // computed to account for the subscene.
- radius: 10,
- labelOffset: 0,
- },
- vertical: {
- // When unexpanded, series whose underlying metagraphs contain
- // one or more non-control edges will show as a vertical stack
- // of ellipses.
- width: 16,
- height: 13,
- labelOffset: -13,
- },
- horizontal: {
- // When unexpanded, series whose underlying metagraphs contain
- // no non-control edges will show as a horizontal stack of
- // ellipses.
- width: 24,
- height: 8,
- radius: 10,
- labelOffset: -10,
- },
- },
- /** Size of bridge nodes. */
- bridge: {
- // NOTE: bridge nodes will normally be invisible, but they must
- // take up some space so that the layout step leaves room for
- // their edges.
- width: 20,
- height: 20,
- radius: 2,
- labelOffset: 0
- }
- },
- shortcutSize: {
- /** Size of shortcuts for op nodes */
- op: { width: 10, height: 4 },
- /** Size of shortcuts for meta nodes */
- meta: { width: 12, height: 4, radius: 1 },
- /** Size of shortcuts for series nodes */
- series: {
- width: 14,
- height: 4,
- }
- },
- annotations: {
- /** Maximum possible width of the bounding box for in annotations */
- inboxWidth: 50,
- /** Maximum possible width of the bounding box for out annotations */
- outboxWidth: 50,
- /** X-space between the shape and each annotation-node. */
- xOffset: 10,
- /** Y-space between each annotation-node. */
- yOffset: 3,
- /** X-space between each annotation-node and its label. */
- labelOffset: 2,
- /** Defines the max width for annotation label */
- maxLabelWidth: 120
- },
- constant: { size: { width: 4, height: 4 } },
- series: {
- /** Maximum number of repeated item for unexpanded series node. */
- maxStackCount: 3,
- /**
- * Positioning offset ratio for collapsed stack
- * of parallel series (series without edges between its members).
- */
- parallelStackOffsetRatio: 0.2,
- /**
- * Positioning offset ratio for collapsed stack
- * of tower series (series with edges between its members).
- */
- towerStackOffsetRatio: 0.5
- },
- minimap: {
- /** The maximum width/height the minimap can have. */
- size: 150
- }
- };
- /** Calculate layout for a scene of a group node. */
- function layoutScene(renderNodeInfo) {
- // Update layout, size, and annotations of its children nodes and edges.
- if (renderNodeInfo.node.isGroupNode) {
- layoutChildren(renderNodeInfo);
- }
- // Update position of its children nodes and edges
- if (renderNodeInfo.node.type === graph_1.NodeType.META) {
- layoutMetanode(renderNodeInfo);
- }
- else if (renderNodeInfo.node.type === graph_1.NodeType.SERIES) {
- layoutSeriesNode(renderNodeInfo);
- }
- }
- layout.layoutScene = layoutScene;
- ;
- /**
- * Updates the total width of an unexpanded node which includes the size of its
- * in and out annotations.
- */
- function updateTotalWidthOfNode(renderInfo) {
- renderInfo.inboxWidth = renderInfo.inAnnotations.list.length > 0 ?
- layout.PARAMS.annotations.inboxWidth : 0;
- renderInfo.outboxWidth = renderInfo.outAnnotations.list.length > 0 ?
- layout.PARAMS.annotations.outboxWidth : 0;
- // Assign the width of the core box (the main shape of the node).
- renderInfo.coreBox.width = renderInfo.width;
- renderInfo.coreBox.height = renderInfo.height;
- // TODO(jimbo): Account for font width rather than using a magic number.
- var labelLength = renderInfo.node.name.length -
- renderInfo.node.name.lastIndexOf(graph_1.NAMESPACE_DELIM) - 1;
- var charWidth = 3; // 3 pixels per character.
- // Compute the total width of the node.
- renderInfo.width = Math.max(renderInfo.coreBox.width +
- renderInfo.inboxWidth + renderInfo.outboxWidth, labelLength * charWidth);
- }
- /**
- * Update layout, size, and annotations of its children nodes and edges.
- */
- function layoutChildren(renderNodeInfo) {
- var children = renderNodeInfo.coreGraph.nodes().map(function (n) {
- return renderNodeInfo.coreGraph.node(n);
- }).concat(renderNodeInfo.isolatedInExtract, renderNodeInfo.isolatedOutExtract);
- _.each(children, function (childNodeInfo) {
- // Set size of each child
- switch (childNodeInfo.node.type) {
- case graph_1.NodeType.OP:
- _.extend(childNodeInfo, layout.PARAMS.nodeSize.op);
- break;
- case graph_1.NodeType.BRIDGE:
- _.extend(childNodeInfo, layout.PARAMS.nodeSize.bridge);
- break;
- case graph_1.NodeType.META:
- if (!childNodeInfo.expanded) {
- // Set fixed width and scalable height based on cardinality
- _.extend(childNodeInfo, layout.PARAMS.nodeSize.meta);
- childNodeInfo.height =
- layout.PARAMS.nodeSize.meta.height(childNodeInfo.node.cardinality);
- }
- else {
- var childGroupNodeInfo = childNodeInfo;
- layoutScene(childGroupNodeInfo); // Recursively layout its subscene.
- }
- break;
- case graph_1.NodeType.SERIES:
- if (childNodeInfo.expanded) {
- _.extend(childNodeInfo, layout.PARAMS.nodeSize.series.expanded);
- var childGroupNodeInfo = childNodeInfo;
- layoutScene(childGroupNodeInfo); // Recursively layout its subscene.
- }
- else {
- var childGroupNodeInfo = childNodeInfo;
- var seriesParams = childGroupNodeInfo.node.hasNonControlEdges ?
- layout.PARAMS.nodeSize.series.vertical :
- layout.PARAMS.nodeSize.series.horizontal;
- _.extend(childNodeInfo, seriesParams);
- }
- break;
- default:
- throw Error('Unrecognized node type: ' + childNodeInfo.node.type);
- }
- // Compute total width of un-expanded nodes. Width of expanded nodes
- // has already been computed.
- if (!childNodeInfo.expanded) {
- updateTotalWidthOfNode(childNodeInfo);
- }
- // Layout each child's annotations
- layoutAnnotation(childNodeInfo);
- });
- }
- /**
- * Calculate layout for a graph using dagre
- * @param graph the graph to be laid out
- * @param params layout parameters
- * @return width and height of the core graph
- */
- function dagreLayout(graph, params) {
- _.extend(graph.graph(), {
- nodesep: params.nodeSep,
- ranksep: params.rankSep,
- edgesep: params.edgeSep
- });
- var bridgeNodeNames = [];
- var nonBridgeNodeNames = [];
- // Split out nodes into bridge and non-bridge nodes, and calculate the total
- // width we should use for bridge nodes.
- _.each(graph.nodes(), function (nodeName) {
- var nodeInfo = graph.node(nodeName);
- if (nodeInfo.node.type === graph_1.NodeType.BRIDGE) {
- bridgeNodeNames.push(nodeName);
- }
- else {
- nonBridgeNodeNames.push(nodeName);
- }
- });
- // If there are no non-bridge nodes, then the graph has zero size.
- if (!nonBridgeNodeNames.length) {
- return {
- width: 0,
- height: 0,
- };
- }
- dagre.layout(graph);
- // Calculate the true bounding box of the graph by iterating over nodes and
- // edges rather than accepting dagre's word for it. In particular, we should
- // ignore the extra-wide bridge nodes and bridge edges, and allow for
- // annotation boxes and labels.
- var minX = Infinity;
- var minY = Infinity;
- var maxX = -Infinity;
- var maxY = -Infinity;
- _.each(nonBridgeNodeNames, function (nodeName) {
- var nodeInfo = graph.node(nodeName);
- var w = 0.5 * nodeInfo.width;
- var x1 = nodeInfo.x - w;
- var x2 = nodeInfo.x + w;
- minX = x1 < minX ? x1 : minX;
- maxX = x2 > maxX ? x2 : maxX;
- // TODO(jimbo): Account for the height of labels above op nodes here.
- var h = 0.5 * nodeInfo.height;
- var y1 = nodeInfo.y - h;
- var y2 = nodeInfo.y + h;
- minY = y1 < minY ? y1 : minY;
- maxY = y2 > maxY ? y2 : maxY;
- });
- _.each(graph.edges(), function (edgeObj) {
- var edgeInfo = graph.edge(edgeObj);
- if (edgeInfo.structural) {
- return; // Skip structural edges from min/max calculations.
- }
- // Since the node size passed to dagre includes the in and out
- // annotations, the endpoints of the edge produced by dagre may not
- // point to the actual node shape (rectangle, ellipse). We correct the
- // end-points by finding the intersection of a line between the
- // next-to-last (next-to-first) point and the destination (source)
- // rectangle.
- var sourceNode = graph.node(edgeInfo.metaedge.v);
- var destNode = graph.node(edgeInfo.metaedge.w);
- // Straight 3-points edges are special case, since they are curved after
- // our default correction. To keep them straight, we remove the mid point
- // and correct the first and the last point to be the center of the
- // source and destination node respectively.
- if (edgeInfo.points.length === 3 && isStraightLine(edgeInfo.points)) {
- if (sourceNode != null) {
- var cxSource = sourceNode.expanded ?
- sourceNode.x : computeCXPositionOfNodeShape(sourceNode);
- edgeInfo.points[0].x = cxSource;
- }
- if (destNode != null) {
- var cxDest = destNode.expanded ?
- destNode.x : computeCXPositionOfNodeShape(destNode);
- edgeInfo.points[2].x = cxDest;
- }
- // Remove the middle point so the edge doesn't curve.
- edgeInfo.points = [edgeInfo.points[0], edgeInfo.points[1]];
- }
- // Correct the destination endpoint of the edge.
- var nextToLastPoint = edgeInfo.points[edgeInfo.points.length - 2];
- // The destination node might be null if this is a bridge edge.
- if (destNode != null) {
- edgeInfo.points[edgeInfo.points.length - 1] =
- intersectPointAndNode(nextToLastPoint, destNode);
- }
- // Correct the source endpoint of the edge.
- var secondPoint = edgeInfo.points[1];
- // The source might be null if this is a bridge edge.
- if (sourceNode != null) {
- edgeInfo.points[0] = intersectPointAndNode(secondPoint, sourceNode);
- }
- _.each(edgeInfo.points, function (point) {
- minX = point.x < minX ? point.x : minX;
- maxX = point.x > maxX ? point.x : maxX;
- minY = point.y < minY ? point.y : minY;
- maxY = point.y > maxY ? point.y : maxY;
- });
- });
- // Shift all nodes and edge points to account for the left-padding amount,
- // and the invisible bridge nodes.
- _.each(graph.nodes(), function (nodeName) {
- var nodeInfo = graph.node(nodeName);
- nodeInfo.x -= minX;
- nodeInfo.y -= minY;
- });
- _.each(graph.edges(), function (edgeObj) {
- _.each(graph.edge(edgeObj).points, function (point) {
- point.x -= minX;
- point.y -= minY;
- });
- });
- return {
- width: maxX - minX,
- height: maxY - minY
- };
- }
- /** Layout a metanode. Only called for an expanded node. */
- function layoutMetanode(renderNodeInfo) {
- // First, copy params specific to meta nodes onto this render info object.
- var params = layout.PARAMS.subscene.meta;
- _.extend(renderNodeInfo, params);
- // Invoke dagre.layout() on the core graph and record the bounding box
- // dimensions.
- _.extend(renderNodeInfo.coreBox, dagreLayout(renderNodeInfo.coreGraph, layout.PARAMS.graph.meta));
- // Calculate the position of nodes in isolatedInExtract relative to the
- // top-left corner of inExtractBox (the bounding box for all inExtract nodes)
- // and calculate the size of the inExtractBox.
- var maxInExtractWidth = _.max(renderNodeInfo.isolatedInExtract, function (renderNode) { return renderNode.width; }).width;
- renderNodeInfo.inExtractBox.width = maxInExtractWidth != null ?
- maxInExtractWidth : 0;
- renderNodeInfo.inExtractBox.height =
- _.reduce(renderNodeInfo.isolatedInExtract, function (height, child, i) {
- var yOffset = i > 0 ? params.extractYOffset : 0;
- // use width/height here to avoid overlaps between extracts
- child.x = 0;
- child.y = height + yOffset + child.height / 2;
- return height + yOffset + child.height;
- }, 0);
- // Calculate the position of nodes in isolatedOutExtract relative to the
- // top-left corner of outExtractBox (the bounding box for all outExtract
- // nodes) and calculate the size of the outExtractBox.
- var maxOutExtractWidth = _.max(renderNodeInfo.isolatedOutExtract, function (renderNode) { return renderNode.width; }).width;
- renderNodeInfo.outExtractBox.width = maxOutExtractWidth != null ?
- maxOutExtractWidth : 0;
- renderNodeInfo.outExtractBox.height =
- _.reduce(renderNodeInfo.isolatedOutExtract, function (height, child, i) {
- var yOffset = i > 0 ? params.extractYOffset : 0;
- // use width/height here to avoid overlaps between extracts
- child.x = 0;
- child.y = height + yOffset + child.height / 2;
- return height + yOffset + child.height;
- }, 0);
- // Compute the total padding between the core graph, in-extract and
- // out-extract boxes.
- var numParts = 0;
- if (renderNodeInfo.isolatedInExtract.length > 0) {
- numParts++;
- }
- if (renderNodeInfo.isolatedOutExtract.length > 0) {
- numParts++;
- }
- if (renderNodeInfo.coreGraph.nodeCount() > 0) {
- numParts++;
- }
- var offset = layout.PARAMS.subscene.meta.extractXOffset;
- var padding = numParts <= 1 ? 0 : (numParts <= 2 ? offset : 2 * offset);
- // Add the in-extract and out-extract width to the core box width.
- renderNodeInfo.coreBox.width += renderNodeInfo.inExtractBox.width +
- renderNodeInfo.outExtractBox.width + padding;
- renderNodeInfo.coreBox.height =
- params.labelHeight +
- Math.max(renderNodeInfo.inExtractBox.height, renderNodeInfo.coreBox.height, renderNodeInfo.outExtractBox.height);
- // Determine the whole metanode's width (from left to right).
- renderNodeInfo.width = renderNodeInfo.coreBox.width +
- params.paddingLeft + params.paddingRight;
- // Determine the whole metanode's height (from top to bottom).
- renderNodeInfo.height =
- renderNodeInfo.paddingTop +
- renderNodeInfo.coreBox.height +
- renderNodeInfo.paddingBottom;
- }
- /**
- * Calculate layout for series node's core graph. Only called for an expanded
- * series.
- */
- function layoutSeriesNode(node) {
- var graph = node.coreGraph;
- var params = layout.PARAMS.subscene.series;
- _.extend(node, params);
- // Layout the core.
- _.extend(node.coreBox, dagreLayout(node.coreGraph, layout.PARAMS.graph.series));
- _.each(graph.nodes(), function (nodeName) {
- graph.node(nodeName).excluded = false;
- });
- // Series do not have in/outExtractBox so no need to include them here.
- node.width = node.coreBox.width + params.paddingLeft + params.paddingRight;
- node.height = node.coreBox.height + params.paddingTop + params.paddingBottom;
- }
- /**
- * Calculate layout for annotations of a given node.
- * This will modify positions of the given node and its annotations.
- *
- * @see tf.graph.render.Node and tf.graph.render.Annotation
- * for description of each property of each render node.
- *
- */
- function layoutAnnotation(renderNodeInfo) {
- // If the render node is an expanded metanode, then its annotations will not
- // be visible and we should skip the annotation calculations.
- if (renderNodeInfo.expanded) {
- return;
- }
- var inAnnotations = renderNodeInfo.inAnnotations.list;
- var outAnnotations = renderNodeInfo.outAnnotations.list;
- // Calculate size for in-annotations
- _.each(inAnnotations, function (a) { return sizeAnnotation(a); });
- // Calculate size for out-annotations
- _.each(outAnnotations, function (a) { return sizeAnnotation(a); });
- var params = layout.PARAMS.annotations;
- // Calculate annotation node position (a.dx, a.dy)
- // and total height for in-annotations
- // After this chunk of code:
- // inboxHeight = sum of annotation heights+ (annotation.length - 1 * yOffset)
- var inboxHeight = _.reduce(inAnnotations, function (height, a, i) {
- var yOffset = i > 0 ? params.yOffset : 0;
- a.dx = -(renderNodeInfo.coreBox.width + a.width) / 2 - params.xOffset;
- a.dy = height + yOffset + a.height / 2;
- return height + yOffset + a.height;
- }, 0);
- _.each(inAnnotations, function (a) {
- a.dy -= inboxHeight / 2;
- a.labelOffset = params.labelOffset;
- });
- // Calculate annotation node position (a.dx, a.dy)
- // and total height for out-annotations
- // After this chunk of code:
- // outboxHeight = sum of annotation heights +
- // (annotation.length - 1 * yOffset)
- var outboxHeight = _.reduce(outAnnotations, function (height, a, i) {
- var yOffset = i > 0 ? params.yOffset : 0;
- a.dx = (renderNodeInfo.coreBox.width + a.width) / 2 + params.xOffset;
- a.dy = height + yOffset + a.height / 2;
- return height + yOffset + a.height;
- }, 0);
- _.each(outAnnotations, function (a) {
- // adjust by (half of ) the total height
- // so dy is relative to the host node's center.
- a.dy -= outboxHeight / 2;
- a.labelOffset = params.labelOffset;
- });
- // Creating scales for touch point between the in-annotation edges
- // and their hosts.
- var inTouchHeight = Math.min(renderNodeInfo.height / 2 - renderNodeInfo.radius, inboxHeight / 2);
- inTouchHeight = inTouchHeight < 0 ? 0 : inTouchHeight;
- var inY = d3.scale.linear()
- .domain([0, inAnnotations.length - 1])
- .range([-inTouchHeight, inTouchHeight]);
- // Calculate annotation edge position
- _.each(inAnnotations, function (a, i) {
- a.points = [
- // The annotation node end
- {
- dx: a.dx + a.width / 2,
- dy: a.dy
- },
- // The host node end
- {
- dx: -renderNodeInfo.coreBox.width / 2,
- // only use scale if there are more than one,
- // otherwise center it vertically
- dy: inAnnotations.length > 1 ? inY(i) : 0
- }
- ];
- });
- // Creating scales for touch point between the out-annotation edges
- // and their hosts.
- var outTouchHeight = Math.min(renderNodeInfo.height / 2 - renderNodeInfo.radius, outboxHeight / 2);
- outTouchHeight = outTouchHeight < 0 ? 0 : outTouchHeight;
- var outY = d3.scale.linear()
- .domain([0, outAnnotations.length - 1])
- .range([-outTouchHeight, outTouchHeight]);
- _.each(outAnnotations, function (a, i) {
- // Add point from the border of the annotation node
- a.points = [
- // The host node end
- {
- dx: renderNodeInfo.coreBox.width / 2,
- // only use scale if there are more than one,
- // otherwise center it vertically
- dy: outAnnotations.length > 1 ? outY(i) : 0
- },
- // The annotation node end
- {
- dx: a.dx - a.width / 2,
- dy: a.dy
- }
- ];
- });
- renderNodeInfo.height =
- Math.max(renderNodeInfo.height, inboxHeight, outboxHeight);
- }
- /**
- * Set size of an annotation node.
- */
- function sizeAnnotation(a) {
- switch (a.annotationType) {
- case graph_1.render.AnnotationType.CONSTANT:
- _.extend(a, layout.PARAMS.constant.size);
- break;
- case graph_1.render.AnnotationType.SHORTCUT:
- if (a.node.type === graph_1.NodeType.OP) {
- _.extend(a, layout.PARAMS.shortcutSize.op);
- }
- else if (a.node.type === graph_1.NodeType.META) {
- _.extend(a, layout.PARAMS.shortcutSize.meta);
- }
- else if (a.node.type === graph_1.NodeType.SERIES) {
- _.extend(a, layout.PARAMS.shortcutSize.series);
- }
- else {
- throw Error('Invalid node type: ' + a.node.type);
- }
- break;
- case graph_1.render.AnnotationType.SUMMARY:
- _.extend(a, layout.PARAMS.constant.size);
- break;
- }
- }
- /**
- * Determines the center position of the node's shape. The position depends
- * on if the node has in and out-annotations.
- */
- function computeCXPositionOfNodeShape(renderInfo) {
- if (renderInfo.expanded) {
- return renderInfo.x;
- }
- var dx = renderInfo.inAnnotations.list.length ? renderInfo.inboxWidth : 0;
- return renderInfo.x - renderInfo.width / 2 + dx +
- renderInfo.coreBox.width / 2;
- }
- layout.computeCXPositionOfNodeShape = computeCXPositionOfNodeShape;
- /** Returns the angle (in degrees) between two points. */
- function angleBetweenTwoPoints(a, b) {
- var dx = b.x - a.x;
- var dy = b.y - a.y;
- return 180 * Math.atan(dy / dx) / Math.PI;
- }
- /**
- * Returns if a line going through the specified points is a straight line.
- */
- function isStraightLine(points) {
- var angle = angleBetweenTwoPoints(points[0], points[1]);
- for (var i = 1; i < points.length - 1; i++) {
- var newAngle = angleBetweenTwoPoints(points[i], points[i + 1]);
- // Have a tolerance of 1 degree.
- if (Math.abs(newAngle - angle) > 1) {
- return false;
- }
- angle = newAngle;
- }
- return true;
- }
- /**
- * Returns the intersection of a line between the provided point
- * and the provided rectangle.
- */
- function intersectPointAndNode(point, node) {
- // cx and cy are the center of the rectangle.
- var cx = node.expanded ?
- node.x : computeCXPositionOfNodeShape(node);
- var cy = node.y;
- // Calculate the slope
- var dx = point.x - cx;
- var dy = point.y - cy;
- var w = node.expanded ? node.width : node.coreBox.width;
- var h = node.expanded ? node.height : node.coreBox.height;
- var deltaX, deltaY;
- if (Math.abs(dy) * w / 2 > Math.abs(dx) * h / 2) {
- // The intersection is above or below the rectangle.
- if (dy < 0) {
- h = -h;
- }
- deltaX = dy === 0 ? 0 : h / 2 * dx / dy;
- deltaY = h / 2;
- }
- else {
- // The intersection is left or right of the rectangle.
- if (dx < 0) {
- w = -w;
- }
- deltaX = w / 2;
- deltaY = dx === 0 ? 0 : w / 2 * dy / dx;
- }
- return { x: cx + deltaX, y: cy + deltaY };
- }
- })(layout = graph_1.layout || (graph_1.layout = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // close module
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph) {
- var parser;
- (function (parser) {
- /**
- * Parses a native js value, which can be either a string, boolean or number.
- *
- * @param value The value to be parsed.
- */
- function parseValue(value) {
- if (value === 'true') {
- return true;
- }
- if (value === 'false') {
- return false;
- }
- var firstChar = value[0];
- if (firstChar === '"') {
- return value.substring(1, value.length - 1);
- }
- var num = parseFloat(value);
- return isNaN(num) ? value : num;
- }
- /**
- * Fetches a text file and returns a promise of the result.
- */
- function fetchPbTxt(filepath) {
- return new Promise(function (resolve, reject) {
- var request = new XMLHttpRequest();
- request.open('GET', filepath);
- request.responseType = 'arraybuffer';
- request.onerror = function () { return reject(request.status); };
- request.onload = function () { return resolve(request.response); };
- request.send(null);
- });
- }
- parser.fetchPbTxt = fetchPbTxt;
- /**
- * Fetches the metadata file, parses it and returns a promise of the result.
- */
- function fetchAndParseMetadata(path, tracker) {
- return tf.graph.util
- .runTask('Reading metadata pbtxt', 40, function () {
- if (path == null) {
- return Promise.resolve(null);
- }
- return fetchPbTxt(path);
- }, tracker)
- .then(function (arrayBuffer) {
- return tf.graph.util.runAsyncPromiseTask('Parsing metadata.pbtxt', 60, function () {
- return arrayBuffer != null ? parseStatsPbTxt(arrayBuffer) :
- Promise.resolve(null);
- }, tracker);
- });
- }
- parser.fetchAndParseMetadata = fetchAndParseMetadata;
- /**
- * Fetches the graph file, parses it and returns a promise of the result. The
- * result will be undefined if the graph is empty.
- */
- function fetchAndParseGraphData(path, pbTxtFile, tracker) {
- return tf.graph.util
- .runTask('Reading graph pbtxt', 40, function () {
- if (pbTxtFile) {
- return new Promise(function (resolve, reject) {
- var fileReader = new FileReader();
- fileReader.onload = function () { return resolve(fileReader.result); };
- fileReader.onerror = function () { return reject(fileReader.error); };
- fileReader.readAsArrayBuffer(pbTxtFile);
- });
- }
- else {
- return fetchPbTxt(path);
- }
- }, tracker)
- .then(function (arrayBuffer) {
- return tf.graph.util.runTask('Parsing graph.pbtxt', 60, function () {
- return parseGraphPbTxt(arrayBuffer);
- }, tracker);
- });
- }
- parser.fetchAndParseGraphData = fetchAndParseGraphData;
- /**
- * Parse a file object in a streaming fashion line by line (or custom delim).
- * Can handle very large files.
- * @param input The file object as an array buffer.
- * @param callback The callback called on each line
- * @param chunkSize The size of each read chunk. (optional)
- * @param delim The delimiter used to split a line. (optional)
- * @returns A promise for when it is finished.
- */
- function streamParse(arrayBuffer, callback, chunkSize, delim) {
- if (chunkSize === void 0) { chunkSize = 1000000; }
- if (delim === void 0) { delim = '\n'; }
- return new Promise(function (resolve, reject) {
- var offset = 0;
- var bufferSize = arrayBuffer.byteLength - 1;
- var data = '';
- function readHandler(str) {
- offset += chunkSize;
- var parts = str.split(delim);
- var first = data + parts[0];
- if (parts.length === 1) {
- data = first;
- readChunk(offset, chunkSize);
- return;
- }
- data = parts[parts.length - 1];
- callback(first);
- for (var i = 1; i < parts.length - 1; i++) {
- callback(parts[i]);
- }
- if (offset >= bufferSize) {
- if (data) {
- callback(data);
- }
- resolve(true);
- return;
- }
- readChunk(offset, chunkSize);
- }
- function readChunk(offset, size) {
- var arrayBufferChunk = arrayBuffer.slice(offset, offset + size);
- var blob = new Blob([arrayBufferChunk]);
- var file = new FileReader();
- file.onload = function (e) { return readHandler(e.target.result); };
- file.readAsText(blob);
- }
- readChunk(offset, chunkSize);
- });
- }
- parser.streamParse = streamParse;
- /**
- * Since proto-txt doesn't explicitly say whether an attribute is repeated
- * (an array) or not, we keep a hard-coded list of attributes that are known
- * to be repeated. This list is used in parsing time to convert repeated
- * attributes into arrays even when the attribute only shows up once in the
- * object.
- */
- var GRAPH_REPEATED_FIELDS = {
- 'node': true,
- 'node.input': true,
- 'node.attr': true,
- '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.list.shape': true,
- 'node.attr.value.list.shape.dim': true,
- 'node.attr.value.list.s': true
- };
- var METADATA_REPEATED_FIELDS = {
- 'step_stats.dev_stats': true,
- 'step_stats.dev_stats.node_stats': true,
- 'step_stats.dev_stats.node_stats.output': true,
- 'step_stats.dev_stats.node_stats.memory': true,
- 'step_stats.dev_stats.node_stats.output.tensor_description.shape.dim': true
- };
- /**
- * Parses an ArrayBuffer of a proto txt file into a raw Graph object.
- */
- function parseGraphPbTxt(input) {
- return parsePbtxtFile(input, GRAPH_REPEATED_FIELDS).then(function (obj) { return obj['node']; });
- }
- parser.parseGraphPbTxt = parseGraphPbTxt;
- /**
- * Parses an ArrayBuffer of a proto txt file into a StepStats object.
- */
- function parseStatsPbTxt(input) {
- return parsePbtxtFile(input, METADATA_REPEATED_FIELDS)
- .then(function (obj) { return obj['step_stats']; });
- }
- parser.parseStatsPbTxt = parseStatsPbTxt;
- /**
- * Parses a ArrayBuffer of a proto txt file into javascript object.
- *
- * @param input The ArrayBuffer or file object implementing slice.
- * @param repeatedFields Map (Set) of all the repeated fields, since you can't
- * tell directly from the pbtxt if a field is repeated or not.
- * @returns The parsed object.
- */
- function parsePbtxtFile(input, repeatedFields) {
- var output = {};
- var stack = [];
- var path = [];
- var current = output;
- function splitNameAndValueInAttribute(line) {
- var colonIndex = line.indexOf(':');
- var name = line.substring(0, colonIndex).trim();
- var value = parseValue(line.substring(colonIndex + 2).trim());
- return {
- name: name,
- value: value
- };
- }
- /**
- * Adds a value, given the attribute name and the host object. If the
- * attribute already exists, but is not an array, it will convert it to an
- * array of values.
- *
- * @param obj The host object that holds the attribute.
- * @param name The attribute name (key).
- * @param value The attribute value.
- * @param path A path that identifies the attribute. Used to check if
- * an attribute is an array or not.
- */
- function addAttribute(obj, name, value, path) {
- // We treat 'node' specially since it is done so often.
- var existingValue = obj[name];
- if (existingValue == null) {
- obj[name] = path.join('.') in repeatedFields ? [value] : value;
- }
- else if (Array.isArray(existingValue)) {
- existingValue.push(value);
- }
- else {
- obj[name] = [existingValue, value];
- }
- }
- // Run through the file a line at a time.
- return streamParse(input, function (line) {
- if (!line) {
- return;
- }
- line = line.trim();
- switch (line[line.length - 1]) {
- case '{':
- var name_1 = line.substring(0, line.length - 2).trim();
- var newValue = {};
- stack.push(current);
- path.push(name_1);
- addAttribute(current, name_1, newValue, path);
- current = newValue;
- break;
- case '}':
- current = stack.pop();
- path.pop();
- break;
- default:
- var x = splitNameAndValueInAttribute(line);
- addAttribute(current, x.name, x.value, path.concat(x.name));
- break;
- }
- }).then(function () {
- return output;
- });
- }
- })(parser = graph.parser || (graph.parser = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // Close module tf.graph.parser.
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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.
-==============================================================================*/
-</script>
-<script>var __extends = (this && this.__extends) || (function () {
- var extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
-})();
-/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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 Render Hierarchy for TensorFlow graph.
- */
-var tf;
-(function (tf) {
- var graph;
- (function (graph_1) {
- var render;
- (function (render) {
- /**
- * Color parameters for op nodes.
- */
- render.OpNodeColors = { DEFAULT_FILL: 'white', DEFAULT_STROKE: '#b2b2b2' };
- /**
- * Color parameters for node encoding.
- * @type {Object}
- */
- render.MetanodeColors = {
- /**
- * Default fill and stroke to use when no other information is available.
- */
- DEFAULT_FILL: '#d9d9d9',
- DEFAULT_STROKE: '#a6a6a6',
- SATURATION: 0.6,
- LIGHTNESS: 0.85,
- /**
- * Neutral color to use when the node is expanded (used when coloring by
- * compute time, memory and device).
- */
- EXPANDED_COLOR: '#f0f0f0',
- /**
- * Standard hue values for node color palette.
- */
- HUES: [220, 100, 180, 40, 20, 340, 260, 300, 140, 60],
- STRUCTURE_PALETTE: function (id, lightened) {
- // The code below is a flexible way to computationally create a set
- // of colors that go well together.
- var hues = render.MetanodeColors.HUES;
- var n = hues.length;
- var hue = hues[id % n];
- var m = Math.sin(hue * Math.PI / 360);
- var sat = lightened ? 30 : 90 - 60 * m;
- var light = lightened ? 95 : 80;
- return d3.hsl(hue, .01 * sat, .01 * light).toString();
- },
- DEVICE_PALETTE: function (index) {
- return render.MetanodeColors.STRUCTURE_PALETTE(index);
- },
- XLA_CLUSTER_PALETTE: function (index) {
- return render.MetanodeColors.STRUCTURE_PALETTE(index);
- },
- UNKNOWN: '#eee',
- GRADIENT_OUTLINE: '#888'
- };
- /**
- * Color parameters for op nodes.
- */
- render.SeriesNodeColors = {
- DEFAULT_FILL: 'white',
- DEFAULT_STROKE: '#b2b2b2'
- };
- /**
- * Parameters that affect how the graph is rendered on the screen.
- */
- var PARAMS = {
- /**
- * Whether to extract high degree nodes from the core part of the graph.
- */
- enableExtraction: true,
- /**
- * The minimum number of nodes for a graph to have in order for high in and
- * out degree nodes to be extracted in auxiliary. The aim here is to prevent
- * nodes from being extracted from small graphs.
- */
- minNodeCountForExtraction: 15,
- /**
- * The minimum in or out degree a node must have in order to be possibly
- * extracted.
- */
- minDegreeForExtraction: 5,
- /**
- * Maximum number of control edges a node can have before they aren't
- * displayed.
- */
- maxControlDegree: 4,
- /**
- * Maximum in (for outbound bridge paths) or out (for inbound bridge paths)
- * degree of a node allowed for a bridge path to be rendered to it from a
- * subhierarchy of nodes. Having a max prevents having too many nodes emanate
- * from a subhierarchy and crowding up.
- */
- maxBridgePathDegree: 4,
- /**
- * Types patterns for predefined out-extract nodes, which are
- * sink-like nodes that will be extracted from the main graph.
- */
- outExtractTypes: [
- 'NoOp' // NoOps are sink-like used for managing control dependencies.
- ],
- /**
- * Types patterns for predefined in-extract nodes, which are
- * source-like nodes that will be extracted from the main graph.
- */
- inExtractTypes: [],
- /**
- * When removing edges from a high degree node, remove all of its edges if
- * detachAllEdgesForHighDegree is true. Otherwise remove all in-edges if
- * the node has high in-degree, or all out-edges if the node has high
- * out-degree.
- */
- detachAllEdgesForHighDegree: true,
- /**
- * After extracting high in/out degree nodes and predefined
- * source-like/sink-like, extract isolated nodes to the side
- * if this extractIsolatedNodesWithAnnotationsOnOneSide is true.
- */
- extractIsolatedNodesWithAnnotationsOnOneSide: true,
- /**
- * Whether to add bridge nodes and edges to the core when building the
- * subhierarchy of an expanded metanode. See buildSubhierarchy().
- */
- enableBridgegraph: true,
- /**
- * 2 colors, for the minimum and maximum value respectively, whenever we
- * have a gradient scale.
- */
- minMaxColors: ['#fff5f0', '#fb6a4a'],
- /**
- * Maximum number of annotations to be displayed on a node before an
- * ellipsis is used.
- */
- maxAnnotations: 5
- };
- /**
- * Stores the rendering information, such as x and y coordinates,
- * for each node in the graph.
- */
- var RenderGraphInfo = (function () {
- function RenderGraphInfo(hierarchy, displayingStats) {
- this.hierarchy = hierarchy;
- this.displayingStats = displayingStats;
- this.index = {};
- this.renderedOpNames = [];
- this.computeScales();
- // Maps node name to whether the rendering hierarchy was already
- // constructed.
- this.hasSubhierarchy = {};
- this.root = new RenderGroupNodeInfo(hierarchy.root);
- this.index[hierarchy.root.name] = this.root;
- this.renderedOpNames.push(hierarchy.root.name);
- this.buildSubhierarchy(hierarchy.root.name);
- this.root.expanded = true;
- this.traceInputs = false;
- }
- RenderGraphInfo.prototype.computeScales = function () {
- this.deviceColorMap = d3.scale.ordinal()
- .domain(this.hierarchy.devices)
- .range(_.map(d3.range(this.hierarchy.devices.length), render.MetanodeColors.DEVICE_PALETTE));
- this.xlaClusterColorMap =
- d3.scale.ordinal()
- .domain(this.hierarchy.xlaClusters)
- .range(_.map(d3.range(this.hierarchy.xlaClusters.length), render.MetanodeColors.XLA_CLUSTER_PALETTE));
- var topLevelGraph = this.hierarchy.root.metagraph;
- // Find the maximum and minimum memory usage.
- var memoryExtent = d3.extent(topLevelGraph.nodes(), function (nodeName, index) {
- var node = topLevelGraph.node(nodeName);
- // Some ops don't have stats at all.
- if (node.stats != null) {
- return node.stats.totalBytes;
- }
- });
- this.memoryUsageScale = d3.scale.linear()
- .domain(memoryExtent)
- .range(PARAMS.minMaxColors);
- // Find also the minimum and maximum compute time.
- var computeTimeExtent = d3.extent(topLevelGraph.nodes(), function (nodeName, index) {
- var node = topLevelGraph.node(nodeName);
- // Some ops don't have stats at all.
- if (node.stats != null) {
- return node.stats.totalMicros;
- }
- });
- this.computeTimeScale = d3.scale.linear()
- .domain(computeTimeExtent)
- .range(PARAMS.minMaxColors);
- this.edgeWidthScale = this.hierarchy.hasShapeInfo ?
- graph_1.scene.edge.EDGE_WIDTH_SCALE :
- d3.scale.linear()
- .domain([1, this.hierarchy.maxMetaEdgeSize])
- .range([graph_1.scene.edge.MIN_EDGE_WIDTH, graph_1.scene.edge.MAX_EDGE_WIDTH]);
- };
- /**
- * Get a previously created RenderNodeInfo by its node name.
- */
- RenderGraphInfo.prototype.getRenderNodeByName = function (nodeName) {
- 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.
- */
- RenderGraphInfo.prototype.getOrCreateRenderNodeByName = function (nodeName) {
- var _this = this;
- // Polymer may invoke this with null.
- if (!nodeName) {
- return null;
- }
- if (nodeName in this.index) {
- 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);
- this.index[nodeName] = renderInfo;
- this.renderedOpNames.push(nodeName);
- if (node.stats) {
- renderInfo.memoryColor = this.memoryUsageScale(node.stats.totalBytes);
- renderInfo.computeTimeColor =
- this.computeTimeScale(node.stats.totalMicros);
- }
- if (!node.isGroupNode) {
- var clusterName = node.xlaCluster;
- if (clusterName) {
- renderInfo.xlaClusterColor = this.xlaClusterColorMap(clusterName);
- }
- }
- // We only fade nodes when we're displaying stats.
- renderInfo.isFadedOut = this.displayingStats &&
- !tf.graph.util.hasDisplayableNodeStats(node.stats);
- if (node.isGroupNode) {
- // Make a list of tuples (device, proportion), where proportion
- // is the fraction of op nodes that have that device.
- var pairs = _.pairs(node.deviceHistogram);
- if (pairs.length > 0) {
- // Compute the total # of devices.
- 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_1
- }); });
- }
- }
- else {
- var device = renderInfo.node.device;
- if (device) {
- renderInfo.deviceColors = [{
- color: this.deviceColorMap(device),
- proportion: 1.0
- }];
- }
- }
- return this.index[nodeName];
- };
- /**
- * Return the nearest ancestor node, including itself, that is visible
- * in the visualization. This method is used so that we can select
- * (highlight) a node that isn't drawn yet, by selecting (highlighting)
- * its nearest ancestor that has been drawn.
- */
- RenderGraphInfo.prototype.getNearestVisibleAncestor = function (name) {
- var path = graph_1.getHierarchicalPath(name);
- for (var i = 0; i < path.length; i++) {
- var nodeName = path[i];
- // Op nodes have expanded set to false by default.
- if (!this.getRenderNodeByName(nodeName).expanded) {
- return nodeName;
- }
- }
- // Fallthrough. If everything was expanded return the node.
- return name;
- };
- // TODO(jimbo): Delete this an any code it touches (all deprecated).
- RenderGraphInfo.prototype.setDepth = function (depth) {
- setGroupNodeDepth(this.root, +depth);
- };
- /**
- * Returns true if the renderNode is an isolated node within its parent node.
- */
- RenderGraphInfo.prototype.isNodeAuxiliary = function (renderNode) {
- var parentNode = this.getRenderNodeByName(renderNode.node.parentNode.name);
- var found = _.find(parentNode.isolatedInExtract, function (node) {
- return node.node.name === renderNode.node.name;
- });
- if (found) {
- return true;
- }
- found = _.find(parentNode.isolatedOutExtract, function (node) {
- return node.node.name === renderNode.node.name;
- });
- return !!found;
- };
- /**
- * Returns a list of ops that have been rendered so far for this graph. More
- * ops may later be rendered if the user expands nodes for instance. The list
- * returned here can only stay the same size or grow on successive calls.
- */
- RenderGraphInfo.prototype.getNamesOfRenderedOps = function () {
- return this.renderedOpNames;
- };
- RenderGraphInfo.prototype.buildSubhierarchy = function (nodeName) {
- var _this = this;
- // Terminate if the rendering hierarchy was already constructed
- // for this node.
- if (nodeName in this.hasSubhierarchy) {
- return;
- }
- var renderNodeInfo = this.index[nodeName];
- // If it is not a meta node or a series node, don't do anything.
- if (renderNodeInfo.node.type !== graph_1.NodeType.META &&
- renderNodeInfo.node.type !== graph_1.NodeType.SERIES) {
- return;
- }
- // At this point we know the rendering information is about a group node.
- var renderGroupNodeInfo = renderNodeInfo;
- var metagraph = renderGroupNodeInfo.node.metagraph;
- var coreGraph = renderGroupNodeInfo.coreGraph;
- // Create render nodes to represent each child from the metagraph. Although
- // these will initially be added to the coreGraph, they may later be
- // extracted. Also, due to extraction, the coreGraph may contain disjoint
- // groups between which there is no visible path (other than annotations).
- _.each(metagraph.nodes(), function (childName) {
- var childRenderInfo = _this.getOrCreateRenderNodeByName(childName);
- var childNode = childRenderInfo.node;
- coreGraph.setNode(childName, childRenderInfo);
- if (!childNode.isGroupNode) {
- _.each(childNode.inEmbeddings, function (embedding) {
- var renderMetaedgeInfo = new RenderMetaedgeInfo(null);
- addInAnnotation(childRenderInfo, embedding, null, renderMetaedgeInfo, AnnotationType.CONSTANT);
- _this.index[embedding.name] = new RenderNodeInfo(embedding);
- });
- _.each(childNode.outEmbeddings, function (embedding) {
- var renderMetaedgeInfo = new RenderMetaedgeInfo(null);
- addOutAnnotation(childRenderInfo, embedding, null, renderMetaedgeInfo, AnnotationType.SUMMARY);
- _this.index[embedding.name] = new RenderNodeInfo(embedding);
- });
- }
- });
- // Add render metaedge info for edges in the metagraph.
- _.each(metagraph.edges(), function (edgeObj) {
- var metaedge = metagraph.edge(edgeObj);
- var renderMetaedgeInfo = new RenderMetaedgeInfo(metaedge);
- renderMetaedgeInfo.isFadedOut =
- _this.index[edgeObj.v].isFadedOut || _this.index[edgeObj.w].isFadedOut;
- coreGraph.setEdge(edgeObj.v, edgeObj.w, renderMetaedgeInfo);
- });
- if (PARAMS.enableExtraction &&
- renderGroupNodeInfo.node.type === graph_1.NodeType.META) {
- extractHighDegrees(renderGroupNodeInfo);
- }
- // Record that we constructed the rendering hierarchy for this node, so we
- // don't construct it another time.
- this.hasSubhierarchy[nodeName] = true;
- // Look up the parent node's render information and short circuit if none.
- var parentNode = renderGroupNodeInfo.node.parentNode;
- if (!parentNode) {
- return;
- }
- var parentNodeInfo = this.index[parentNode.name];
- // Utility function for computing the name of a bridge node.
- var getBridgeNodeName = function (inbound) {
- var rest = [];
- for (var _i = 1; _i < arguments.length; _i++) {
- rest[_i - 1] = arguments[_i];
- }
- return rest.concat([inbound ? 'IN' : 'OUT']).join('~~');
- };
- // Build out the bridgegraph.
- var bridgegraph = this.hierarchy.getBridgegraph(nodeName);
- // Look for popular nodes so we can make annotations instead of paths.
- var otherCounts = {
- // Counts of edges coming INTO other nodes by name (outgoing from self).
- in: {},
- // Counts of edges going OUT from other nodes by name (coming into self).
- out: {},
- // Counts of all control edges involving other nodes by name.
- control: {},
- };
- _.each(bridgegraph.edges(), function (e) {
- // An edge is inbound if its destination node is in the metagraph.
- var inbound = !!metagraph.node(e.w);
- var otherName = inbound ? e.v : e.w;
- var metaedge = bridgegraph.edge(e);
- if (!metaedge.numRegularEdges) {
- otherCounts.control[otherName] =
- (otherCounts.control[otherName] || 0) + 1;
- }
- else if (inbound) {
- otherCounts.out[otherName] = (otherCounts.out[otherName] || 0) + 1;
- }
- else {
- otherCounts.in[otherName] = (otherCounts.in[otherName] || 0) + 1;
- }
- });
- // Add annotations and edges for bridgegraph relationships.
- var hierarchyNodeMap = this.hierarchy.getNodeMap();
- _.each(bridgegraph.edges(), function (bridgeEdgeObj) {
- var bridgeMetaedge = bridgegraph.edge(bridgeEdgeObj);
- // Determine whether this bridge edge is incoming by checking the
- // metagraph for a node that matches the destination end.
- var inbound = !!metagraph.node(bridgeEdgeObj.w);
- // Based on the direction of the edge, one endpoint will be an immediate
- // child of this renderNodeInfo, and the other endpoint will be a sibling
- // of the parent (or an ancestor further up).
- var _a = inbound ?
- [bridgeEdgeObj.w, bridgeEdgeObj.v] :
- [bridgeEdgeObj.v, bridgeEdgeObj.w], childName = _a[0], otherName = _a[1];
- var childRenderInfo = _this.index[childName];
- var otherRenderInfo = _this.index[otherName];
- var otherNode = otherRenderInfo ?
- otherRenderInfo.node :
- hierarchyNodeMap[otherName];
- // Determine whether this edge is a control edge between nodes where
- // either node is high-degree with respect to control edges. This will
- // be a signal to show it as an annotation instead of a bridge edge.
- var isHighDegreeControlEdge = !bridgeMetaedge.numRegularEdges &&
- otherCounts.control[otherName] > PARAMS.maxControlDegree;
- var _b = inbound ?
- [renderNodeInfo.inAnnotations, childRenderInfo.inAnnotations] :
- [renderNodeInfo.outAnnotations, childRenderInfo.outAnnotations], childAnnotations = _b[1];
- // Don't render a bridge path if the other node has in or out degree above
- // a threshold, lest bridge paths emanating out of a metagraph crowd up,
- // as was the case for the Fatcat LSTM lstm_1 > lstm_1 metagraph.
- var otherDegreeCount = (inbound ? otherCounts.out : otherCounts.in)[otherName];
- var isOtherHighDegree = otherDegreeCount > PARAMS.maxBridgePathDegree;
- // The adjoining render metaedge info from the parent's coreGraph, if any.
- // It will either be a Metaedge involving this node directly, if it
- // previously came from a metagraph, or it'll be a Metaedge involving
- // a previously created bridge node standing in for the other node.
- var adjoiningMetaedge = null;
- // We can only hope to render a bridge path if:
- // - bridgegraph paths are enabled,
- // - the other node is not too high-degree,
- // - the child is in the core (not extracted for being high-degree), and
- // - there's a path (in the traversal sense) between child and other.
- var canDrawBridgePath = false;
- if (PARAMS.enableBridgegraph &&
- !isOtherHighDegree &&
- !isHighDegreeControlEdge &&
- childRenderInfo.isInCore()) {
- // Utility function for finding an adjoining metaedge.
- var findAdjoiningMetaedge = function (targetName) {
- var adjoiningEdgeObj = inbound ?
- { v: targetName, w: nodeName } :
- { v: nodeName, w: targetName };
- return parentNodeInfo.coreGraph.edge(adjoiningEdgeObj);
- };
- adjoiningMetaedge = findAdjoiningMetaedge(otherName);
- if (!adjoiningMetaedge) {
- adjoiningMetaedge = findAdjoiningMetaedge(getBridgeNodeName(inbound, otherName, parentNode.name));
- }
- canDrawBridgePath = !!adjoiningMetaedge;
- }
- // Although dataflow edges are acyclic, control dependency edges may
- // actually point 'backwards' in the graph. If this bridgeMetaedge is
- // a control dependency, we need to determine whether it's backwards
- // pointing so that we render it appropriately.
- //
- // For instance, say we're rendering a graph with nodes named A/B and Z/Y,
- // and we're currently rendering the bridgegraph for A. Further, let's say
- // that there was an original BaseEdge from A/B->Z/Y and a CONTROL EDGE
- // from Z/Y=>A/B.
- //
- // +----------------+
- // | A |
- // | +-----+ | +------+
- // | | B |>-----\x3e|>-------\x3e| Z |
- // | | | | | |
- // | | | * | | |
- // | | |<=====<|<=======<| |
- // | +-----+ | +------+
- // +----------------+
- //
- // When we render the subhierarchy for Metanode A, we'll come across a
- // control-only Metaedge in the bridgegraph from Z=>A/B (*). The question
- // is whether this edge is backwards.
- //
- // To answer that question, we follow the chain of adjoining metaedges
- // until we reach the topmost one. In this case, that's the control-only
- // Metaedge Z=>A in the ROOT's metagraph. We determine that this edge
- // is backwards by looking at the topological ordering of ROOT's metagraph
- // (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 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;
- if (adjoiningMetaedge && !bridgeMetaedge.numRegularEdges) {
- // Find the top-most adjoining render metaedge information, and the
- // GroupNode whose metagraph must contain the associated metaedge.
- var topAdjoiningMetaedge = adjoiningMetaedge;
- var topGroupNode = parentNodeInfo.node;
- while (topAdjoiningMetaedge.adjoiningMetaedge) {
- topAdjoiningMetaedge = topAdjoiningMetaedge.adjoiningMetaedge;
- topGroupNode = topGroupNode.parentNode;
- }
- // Check against the topological ordering for the top node. The current
- // bridge metaedge we're evaluating is backwards if its source comes
- // after its destination.
- var ordering = _this.hierarchy.getTopologicalOrdering(topGroupNode.name);
- var e = topAdjoiningMetaedge.metaedge;
- backwards = ordering[e.v] > ordering[e.w];
- }
- // Render backwards control edges as annotations.
- canDrawBridgePath = canDrawBridgePath && !backwards;
- // If we can't make a bridge path for any reason, then we add an
- // annotation instead.
- if (!canDrawBridgePath) {
- childAnnotations.push(new Annotation(otherNode, otherRenderInfo, new RenderMetaedgeInfo(bridgeMetaedge), AnnotationType.SHORTCUT, inbound));
- return;
- }
- // At this point, all conditions have been met for drawing a bridge path.
- // Find or create the IN/OUT node representing otherNode.
- var bridgeContainerName = getBridgeNodeName(inbound, nodeName);
- var bridgeNodeName = getBridgeNodeName(inbound, otherName, nodeName);
- var bridgeNodeRenderInfo = coreGraph.node(bridgeNodeName);
- if (!bridgeNodeRenderInfo) {
- // Find or create the directional container for the bridge node.
- var bridgeContainerInfo = coreGraph.node(bridgeContainerName);
- if (!bridgeContainerInfo) {
- var bridgeContainerNode = {
- // Important node properties.
- name: bridgeContainerName,
- type: graph_1.NodeType.BRIDGE,
- // Unused node properties.
- isGroupNode: false,
- cardinality: 0,
- parentNode: null,
- stats: null,
- include: graph_1.InclusionType.UNSPECIFIED,
- // BridgeNode properties.
- inbound: inbound,
- nodeAttributes: {},
- };
- bridgeContainerInfo =
- new RenderNodeInfo(bridgeContainerNode);
- _this.index[bridgeContainerName] = bridgeContainerInfo;
- coreGraph.setNode(bridgeContainerName, bridgeContainerInfo);
- }
- var bridgeNode = {
- // Important node properties.
- name: bridgeNodeName,
- type: graph_1.NodeType.BRIDGE,
- // Unimportant node properties.
- isGroupNode: false,
- cardinality: 1,
- parentNode: null,
- stats: null,
- include: graph_1.InclusionType.UNSPECIFIED,
- // BridgeNode properties.
- inbound: inbound,
- nodeAttributes: {},
- };
- bridgeNodeRenderInfo = new RenderNodeInfo(bridgeNode);
- _this.index[bridgeNodeName] = bridgeNodeRenderInfo;
- coreGraph.setNode(bridgeNodeName, bridgeNodeRenderInfo);
- // Set bridgeNode to be a graphlib child of the container node.
- coreGraph.setParent(bridgeNodeName, bridgeContainerName);
- bridgeContainerInfo.node.cardinality++;
- }
- // Create and add a bridge render metaedge.
- var bridgeRenderMetaedge = new RenderMetaedgeInfo(bridgeMetaedge);
- bridgeRenderMetaedge.adjoiningMetaedge = adjoiningMetaedge;
- inbound ?
- coreGraph.setEdge(bridgeNodeName, childName, bridgeRenderMetaedge) :
- coreGraph.setEdge(childName, bridgeNodeName, bridgeRenderMetaedge);
- }); // End _.each(bridgegraph.edges).
- // For each bridge container (IN and/or OUT), add structural edges between
- // terminal nodes and that container. A terminal node is one which has no
- // non-bridge edges in the direction of the container.
- //
- // For example, consider a Metanode A which contains two child nodes A/B
- // and A/C. Let's say it has one edge in the metagraph from A/B->A/C, and
- // 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've also added a bridge node Z' (with parent IN)
- // to A, and a bridge edge from Z'->C.
- //
- // +----------------------+
- // | A +---+ |
- // | +------\x3e| C | |
- // | | +---+ |
- // | | ^ |
- // | | | |
- // | | +----|----+ |
- // | | | IN | | |
- // | +---+ | +---+ | |
- // | | B | | | Z'| | |
- // | +---+ | +---+ | |
- // | +---------+ |
- // +----------------------+
- //
- // With no other help, dagre would lay out B and Z' on the same level,
- // because both of them have no incoming edges. In other words, B is a
- // terminal node in the INCOMING direction.
- //
- // But we want to force dagre to lay out Z' (and everything in IN) lower
- // than all non-bridge nodes, so that there's enough room for the bridge
- // edges after they've been adjusted to meet up with paths coming in from
- // outside.
- //
- // To force Z' (and all other bridge nodes) to be lowest in the graph, we
- // identify terminal nodes like B and give them structural edges to
- // a new structural bridge node S which we add to IN.
- //
- // +----------------------+
- // | A +---+ |
- // | +---\x3e| C | |
- // | | +---+ |
- // | +---+ ^ |
- // | | B | | |
- // | +---+ | |
- // | ^ | |
- // | | | |
- // | +----|------|----+ |
- // | |IN | | | |
- // | | +---+ +---+ | |
- // | | | S | | Z'| | |
- // | | +---+ +---+ | |
- // | +----------------+ |
- // +----------------------+
- //
- // This ensures that dagre will lay out the bridge containers strictly at
- // the ends of the graph. The structural edges will never be seen in the
- // visualization except as a debugging aid.
- _.each([true, false], function (inbound) {
- var bridgeContainerName = getBridgeNodeName(inbound, nodeName);
- var bridgeContainerInfo = coreGraph.node(bridgeContainerName);
- if (!bridgeContainerInfo) {
- return;
- }
- _.each(coreGraph.nodes(), function (childName) {
- // Short-circuit if this child is a bridge node or it's not a terminal
- // node in the direction we're interested in.
- var childNodeInfo = coreGraph.node(childName);
- if (childNodeInfo.node.type === graph_1.NodeType.BRIDGE) {
- return;
- }
- var isTerminal = inbound ?
- !coreGraph.predecessors(childName).length :
- !coreGraph.successors(childName).length;
- if (!isTerminal) {
- return;
- }
- // Find or create a bridge node in the container for all structural
- // metaedges. It would have been nice to skip this step and simply
- // set a metaedge between the terminal node and the container node, but
- // in that case, something about the graph upsets dagre.layout()'s
- // longestPath algorithm (was getting errors due to an undefined).
- var structuralNodeName = getBridgeNodeName(inbound, nodeName, 'STRUCTURAL_TARGET');
- var structuralRenderInfo = coreGraph.node(structuralNodeName);
- if (!structuralRenderInfo) {
- var bridgeNode = {
- // Important Node properties.
- name: structuralNodeName,
- type: graph_1.NodeType.BRIDGE,
- // Unimportant Node properties.
- isGroupNode: false,
- cardinality: 1,
- parentNode: null,
- stats: null,
- include: graph_1.InclusionType.UNSPECIFIED,
- // BridgeNode properties.
- inbound: inbound,
- nodeAttributes: {},
- };
- structuralRenderInfo = new RenderNodeInfo(bridgeNode);
- structuralRenderInfo.structural = true;
- _this.index[structuralNodeName] = structuralRenderInfo;
- coreGraph.setNode(structuralNodeName, structuralRenderInfo);
- bridgeContainerInfo.node.cardinality++;
- coreGraph.setParent(structuralNodeName, bridgeContainerName);
- }
- // Create the structural Metaedge and insert it.
- var structuralMetaedgeInfo = new RenderMetaedgeInfo(null);
- structuralMetaedgeInfo.structural = true;
- structuralMetaedgeInfo.weight--; // Reduce weight for dagre layout.
- inbound ?
- coreGraph.setEdge(structuralNodeName, childName, structuralMetaedgeInfo) :
- coreGraph.setEdge(childName, structuralNodeName, structuralMetaedgeInfo);
- });
- });
- };
- return RenderGraphInfo;
- }());
- render.RenderGraphInfo = RenderGraphInfo;
- /**
- * A class for rendering annotation object which contains label
- * about the node embedded as annotation, type of annotation and the location
- * of both the annotation's node and edge.
- *
- * Annotation objects include embedded constants, embedded summary, and
- * edge shortcuts.
- */
- var Annotation = (function () {
- /**
- * Creates a new Annotation.
- *
- * @param node The underlying node this annotation points to.
- * @param renderNodeInfo The render information for the underlying node
- * this annotation points to. This can be null if the annotation
- * denotes an embedding (constant, summary), in which case we
- * use the node property.
- * @param renderMetaedgeInfo The render information for the edge associated
- * with the annotation.
- * @param type The type of the annotation.
- * @param isIn True if it is an in-annotation. False if it is an
- * out-annotation.
- */
- function Annotation(node, renderNodeInfo, renderMetaedgeInfo, type, isIn) {
- this.node = node;
- this.renderNodeInfo = renderNodeInfo;
- this.renderMetaedgeInfo = renderMetaedgeInfo;
- this.annotationType = type;
- // Properties specified by layout
- this.dx = 0;
- this.dy = 0;
- this.width = 0;
- this.height = 0;
- // Properties needed for generating an ID for the edge's path element if
- // this annotation is associated with a metaedge.
- if (renderMetaedgeInfo && renderMetaedgeInfo.metaedge) {
- this.v = renderMetaedgeInfo.metaedge.v;
- this.w = renderMetaedgeInfo.metaedge.w;
- }
- this.isIn = isIn;
- this.points = [];
- }
- return Annotation;
- }());
- render.Annotation = Annotation;
- ;
- var AnnotationType;
- (function (AnnotationType) {
- AnnotationType[AnnotationType["SHORTCUT"] = 0] = "SHORTCUT";
- AnnotationType[AnnotationType["CONSTANT"] = 1] = "CONSTANT";
- AnnotationType[AnnotationType["SUMMARY"] = 2] = "SUMMARY";
- AnnotationType[AnnotationType["ELLIPSIS"] = 3] = "ELLIPSIS";
- })(AnnotationType = render.AnnotationType || (render.AnnotationType = {}));
- ;
- /**
- * Manages a list of annotations. Two will be used for each
- * RenderNodeInfo, one for in annotations and one for out annotations.
- */
- var AnnotationList = (function () {
- function AnnotationList() {
- this.list = [];
- this.nodeNames = {};
- }
- /**
- * Append an annotation to the list, or a stand-in ellipsis annotation instead
- * if this would make it too many.
- */
- AnnotationList.prototype.push = function (annotation) {
- if (annotation.node.name in this.nodeNames) {
- return; // Skip duplicate annotation.
- }
- this.nodeNames[annotation.node.name] = true;
- if (this.list.length < PARAMS.maxAnnotations) {
- this.list.push(annotation);
- return;
- }
- var lastAnnotation = this.list[this.list.length - 1];
- if (lastAnnotation.annotationType === AnnotationType.ELLIPSIS) {
- var ellipsisNode_1 = lastAnnotation.node;
- ellipsisNode_1.setNumMoreNodes(++ellipsisNode_1.numMoreNodes);
- return;
- }
- var ellipsisNode = new tf.graph.EllipsisNodeImpl(1);
- 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.
- */
- var RenderNodeInfo = (function () {
- function RenderNodeInfo(node) {
- this.node = node;
- this.expanded = false;
- this.inAnnotations = new AnnotationList();
- this.outAnnotations = new AnnotationList();
- // Params specified by layout
- this.x = 0;
- this.y = 0;
- this.width = 0;
- this.height = 0;
- this.inboxWidth = 0;
- this.outboxWidth = 0;
- this.excluded = false;
- // Params for bridge paths.
- this.structural = false;
- // Params for node box.
- this.labelOffset = 0;
- this.radius = 0;
- // Params for expanded node
- this.labelHeight = 0;
- this.paddingTop = 0;
- this.paddingLeft = 0;
- this.paddingRight = 0;
- this.paddingBottom = 0;
- this.isInExtract = false;
- this.isOutExtract = false;
- this.coreBox = { width: 0, height: 0 };
- // By default, we don't fade nodes out. Default to false for safety.
- this.isFadedOut = false;
- }
- RenderNodeInfo.prototype.isInCore = function () {
- return !this.isInExtract && !this.isOutExtract;
- };
- return RenderNodeInfo;
- }());
- render.RenderNodeInfo = RenderNodeInfo;
- /**
- * Contains rendering information about a Metaedge from the underlying
- * hierarchical graph. It may be from either a metagraph or a bridgegraph.
- */
- var RenderMetaedgeInfo = (function () {
- function RenderMetaedgeInfo(metaedge) {
- this.metaedge = metaedge;
- this.adjoiningMetaedge = null;
- this.structural = false;
- this.weight = 1;
- this.isFadedOut = false;
- }
- return RenderMetaedgeInfo;
- }());
- render.RenderMetaedgeInfo = RenderMetaedgeInfo;
- function addInAnnotation(node, predecessor, predecessorRenderInfo, edge, type) {
- var annotation = new Annotation(predecessor, predecessorRenderInfo, edge, type, true);
- node.inAnnotations.push(annotation);
- }
- function addOutAnnotation(node, successor, successorRenderInfo, edge, type) {
- var annotation = new Annotation(successor, successorRenderInfo, edge, type, false);
- node.outAnnotations.push(annotation);
- }
- function setGraphDepth(graph, depth) {
- _.each(graph.nodes(), function (nodeName) {
- var child = graph.node(nodeName);
- child.expanded = depth > 1; // set all child of depth 1 to collapsed
- if (depth > 0) {
- switch (child.node.type) {
- case graph_1.NodeType.META:
- case graph_1.NodeType.SERIES:
- setGroupNodeDepth(child, depth - 1);
- break;
- }
- }
- });
- }
- ;
- var RenderGroupNodeInfo = (function (_super) {
- __extends(RenderGroupNodeInfo, _super);
- function RenderGroupNodeInfo(groupNode) {
- var _this = _super.call(this, groupNode) || this;
- var metagraph = groupNode.metagraph;
- var gl = metagraph.graph();
- _this.coreGraph =
- graph_1.createGraph(gl.name, graph_1.GraphType.CORE, { compound: true });
- _this.inExtractBox = { width: 0, height: 0 };
- _this.outExtractBox = { width: 0, height: 0 };
- _this.isolatedInExtract = [];
- _this.isolatedOutExtract = [];
- return _this;
- }
- return RenderGroupNodeInfo;
- }(RenderNodeInfo));
- render.RenderGroupNodeInfo = RenderGroupNodeInfo;
- function setGroupNodeDepth(renderInfo, depth) {
- if (renderInfo.coreGraph) {
- setGraphDepth(renderInfo.coreGraph, depth);
- }
- }
- /**
- * Remove an edge from the graph and add annotations to both ends of the edge.
- *
- * @param The core graph.
- * @param v Source name.
- * @param w Sink name.
- */
- function createShortcut(graph, v, w) {
- var src = graph.node(v);
- var sink = graph.node(w);
- var edge = graph.edge(v, w);
- // If either of the nodes is explicitly included in the main graph and
- // both nodes are in the main graph then do not create the shortcut
- // and instead keep the real edge.
- if ((src.node.include === graph_1.InclusionType.INCLUDE ||
- sink.node.include === graph_1.InclusionType.INCLUDE) &&
- src.node.include !== graph_1.InclusionType.EXCLUDE &&
- sink.node.include !== graph_1.InclusionType.EXCLUDE) {
- return;
- }
- // Add each annotation.
- addOutAnnotation(src, sink.node, sink, edge, AnnotationType.SHORTCUT);
- addInAnnotation(sink, src.node, src, edge, AnnotationType.SHORTCUT);
- // Remove the edge from the core graph.
- graph.removeEdge(v, w);
- }
- /**
- * Remove edges from a node, and set its isOutExtract property to true,
- * and remove the node and move it to isolatedOutExtract.
- *
- * If detachAllEdgesForHighDegree or forceDetach is true, extract all of its
- * edges. Otherwise, only extract all in-edges.
- */
- function makeOutExtract(renderNode, n, forceDetach) {
- var graph = renderNode.coreGraph;
- var child = graph.node(n);
- child.isOutExtract = true;
- _.each(graph.predecessors(n), function (p, index) {
- createShortcut(graph, p, n);
- });
- if (PARAMS.detachAllEdgesForHighDegree || forceDetach) {
- _.each(graph.successors(n), function (s, index) {
- createShortcut(graph, n, s);
- });
- }
- // Remove the node from the core graph if it no longer has neighbors.
- if (graph.neighbors(n).length === 0) {
- child.node.include = graph_1.InclusionType.EXCLUDE;
- renderNode.isolatedOutExtract.push(child);
- graph.removeNode(n);
- }
- }
- /**
- * Remove edges from a node, set its isInExtract property to true,
- * and remove the node and move it to isolatedInExtract.
- *
- * If detachAllEdgesForHighDegree or forceDetach is true, extract all of its
- * edges. Otherwise, only remove all out-edges.
- */
- function makeInExtract(renderNode, n, forceDetach) {
- var graph = renderNode.coreGraph;
- var child = graph.node(n);
- child.isInExtract = true;
- _.each(graph.successors(n), function (s, index) {
- createShortcut(graph, n, s);
- });
- if (PARAMS.detachAllEdgesForHighDegree || forceDetach) {
- _.each(graph.predecessors(n), function (p, index) {
- createShortcut(graph, p, n);
- });
- }
- // Remove the node from the core graph if it no longer has neighbors.
- if (graph.neighbors(n).length === 0) {
- child.node.include = graph_1.InclusionType.EXCLUDE;
- renderNode.isolatedInExtract.push(child);
- graph.removeNode(n);
- }
- }
- render.makeInExtract = makeInExtract;
- /**
- * Check whether the node's type is a member of the given list of types.
- *
- * @param node Node.
- * @param types List of type to match.
- */
- function hasTypeIn(node, types) {
- if (node.type === graph_1.NodeType.OP) {
- for (var i = 0; i < types.length; i++) {
- if (node.op === types[i]) {
- return true;
- }
- }
- }
- else if (node.type === graph_1.NodeType.META) {
- var rootOpNode = node.getRootOp();
- if (rootOpNode) {
- for (var i = 0; i < types.length; i++) {
- if (rootOpNode.op === types[i]) {
- return true;
- }
- }
- }
- }
- return false;
- }
- /** Move nodes that are specified to be excluded out of the core graph. */
- function extractSpecifiedNodes(renderNode) {
- var graph = renderNode.coreGraph;
- _.each(graph.nodes(), function (n) {
- var renderInfo = graph.node(n);
- if (renderInfo.node.include === graph_1.InclusionType.EXCLUDE) {
- if (renderNode.coreGraph.outEdges(n).length >
- renderNode.coreGraph.inEdges(n).length) {
- makeOutExtract(renderNode, n, true);
- }
- else {
- makeInExtract(renderNode, n, true);
- }
- }
- });
- }
- /** Remove edges from pre-defined out-extract patterns */
- function extractPredefinedSink(renderNode) {
- var graph = renderNode.coreGraph;
- _.each(graph.nodes(), function (n) {
- var renderInfo = graph.node(n);
- if (renderInfo.node.include !== graph_1.InclusionType.UNSPECIFIED) {
- return;
- }
- if (hasTypeIn(renderInfo.node, PARAMS.outExtractTypes)) {
- makeOutExtract(renderNode, n);
- }
- });
- }
- /** Remove edges from pre-defined in-extract patterns */
- function extractPredefinedSource(renderNode) {
- var graph = renderNode.coreGraph;
- _.each(graph.nodes(), function (n) {
- var renderInfo = graph.node(n);
- if (renderInfo.node.include !== graph_1.InclusionType.UNSPECIFIED) {
- return;
- }
- if (hasTypeIn(renderInfo.node, PARAMS.inExtractTypes)) {
- makeInExtract(renderNode, n);
- }
- });
- }
- /** Extract nodes deemed to have either high in-degree or high out-degree. */
- function extractHighInOrOutDegree(renderNode) {
- var graph = renderNode.coreGraph;
- // Create mappings from node to in and out degrees. Count the number of valid
- // nodes along the way.
- var nodeToInDegree = {};
- var nodeToOutDegree = {};
- var validNodeCount = 0;
- _.each(graph.nodes(), function (currentNode) {
- if (graph.node(currentNode).node.include !== graph_1.InclusionType.UNSPECIFIED) {
- // This node is not included in the first place.
- return;
- }
- // Count the in and out degrees based on only regular edges, unless there
- // are no regular edges, in which case use the number of control edges.
- // This is done so that control edges don't affect if nodes are extracted
- // from the core graph, unless the node is only used for control.
- var inDegree = _.reduce(graph.predecessors(currentNode), function (inDegree, pred) {
- var metaedge = graph.edge(pred, currentNode).metaedge;
- return inDegree + (metaedge.numRegularEdges ? 1 : 0);
- }, 0);
- if (inDegree === 0 && graph.predecessors(currentNode).length > 0) {
- inDegree = graph.predecessors(currentNode).length;
- }
- var outDegree = _.reduce(graph.successors(currentNode), function (outDegree, succ) {
- var metaedge = graph.edge(currentNode, succ).metaedge;
- return outDegree + (metaedge.numRegularEdges ? 1 : 0);
- }, 0);
- if (outDegree === 0 && graph.successors(currentNode).length > 0) {
- outDegree = graph.successors(currentNode).length;
- }
- // Store the in and out degrees of this node to avoid recomputing.
- nodeToInDegree[currentNode] = inDegree;
- nodeToOutDegree[currentNode] = outDegree;
- validNodeCount++;
- });
- if (validNodeCount < PARAMS.minNodeCountForExtraction) {
- // This graph has few nodes. Do not extract any nodes.
- return;
- }
- // We only extract if the node has a min in or out degree greater than this.
- var minUpperBound = PARAMS.minDegreeForExtraction - 1;
- // Mark for extraction nodes with in-degree > Q3 + (Q3 - Q1).
- var q3Index = Math.round(validNodeCount * 0.75);
- var q1Index = Math.round(validNodeCount * 0.25);
- var sortedByInDegree = Object.keys(nodeToInDegree).sort(function (node0, node1) {
- return nodeToInDegree[node0] - nodeToInDegree[node1];
- });
- var inDegreeQ3 = nodeToInDegree[sortedByInDegree[q3Index]];
- var inDegreeQ1 = nodeToInDegree[sortedByInDegree[q1Index]];
- var inDegreeUpperBound = inDegreeQ3 + inDegreeQ3 - inDegreeQ1;
- // Only extract if the upper bound is high enough.
- inDegreeUpperBound = Math.max(inDegreeUpperBound, minUpperBound);
- for (var i = validNodeCount - 1; nodeToInDegree[sortedByInDegree[i]] > inDegreeUpperBound; i--) {
- // Extract a high in-degree node.
- makeInExtract(renderNode, sortedByInDegree[i]);
- }
- // Mark for extraction nodes with out-degree > Q3 + (Q3 - Q1) * 4.
- var sortedByOutDegree = Object.keys(nodeToOutDegree).sort(function (node0, node1) {
- return nodeToOutDegree[node0] - nodeToOutDegree[node1];
- });
- var outDegreeQ3 = nodeToOutDegree[sortedByOutDegree[q3Index]];
- var outDegreeQ1 = nodeToOutDegree[sortedByOutDegree[q1Index]];
- // The upper bound for extracting out-degree nodes is higher than that for
- // extracting in-degree ones (Note the "* 4") because, in practice, some
- // graphs look worse with a smaller out-degree bound. For instance, a smaller
- // out-degree bound removes the convolution nodes from cifar 10 train's graph.
- var outDegreeUpperBound = outDegreeQ3 + (outDegreeQ3 - outDegreeQ1) * 4;
- // Only extract if the upper bound is high enough.
- outDegreeUpperBound = Math.max(outDegreeUpperBound, minUpperBound);
- for (var i = validNodeCount - 1; nodeToOutDegree[sortedByOutDegree[i]] > outDegreeUpperBound; i--) {
- var node = graph.node(sortedByOutDegree[i]);
- if (!node || node.isInExtract) {
- // This node has already been extracted due to high in-degree. It might
- // have been removed from the graph in general (during in-degree
- // extraction) due to a lack of neighbors. Do not extract this node twice.
- continue;
- }
- // Extract a high out-degree node that has not already been extracted.
- makeOutExtract(renderNode, sortedByOutDegree[i]);
- }
- }
- /** Remove control edges from nodes that have too many control edges */
- function removeControlEdges(renderNode) {
- var graph = renderNode.coreGraph;
- // Collect control edges into a map by node name.
- var map = {};
- _.each(graph.edges(), function (e) {
- if (!graph.edge(e).metaedge.numRegularEdges) {
- (map[e.v] = map[e.v] || []).push(e);
- (map[e.w] = map[e.w] || []).push(e);
- }
- });
- // For each node with too many control edges, turn them into annotations.
- _.each(map, function (edges, nodeName) {
- if (edges.length > PARAMS.maxControlDegree) {
- _.each(edges, function (e) { return createShortcut(graph, e.v, e.w); });
- }
- });
- }
- /**
- * Given an integer, picks a hue that is far apart from other colors.
- * The formula for picking color that avoid collision is:
- * hue = (color range * golden ratio * index) % color range
- */
- function mapIndexToHue(id) {
- var GOLDEN_RATIO = 1.61803398875;
- // Hue of 0 is reserved for the gray nodes.
- var MIN_HUE = 1;
- var MAX_HUE = 359;
- var COLOR_RANGE = MAX_HUE - MIN_HUE;
- return MIN_HUE + ((COLOR_RANGE * GOLDEN_RATIO * id) % COLOR_RANGE);
- }
- render.mapIndexToHue = mapIndexToHue;
- ;
- /**
- * Remove edges and add to annotation instead.
- *
- * For root node, consider predefined types for source and sink.
- * We do not extract predefined type from non-root so that Variables and the
- * sgd node (op type = 'NoOp') do not get extract from inside own group.
- *
- * The order of extraction is important here as swapping the order can totally
- * screw up the graph layout.
- *
- * @param {Render.Node} renderNode Node to manipulate.
- */
- function extractHighDegrees(renderNode) {
- extractSpecifiedNodes(renderNode);
- if (PARAMS.outExtractTypes) {
- extractPredefinedSink(renderNode);
- }
- // This has to come before extract high in-degree to protect the core part
- // that takes many variables.
- if (PARAMS.inExtractTypes) {
- extractPredefinedSource(renderNode);
- }
- extractHighInOrOutDegree(renderNode);
- if (PARAMS.maxControlDegree) {
- removeControlEdges(renderNode);
- }
- // Extract isolated nodes, which can be
- // (1) source-like and sink-like nodes that are not originally isolated but
- // become isolated after further removal.
- // (2) isolated nodes with annotations on one-side. These might be either
- // - nodes that originally have high out-degree but because we remove
- // high in-degree nodes first, they no longer have high in-degree when
- // we check. (Detecting all high-degree before removing also leads to
- // another problem.)
- // - nodes that do not have high degree, but their neighbors are all
- // extracted, so it might make sense to extract them too.
- var graph = renderNode.coreGraph;
- _.each(graph.nodes(), function (n) {
- var child = graph.node(n);
- var degree = graph.neighbors(n).length;
- if (child.node.include !== graph_1.InclusionType.UNSPECIFIED) {
- return;
- }
- if (degree === 0) {
- var hasOutAnnotations = child.outAnnotations.list.length > 0;
- var hasInAnnotations = child.inAnnotations.list.length > 0;
- if (child.isInExtract) {
- // This case only happens if detachAllEdgesForHighDegree is false.
- // (Otherwise all source-like nodes are all isolated already.)
- renderNode.isolatedInExtract.push(child);
- child.node.include = graph_1.InclusionType.EXCLUDE;
- graph.removeNode(n);
- }
- else if (child.isOutExtract) {
- // This case only happens if detachAllEdgesForHighDegree is false.
- // // (Otherwise all sink-like nodes are all isolated already.)
- renderNode.isolatedOutExtract.push(child);
- child.node.include = graph_1.InclusionType.EXCLUDE;
- graph.removeNode(n);
- }
- else if (PARAMS.extractIsolatedNodesWithAnnotationsOnOneSide) {
- if (hasOutAnnotations && !hasInAnnotations) {
- child.isInExtract = true; // for ones with high out-annotations
- renderNode.isolatedInExtract.push(child);
- child.node.include = graph_1.InclusionType.EXCLUDE;
- graph.removeNode(n);
- }
- else if (hasInAnnotations && !hasOutAnnotations) {
- child.isOutExtract = true; // for ones with high in-annotations
- renderNode.isolatedOutExtract.push(child);
- child.node.include = graph_1.InclusionType.EXCLUDE;
- graph.removeNode(n);
- }
- else {
- // if a low degree node has both in- & out- annotations, do nothing
- // because it is unclear which side it should go to.
- }
- }
- }
- });
- }
- })(render = graph_1.render || (graph_1.render = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // close module tf.graph.render
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph) {
- var scene;
- (function (scene) {
- var annotation;
- (function (annotation_1) {
- /**
- * Populate a given annotation container group
- *
- * <g class='{in|out}-annotations'></g>
- *
- * with annotation group of the following structure:
- *
- * <g class='annotation'>
- * <g class='annotation-node'>
- * \x3c!--
- * Content here determined by Scene.node.buildGroup.
- * --\x3e
- * </g>
- * </g>
- *
- * @param container selection of the container.
- * @param annotationData node.{in|out}Annotations
- * @param d node to build group for.
- * @param sceneElement <tf-graph-scene> polymer element.
- * @return selection of appended objects
- */
- function buildGroup(container, annotationData, d, sceneElement) {
- // Select all children and join with data.
- var annotationGroups = container
- .selectAll(function () {
- // using d3's selector function
- // See https://github.com/mbostock/d3/releases/tag/v2.0.0
- // (It's not listed in the d3 wiki.)
- return this.childNodes;
- })
- .data(annotationData.list, function (d) { return d.node.name; });
- annotationGroups.enter()
- .append('g')
- .attr('data-name', function (a) { return a.node.name; })
- .each(function (a) {
- var aGroup = d3.select(this);
- // Add annotation to the index in the scene
- sceneElement.addAnnotationGroup(a, d, aGroup);
- // Append annotation edge
- var edgeType = scene.Class.Annotation.EDGE;
- var metaedge = a.renderMetaedgeInfo && a.renderMetaedgeInfo.metaedge;
- if (metaedge && !metaedge.numRegularEdges) {
- edgeType += ' ' + scene.Class.Annotation.CONTROL_EDGE;
- }
- // If any edges are reference edges, add the reference edge class.
- if (metaedge && metaedge.numRefEdges) {
- edgeType += ' ' + scene.Class.Edge.REF_LINE;
- }
- scene.edge.appendEdge(aGroup, a, sceneElement, edgeType);
- if (a.annotationType !== graph.render.AnnotationType.ELLIPSIS) {
- addAnnotationLabelFromNode(aGroup, a);
- buildShape(aGroup, a);
- }
- else {
- addAnnotationLabel(aGroup, a.node.name, a, scene.Class.Annotation.ELLIPSIS);
- }
- });
- annotationGroups
- .attr('class', function (a) {
- return scene.Class.Annotation.GROUP + ' ' +
- annotationToClassName(a.annotationType) + ' ' +
- scene.node.nodeClass(a);
- })
- .each(function (a) {
- var aGroup = d3.select(this);
- 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
- sceneElement.removeAnnotationGroup(a, d, aGroup);
- })
- .remove();
- return annotationGroups;
- }
- annotation_1.buildGroup = buildGroup;
- ;
- /**
- * Maps an annotation enum to a class name used in css rules.
- */
- function annotationToClassName(annotationType) {
- return (graph.render.AnnotationType[annotationType] || '').toLowerCase() || null;
- }
- function buildShape(aGroup, a) {
- if (a.annotationType === graph.render.AnnotationType.SUMMARY) {
- var summary = scene.selectOrCreateChild(aGroup, 'use');
- summary.attr({
- 'class': 'summary',
- 'xlink:href': '#summary-icon',
- 'cursor': 'pointer'
- });
- }
- else {
- var shape = scene.node.buildShape(aGroup, a, scene.Class.Annotation.NODE);
- // add title tag to get native tooltips
- scene.selectOrCreateChild(shape, 'title').text(a.node.name);
- }
- }
- function addAnnotationLabelFromNode(aGroup, a) {
- var namePath = a.node.name.split('/');
- var text = namePath[namePath.length - 1];
- return addAnnotationLabel(aGroup, text, a, null);
- }
- function addAnnotationLabel(aGroup, label, a, additionalClassNames) {
- var classNames = scene.Class.Annotation.LABEL;
- if (additionalClassNames) {
- classNames += ' ' + additionalClassNames;
- }
- var txtElement = aGroup.append('text')
- .attr('class', classNames)
- .attr('dy', '.35em')
- .attr('text-anchor', a.isIn ? 'end' : 'start')
- .text(label);
- return tf.graph.scene.node.enforceLabelWidth(txtElement, -1);
- }
- function addInteraction(selection, d, annotation, sceneElement) {
- selection
- .on('mouseover', function (a) {
- sceneElement.fire('annotation-highlight', { name: a.node.name, hostName: d.node.name });
- })
- .on('mouseout', function (a) {
- sceneElement.fire('annotation-unhighlight', { name: a.node.name, hostName: d.node.name });
- })
- .on('click', function (a) {
- // Stop this event's propagation so that it isn't also considered a
- // graph-select.
- d3.event.stopPropagation();
- sceneElement.fire('annotation-select', { name: a.node.name, hostName: d.node.name });
- });
- 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)));
- }
- }
- ;
- /**
- * Adjust annotation's position.
- *
- * @param aGroup selection of a 'g.annotation' element.
- * @param d Host node data.
- * @param a annotation node data.
- * @param sceneElement <tf-graph-scene> polymer element.
- */
- 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 !== graph.render.AnnotationType.ELLIPSIS) {
- scene.node.stylize(aGroup, a.renderNodeInfo, sceneElement, scene.Class.Annotation.NODE);
- }
- if (a.annotationType === graph.render.AnnotationType.SUMMARY) {
- // Update the width of the annotation to give space for the image.
- a.width += 10;
- }
- // label position
- aGroup.select('text.' + scene.Class.Annotation.LABEL).transition().attr({
- x: cx + a.dx + (a.isIn ? -1 : 1) * (a.width / 2 + a.labelOffset),
- y: d.y + a.dy
- });
- // Some annotations (such as summary) are represented using a 12x12 image tag.
- // 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.
- aGroup.select('use.summary').transition().attr({
- x: cx + a.dx - 3,
- y: d.y + a.dy - 6
- });
- // Node position (only one of the shape selection will be non-empty.)
- scene.positionEllipse(aGroup.select('.' + scene.Class.Annotation.NODE + ' ellipse'), cx + a.dx, d.y + a.dy, a.width, a.height);
- scene.positionRect(aGroup.select('.' + scene.Class.Annotation.NODE + ' rect'), cx + a.dx, d.y + a.dy, a.width, a.height);
- scene.positionRect(aGroup.select('.' + scene.Class.Annotation.NODE + ' use'), cx + a.dx, d.y + a.dy, a.width, a.height);
- // Edge position
- aGroup.select('path.' + scene.Class.Annotation.EDGE).transition().attr('d', function (a) {
- // map relative position to absolute position
- var points = a.points.map(function (p) { return { x: p.dx + cx, y: p.dy + d.y }; });
- return scene.edge.interpolate(points);
- });
- }
- ;
- })(annotation = scene.annotation || (scene.annotation = {}));
- })(scene = graph.scene || (graph.scene = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // close module
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph) {
- var scene;
- (function (scene) {
- var contextmenu;
- (function (contextmenu) {
- /**
- * Returns the event listener, which can be used as an argument for the d3
- * selection.on function. Renders the context menu that is to be displayed
- * in response to the event.
- */
- function getMenu(menu) {
- var menuSelection = d3.select('.context-menu');
- // Close the menu when anything else is clicked.
- d3.select('body').on('click.context', function () { menuSelection.style('display', 'none'); });
- // Function called to populate the context menu.
- return function (data, index) {
- var _this = this;
- // Position and display the menu.
- var event = d3.event;
- menuSelection.style({
- 'display': 'block',
- 'left': (event.layerX + 1) + 'px',
- 'top': (event.layerY + 1) + 'px'
- });
- // Stop the event from propagating further.
- event.preventDefault();
- event.stopPropagation();
- // Add provided items to the context menu.
- menuSelection.html('');
- var list = menuSelection.append('ul');
- list.selectAll('li')
- .data(menu)
- .enter()
- .append('li')
- .html(function (d) { return d.title(data); })
- .on('click', function (d, i) {
- d.action(_this, data, index);
- menuSelection.style('display', 'none');
- });
- };
- }
- contextmenu.getMenu = getMenu;
- ;
- })(contextmenu = scene.contextmenu || (scene.contextmenu = {}));
- })(scene = graph.scene || (graph.scene = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // close module
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph_1) {
- var scene;
- (function (scene) {
- var edge;
- (function (edge) {
- /** Delimiter between dimensions when showing sizes of tensors. */
- var TENSOR_SHAPE_DELIM = '×';
- /** The minimum stroke width of an edge. */
- edge.MIN_EDGE_WIDTH = 0.75;
- /** The maximum stroke width of an edge. */
- edge.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];
- edge.EDGE_WIDTH_SCALE = d3.scale.pow()
- .exponent(EDGE_WIDTH_SCALE_EXPONENT)
- .domain(DOMAIN_EDGE_WIDTH_SCALE)
- .range([edge.MIN_EDGE_WIDTH, edge.MAX_EDGE_WIDTH])
- .clamp(true);
- var arrowheadMap = d3.scale.quantize().domain([edge.MIN_EDGE_WIDTH, edge.MAX_EDGE_WIDTH]).range([
- 'small', 'medium', 'large', 'xlarge'
- ]);
- /** Minimum stroke width to put edge labels in the middle of edges */
- var CENTER_EDGE_LABEL_MIN_STROKE_WIDTH = 2.5;
- function getEdgeKey(edgeObj) {
- return edgeObj.v + graph_1.EDGE_KEY_DELIM + edgeObj.w;
- }
- edge.getEdgeKey = getEdgeKey;
- /**
- * Select or Create a 'g.edges' group to a given sceneGroup
- * and builds a number of 'g.edge' groups inside the group.
- *
- * Structure Pattern:
- *
- * <g class='edges'>
- * <g class='edge'>
- * <path class='edgeline'/>
- * </g>
- * ...
- * </g>
- *
- *
- * @param sceneGroup container
- * @param graph
- * @param sceneElement <tf-graph-scene> polymer element.
- * @return selection of the created nodeGroups
- */
- function buildGroup(sceneGroup, graph, sceneElement) {
- var edges = [];
- edges = _.reduce(graph.edges(), function (edges, edgeObj) {
- var edgeLabel = graph.edge(edgeObj);
- edges.push({
- v: edgeObj.v,
- w: edgeObj.w,
- label: edgeLabel
- });
- return edges;
- }, edges);
- var container = scene.selectOrCreateChild(sceneGroup, 'g', scene.Class.Edge.CONTAINER);
- // Select all children and join with data.
- // (Note that all children of g.edges are g.edge)
- var edgeGroups = container.selectAll(function () {
- // using d3's selector function
- // See https://github.com/mbostock/d3/releases/tag/v2.0.0
- // (It's not listed in the d3 wiki.)
- return this.childNodes;
- }).data(edges, getEdgeKey);
- // Make edges a group to support rendering multiple lines for metaedge
- edgeGroups.enter()
- .append('g')
- .attr('class', scene.Class.Edge.GROUP)
- .attr('data-edge', getEdgeKey)
- .each(function (d) {
- var edgeGroup = d3.select(this);
- d.label.edgeGroup = edgeGroup;
- // index node group for quick highlighting
- sceneElement._edgeGroupIndex[getEdgeKey(d)] = edgeGroup;
- // Add line during enter because we're assuming that type of line
- // normally does not change.
- appendEdge(edgeGroup, d, sceneElement);
- });
- edgeGroups.each(position);
- edgeGroups.each(function (d) {
- stylize(d3.select(this), d, sceneElement);
- });
- edgeGroups.exit()
- .each(function (d) {
- delete sceneElement._edgeGroupIndex[getEdgeKey(d)];
- })
- .remove();
- return edgeGroups;
- }
- edge.buildGroup = buildGroup;
- ;
- /**
- * Returns the label for the given base edge.
- * The label is the shape of the underlying tensor.
- */
- function getLabelForBaseEdge(baseEdge, renderInfo) {
- var node = renderInfo.getNodeByName(baseEdge.v);
- if (node.outputShapes == null || node.outputShapes.length === 0) {
- return null;
- }
- var shape = node.outputShapes[baseEdge.outputTensorIndex];
- 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.getLabelForBaseEdge = getLabelForBaseEdge;
- /**
- * 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;
- return isMultiEdge ?
- metaedge.baseEdgeList.length + ' tensors' :
- getLabelForBaseEdge(metaedge.baseEdgeList[0], renderInfo);
- }
- edge.getLabelForEdge = getLabelForEdge;
- /**
- * Shortens the path enought such that the tip of the start/end marker will
- * point to the start/end of the path. The marker can be of arbitrary size.
- *
- * @param points Array of path control points.
- * @param marker D3 selection of the <marker> svg element.
- * @param isStart Is the marker a `start-marker`. If false, the marker is
- * an `end-marker`.
- * @return The new array of control points.
- */
- function adjustPathPointsForMarker(points, marker, isStart) {
- var lineFunc = d3.svg.line()
- .x(function (d) { return d.x; })
- .y(function (d) { return d.y; });
- var path = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'path'))
- .attr('d', lineFunc(points));
- var markerWidth = +marker.attr('markerWidth');
- var viewBox = marker.attr('viewBox').split(' ').map(Number);
- var viewBoxWidth = viewBox[2] - viewBox[0];
- var refX = +marker.attr('refX');
- var pathNode = path.node();
- if (isStart) {
- var fractionStickingOut = refX / viewBoxWidth;
- var length_1 = markerWidth * fractionStickingOut;
- var point = pathNode.getPointAtLength(length_1);
- // Figure out how many segments of the path we need to remove in order
- // to shorten the path.
- var segIndex = pathNode.getPathSegAtLength(length_1);
- // Update the very first segment.
- points[segIndex - 1] = { x: point.x, y: point.y };
- // Ignore every point before segIndex - 1.
- return points.slice(segIndex - 1);
- }
- else {
- var fractionStickingOut = 1 - refX / viewBoxWidth;
- var length_2 = pathNode.getTotalLength() - markerWidth * fractionStickingOut;
- var point = pathNode.getPointAtLength(length_2);
- // Figure out how many segments of the path we need to remove in order
- // to shorten the path.
- var segIndex = pathNode.getPathSegAtLength(length_2);
- // Update the very last segment.
- points[segIndex] = { x: point.x, y: point.y };
- // Ignore every point after segIndex.
- return points.slice(0, segIndex + 1);
- }
- }
- /**
- * For a given d3 selection and data object, create a path to represent the
- * edge described in d.label.
- *
- * If d.label is defined, it will be a RenderMetaedgeInfo instance. It
- * 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, 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);
- var path = edgeGroup.append('path')
- .attr({
- 'id': pathId,
- 'class': edgeClass,
- })
- .style({ 'stroke-width': strokeWidth + 'px' });
- // Check if there is a reference edge and add an arrowhead of the right size.
- if (d.label && d.label.metaedge && d.label.metaedge.numRefEdges) {
- var markerId = "ref-arrowhead-" + arrowheadMap(strokeWidth);
- path.style('marker-start', "url(#" + markerId + ")");
- d.label.startMarkerId = markerId;
- }
- 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;
- }
- // Put edge label in the middle of edge only if the edge is thick enough.
- var baseline = strokeWidth > CENTER_EDGE_LABEL_MIN_STROKE_WIDTH ?
- 'central' :
- 'text-after-edge';
- edgeGroup.append('text')
- .append('textPath')
- .attr({
- 'xlink:href': '#' + pathId,
- 'startOffset': '50%',
- 'text-anchor': 'middle',
- 'dominant-baseline': 'central'
- })
- .text(labelForEdge);
- }
- edge.appendEdge = appendEdge;
- ;
- edge.interpolate = d3.svg.line()
- .interpolate('basis')
- .x(function (d) { return d.x; })
- .y(function (d) { return d.y; });
- /**
- * Returns a tween interpolator for the endpoint of an edge path.
- */
- function getEdgePathInterpolator(d, i, a) {
- var renderMetaedgeInfo = d.label;
- var adjoiningMetaedge = renderMetaedgeInfo.adjoiningMetaedge;
- var points = renderMetaedgeInfo.points;
- // Adjust the path so that start/end markers point to the end
- // of the path.
- if (d.label.startMarkerId) {
- points = adjustPathPointsForMarker(points, d3.select('#' + d.label.startMarkerId), true);
- }
- if (d.label.endMarkerId) {
- points = adjustPathPointsForMarker(points, d3.select('#' + d.label.endMarkerId), false);
- }
- if (!adjoiningMetaedge) {
- return d3.interpolate(a, edge.interpolate(points));
- }
- var renderPath = this;
- // Get the adjoining path that matches the adjoining metaedge.
- var adjoiningPath = (adjoiningMetaedge.edgeGroup.node()
- .firstChild);
- // Find the desired SVGPoint along the adjoining path, then convert those
- // coordinates into the space of the renderPath using its Current
- // Transformation Matrix (CTM).
- var inbound = renderMetaedgeInfo.metaedge.inbound;
- return function (t) {
- var adjoiningPoint = adjoiningPath
- .getPointAtLength(inbound ? adjoiningPath.getTotalLength() : 0)
- .matrixTransform(adjoiningPath.getCTM())
- .matrixTransform(renderPath.getCTM().inverse());
- // Update the relevant point in the renderMetaedgeInfo's points list, then
- // re-interpolate the path.
- var index = inbound ? 0 : points.length - 1;
- points[index].x = adjoiningPoint.x;
- points[index].y = adjoiningPoint.y;
- var dPath = edge.interpolate(points);
- return dPath;
- };
- }
- function position(d) {
- d3.select(this)
- .select('path.' + scene.Class.Edge.LINE)
- .transition()
- .attrTween('d', getEdgePathInterpolator);
- }
- ;
- /**
- * For a given d3 selection and data object, mark the edge as a control
- * dependency if it contains only control edges.
- *
- * d's label property will be a RenderMetaedgeInfo object.
- */
- function stylize(edgeGroup, d, stylize) {
- edgeGroup.classed('faded', d.label.isFadedOut);
- var metaedge = d.label.metaedge;
- edgeGroup.select('path.' + scene.Class.Edge.LINE)
- .classed('control-dep', metaedge && !metaedge.numRegularEdges);
- }
- ;
- })(edge = scene.edge || (scene.edge = {}));
- })(scene = graph_1.scene || (graph_1.scene = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // close module
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph) {
- var scene;
- (function (scene) {
- var node;
- (function (node_1) {
- /**
- * Select or Create a 'g.nodes' group to a given sceneGroup
- * and builds a number of 'g.node' groups inside the group.
- *
- * Structure Pattern:
- *
- * <g class='nodes'>
- * <g class='node'>
- * <g class='in-annotations'>
- * ...
- * </g>
- * <g class='out-annotations'>
- * ...
- * </g>
- * <g class='nodeshape'>
- * \x3c!--
- * Content of the node shape should be for the node itself. For example a
- * Metanode would have a <rect> with rounded edges, an op would have an
- * <ellipse>. More complex nodes like series may contain multiple
- * elements which are conditionally visible based on whether the node is
- * expanded.
- * --\x3e
- * </g>
- * <text class='label'>node name</text>
- * <g class='subscene'>
- * \x3c!--
- * Content of the subscene (only for metanode and series node).
- *
- * Subscene is a svg group that contains content of the
- * metanode's metagraph that is recursively generated by Scene.build().
- *
- * When the graph is expanded multiple times, a subscene can contain
- * nested subscenes inside.
- * --\x3e
- * </g>
- * </g>
- * ...
- * </g>
- *
- *
- * @param sceneGroup selection of the container
- * @param nodeData array of render node information to map
- * @param sceneElement <tf-graph-scene> polymer element
- * @return selection of the created nodeGroups
- */
- 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)
- var nodeGroups = container
- .selectAll(function () {
- // using d3's selector function
- // See https://github.com/mbostock/d3/releases/tag/v2.0.0
- // (It's not listed in the d3 wiki.)
- return this.childNodes; // this here refers to container.node()
- })
- .data(nodeData, function (d) {
- // make sure that we don't have to swap shape type
- return d.node.name + ':' + d.node.type;
- });
- // ENTER
- nodeGroups.enter()
- .append('g')
- .attr('data-name', function (d) { return d.node.name; })
- .each(function (d) {
- var nodeGroup = d3.select(this);
- // index node group for quick stylizing
- sceneElement.addNodeGroup(d.node.name, nodeGroup);
- });
- // UPDATE
- nodeGroups
- .attr('class', function (d) { return scene.Class.Node.GROUP + ' ' + nodeClass(d); })
- .each(function (d) {
- var nodeGroup = d3.select(this);
- // 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, 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, sceneElement);
- // Build .shape first (background of the node).
- var shape = buildShape(nodeGroup, d, scene.Class.Node.SHAPE);
- if (d.node.isGroupNode) {
- addButton(shape, d, sceneElement);
- }
- 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
- 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) { sceneElement.removeAnnotationGroup(a, d); });
- }
- if (d.outAnnotations.list.length > 0) {
- nodeGroup.select('.' + scene.Class.Annotation.OUTBOX)
- .selectAll('.' + scene.Class.Annotation.GROUP)
- .each(function (a) { sceneElement.removeAnnotationGroup(a, d); });
- }
- })
- .remove();
- return nodeGroups;
- }
- node_1.buildGroup = buildGroup;
- ;
- /**
- * Update or remove the subscene of a render group node depending on whether it
- * is a expanded. If the node is not a group node, this method has no effect.
- *
- * @param nodeGroup selection of the container
- * @param renderNodeInfo the render information for the node.
- * @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, sceneElement) {
- if (renderNodeInfo.node.isGroupNode) {
- if (renderNodeInfo.expanded) {
- // Recursively build the subscene.
- 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();
- }
- return null;
- }
- ;
- /**
- * Translate the subscene of the given node group
- */
- function subscenePosition(nodeGroup, d) {
- var x0 = d.x - d.width / 2.0 + d.paddingLeft;
- var y0 = d.y - d.height / 2.0 + d.paddingTop;
- var subscene = scene.selectChild(nodeGroup, 'g', scene.Class.Subscene.GROUP);
- scene.translate(subscene, x0, y0);
- }
- ;
- /**
- * Add an expand/collapse button to a group node
- *
- * @param selection The group node selection.
- * @param d Info about the node being rendered.
- * @param sceneElement <tf-graph-scene> polymer element.
- */
- 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');
- scene.selectOrCreateChild(group, 'path', scene.Class.Node.COLLAPSE_BUTTON)
- .attr('d', 'M-2.2,0 H2.2');
- group.on('click', function (d) {
- // Stop this event's propagation so that it isn't also considered a
- // node-select.
- d3.event.stopPropagation();
- sceneElement.fire('node-toggle-expand', { name: d.node.name });
- });
- scene.positionButton(group, d);
- }
- ;
- /**
- * Fire node-* events when the selection is interacted.
- *
- * @param disableInteraction When true, have the provided selection
- * ignore all pointer events. Used for text labels inside of metanodes, which
- * 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, sceneElement, disableInteraction) {
- if (disableInteraction) {
- selection.attr('pointer-events', 'none');
- return;
- }
- var contextMenuFunction = scene.contextmenu.getMenu(getContextMenu(d.node, sceneElement));
- selection
- .on('dblclick', function (d) {
- 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 (sceneElement.isNodeExpanded(d)) {
- return;
- }
- 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 (sceneElement.isNodeExpanded(d)) {
- return;
- }
- 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();
- sceneElement.fire('node-select', { name: d.node.name });
- })
- .on('contextmenu', function (d, i) {
- sceneElement.fire('node-select', { name: d.node.name });
- contextMenuFunction.call(d, i);
- });
- }
- ;
- /**
- * Returns the d3 context menu specification for the provided node.
- */
- function getContextMenu(node, sceneElement) {
- var menu = [{
- title: function (d) {
- return graph.getIncludeNodeButtonString(node.include);
- },
- action: function (elm, d, i) {
- 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 sceneElement <tf-graph-scene> polymer element.
- */
- 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 > sceneElement.maxMetanodeLabelLength) {
- text = text.substr(0, sceneElement.maxMetanodeLabelLength - 2) + '...';
- }
- var scale = getLabelFontScale(sceneElement);
- label.attr('font-size', scale(text.length) + 'px');
- }
- var txtElement = label.text(text);
- enforceLabelWidth(txtElement, renderNodeInfo.node.type, renderNodeInfo);
- return label;
- }
- /**
- * This function shortens text which would exceed the maximum pixel width of
- * a label.
- *
- * @param txtElementSelection The text element containing the label's text as d3
- * selection.
- * @param nodeType The type of the node the label belongs to. If the node is
- * an annotation, the value is -1. Label widths are defined in
- * layout.PARAMS.nodeSize.{meta|op|...}.maxLabelWidth for nodes and
- * layout.PARAMS.annotations.labelWidth for annotations.
- * @param renderNodeInfo The render information about the node, required to
- * determine whether META nodes are collapsed or expanded.
- */
- function enforceLabelWidth(txtElementSelection, nodeType, renderNodeInfo) {
- // Get text element itself and its on-screen width.
- var txtNode = txtElementSelection.node();
- var computedTxtLength = txtNode.getComputedTextLength();
- var labelContent = txtNode.textContent;
- // Get maximum length from settings.
- var maxLength = null;
- switch (nodeType) {
- case graph.NodeType.META:
- if (renderNodeInfo && !renderNodeInfo.expanded) {
- // node expanded.
- maxLength = graph.layout.PARAMS.nodeSize.meta.maxLabelWidth;
- }
- break;
- case graph.NodeType.OP:
- maxLength = graph.layout.PARAMS.nodeSize.op.maxLabelWidth;
- break;
- case -1:
- maxLength = graph.layout.PARAMS.annotations.maxLabelWidth;
- break;
- default:
- break;
- }
- // Return if no max length provided for node type, or current label length is
- // less than or equal to the provided length limit.
- if (maxLength === null || computedTxtLength <= maxLength) {
- return;
- }
- // Find the index of the character which exceeds the width.
- // getSubStringLength performs far better than getComputedTextLength, and
- // results in a 3x speed-up on average.
- var index = 1;
- while (txtNode.getSubStringLength(0, index) < maxLength) {
- index++;
- }
- // Shorten the label starting at the string length known to be one
- // character above max pixel length.
- // When shortened the original label's substring is concatenated with
- // '...', baseText contains the substring not including the '...'.
- var baseText = txtNode.textContent.substr(0, index);
- do {
- baseText = baseText.substr(0, baseText.length - 1);
- // Recompute text length.
- txtNode.textContent = baseText + '...';
- computedTxtLength = txtNode.getComputedTextLength();
- } while (computedTxtLength > maxLength && baseText.length > 0);
- // Add tooltip with full name and return.
- return txtElementSelection.append('title').text(labelContent);
- }
- node_1.enforceLabelWidth = enforceLabelWidth;
- /**
- * d3 scale used for sizing font of labels, used by labelBuild,
- * initialized once by getLabelFontScale.
- */
- var fontScale = null;
- function getLabelFontScale(sceneElement) {
- if (!fontScale) {
- fontScale = d3.scale.linear()
- .domain([sceneElement.maxMetanodeLabelLengthLargeFont,
- sceneElement.maxMetanodeLabelLength])
- .range([sceneElement.maxMetanodeLabelLengthFontSize,
- sceneElement.minMetanodeLabelLengthFontSize]).clamp(true);
- }
- return fontScale;
- }
- /**
- * Set label position of a given node group
- */
- function labelPosition(nodeGroup, cx, cy, yOffset) {
- scene.selectChild(nodeGroup, 'text', scene.Class.Node.LABEL)
- .transition()
- .attr('x', cx)
- .attr('y', cy + yOffset);
- }
- ;
- /**
- * Select or append/insert shape for a node and assign renderNode
- * as the shape's data.
- *
- * @param nodeGroup
- * @param d Render node information.
- * @param nodeClass class for the element.
- * @return Selection of the shape.
- */
- function buildShape(nodeGroup, d, nodeClass) {
- // Create a group to house the underlying visual elements.
- 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:
- scene.selectOrCreateChild(shapeGroup, 'ellipse', scene.Class.Node.COLOR_TARGET);
- break;
- case graph.NodeType.SERIES:
- // Choose the correct stamp to use to represent this series.
- var stampType = 'annotation';
- var groupNodeInfo = d;
- if (groupNodeInfo.coreGraph) {
- stampType =
- groupNodeInfo.node.hasNonControlEdges ? 'vertical' : 'horizontal';
- }
- var classList = [scene.Class.Node.COLOR_TARGET];
- if (groupNodeInfo.isFadedOut) {
- classList.push('faded-ellipse');
- }
- scene.selectOrCreateChild(shapeGroup, 'use', classList)
- .attr('xlink:href', '#op-series-' + stampType + '-stamp');
- scene.selectOrCreateChild(shapeGroup, 'rect', scene.Class.Node.COLOR_TARGET)
- .attr({ rx: d.radius, ry: d.radius });
- break;
- case graph.NodeType.BRIDGE:
- scene.selectOrCreateChild(shapeGroup, 'rect', scene.Class.Node.COLOR_TARGET)
- .attr({ rx: d.radius, ry: d.radius });
- break;
- case graph.NodeType.META:
- scene.selectOrCreateChild(shapeGroup, 'rect', scene.Class.Node.COLOR_TARGET)
- .attr({ rx: d.radius, ry: d.radius });
- break;
- default:
- throw Error('Unrecognized node type: ' + d.node.type);
- }
- return shapeGroup;
- }
- node_1.buildShape = buildShape;
- ;
- function nodeClass(d) {
- switch (d.node.type) {
- case graph.NodeType.OP:
- return scene.Class.OPNODE;
- case graph.NodeType.META:
- return scene.Class.METANODE;
- case graph.NodeType.SERIES:
- return scene.Class.SERIESNODE;
- case graph.NodeType.BRIDGE:
- return scene.Class.BRIDGENODE;
- case graph.NodeType.ELLIPSIS:
- return scene.Class.ELLIPSISNODE;
- }
- ;
- throw Error('Unrecognized node type: ' + d.node.type);
- }
- node_1.nodeClass = nodeClass;
- ;
- /** Modify node and its subscene and its label's positional attributes */
- function position(nodeGroup, d) {
- var shapeGroup = scene.selectChild(nodeGroup, 'g', scene.Class.Node.SHAPE);
- var cx = graph.layout.computeCXPositionOfNodeShape(d);
- switch (d.node.type) {
- case graph.NodeType.OP: {
- // position shape
- var shape = scene.selectChild(shapeGroup, 'ellipse');
- scene.positionEllipse(shape, cx, d.y, d.coreBox.width, d.coreBox.height);
- labelPosition(nodeGroup, cx, d.y, d.labelOffset);
- break;
- }
- case graph.NodeType.META: {
- // position shape
- var shape = scene.selectChild(shapeGroup, 'rect');
- if (d.expanded) {
- scene.positionRect(shape, d.x, d.y, d.width, d.height);
- subscenePosition(nodeGroup, d);
- // put label on top
- labelPosition(nodeGroup, cx, d.y, -d.height / 2 + d.labelHeight / 2);
- }
- else {
- scene.positionRect(shape, cx, d.y, d.coreBox.width, d.coreBox.height);
- labelPosition(nodeGroup, cx, d.y, 0);
- }
- break;
- }
- case graph.NodeType.SERIES: {
- var shape = scene.selectChild(shapeGroup, 'use');
- if (d.expanded) {
- scene.positionRect(shape, d.x, d.y, d.width, d.height);
- subscenePosition(nodeGroup, d);
- // put label on top
- labelPosition(nodeGroup, cx, d.y, -d.height / 2 + d.labelHeight / 2);
- }
- else {
- scene.positionRect(shape, cx, d.y, d.coreBox.width, d.coreBox.height);
- labelPosition(nodeGroup, cx, d.y, d.labelOffset);
- }
- break;
- }
- case graph.NodeType.BRIDGE: {
- // position shape
- // NOTE: In reality, these will not be visible, but it helps to put them
- // in the correct position for debugging purposes.
- var shape = scene.selectChild(shapeGroup, 'rect');
- scene.positionRect(shape, d.x, d.y, d.width, d.height);
- break;
- }
- default: {
- throw Error('Unrecognized node type: ' + d.node.type);
- }
- }
- }
- ;
- /** Enum specifying the options to color nodes by */
- var ColorBy;
- (function (ColorBy) {
- ColorBy[ColorBy["STRUCTURE"] = 0] = "STRUCTURE";
- ColorBy[ColorBy["DEVICE"] = 1] = "DEVICE";
- ColorBy[ColorBy["XLA_CLUSTER"] = 2] = "XLA_CLUSTER";
- ColorBy[ColorBy["COMPUTE_TIME"] = 3] = "COMPUTE_TIME";
- ColorBy[ColorBy["MEMORY"] = 4] = "MEMORY";
- })(ColorBy = node_1.ColorBy || (node_1.ColorBy = {}));
- ;
- /**
- * Returns the fill color for the node given its state and the 'color by'
- * option.
- */
- function getFillForNode(templateIndex, colorBy, renderInfo, isExpanded) {
- var colorParams = graph.render.MetanodeColors;
- switch (colorBy) {
- case ColorBy.STRUCTURE:
- 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 === 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.
- return isExpanded ? colorParams.EXPANDED_COLOR : 'white';
- }
- else if (renderInfo.node.type === graph.NodeType.BRIDGE) {
- return renderInfo.structural ?
- '#f0e' :
- renderInfo.node.inbound ? '#0ef' : '#fe0';
- }
- else {
- // Op nodes are white.
- return 'white';
- }
- case ColorBy.DEVICE:
- if (renderInfo.deviceColors == null) {
- // Return the hue for unknown device.
- return colorParams.UNKNOWN;
- }
- var id = renderInfo.node.name;
- var escapedId = tf.graph.util.escapeQuerySelector(id);
- var gradientDefs = d3.select('svg#svg defs #linearGradients');
- var linearGradient_1 = gradientDefs.select('linearGradient#' + escapedId);
- // If the linear gradient is not there yet, create it.
- if (linearGradient_1.size() === 0) {
- linearGradient_1 = gradientDefs.append('linearGradient').attr('id', id);
- // Re-create the stops of the linear gradient.
- 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_1.append('stop')
- .attr('offset', cumulativeProportion_1)
- .attr('stop-color', color);
- linearGradient_1.append('stop')
- .attr('offset', cumulativeProportion_1 + d.proportion)
- .attr('stop-color', color);
- cumulativeProportion_1 += d.proportion;
- });
- }
- return isExpanded ? colorParams.EXPANDED_COLOR : "url(#" + escapedId + ")";
- case ColorBy.XLA_CLUSTER:
- return isExpanded ? colorParams.EXPANDED_COLOR :
- renderInfo.xlaClusterColor || colorParams.UNKNOWN;
- case ColorBy.COMPUTE_TIME:
- return isExpanded ?
- colorParams.EXPANDED_COLOR : renderInfo.computeTimeColor ||
- colorParams.UNKNOWN;
- case ColorBy.MEMORY:
- return isExpanded ?
- colorParams.EXPANDED_COLOR : renderInfo.memoryColor ||
- colorParams.UNKNOWN;
- default:
- throw new Error('Unknown case to color nodes by');
- }
- }
- node_1.getFillForNode = getFillForNode;
- /**
- * Modify node style by toggling class and assign attributes (only for things
- * that can't be done in css).
- */
- function stylize(nodeGroup, renderInfo, sceneElement, nodeClass) {
- nodeClass = nodeClass || scene.Class.Node.SHAPE;
- var isHighlighted = sceneElement.isNodeHighlighted(renderInfo.node.name);
- var isSelected = sceneElement.isNodeSelected(renderInfo.node.name);
- var isExtract = renderInfo.isInExtract || renderInfo.isOutExtract;
- var isExpanded = renderInfo.expanded;
- var isFadedOut = renderInfo.isFadedOut;
- nodeGroup.classed('highlighted', isHighlighted);
- nodeGroup.classed('selected', isSelected);
- nodeGroup.classed('extract', isExtract);
- nodeGroup.classed('expanded', isExpanded);
- nodeGroup.classed('faded', isFadedOut);
- // 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(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.
- node.style('stroke', isSelected ? null : getStrokeForFill(fillColor));
- }
- node_1.stylize = stylize;
- ;
- /**
- * Given a node's fill color/gradient, determine the stroke for the node.
- */
- function getStrokeForFill(fill) {
- // If node is colored by a gradient, then use a dark gray outline.
- return fill.substring(0, 3) === 'url' ?
- graph.render.MetanodeColors.GRADIENT_OUTLINE :
- d3.rgb(fill).darker().toString();
- }
- node_1.getStrokeForFill = getStrokeForFill;
- /**
- * Finds selected node and highlights all nodes which are providing direct
- * or indirect input to the node and all edges connecting these nodes
- * together and to the selected node.
- *
- * @param renderGraphInfo Information on the rendered state of the graph.
- */
- function traceInputs(renderGraphInfo) {
- // Reset all styling.
- d3.selectAll('.input-highlight').classed('input-highlight', false);
- d3.selectAll('.non-input').classed('non-input', false);
- d3.selectAll('.input-parent').classed('input-parent', false);
- d3.selectAll('.input-child').classed('input-child', false);
- d3.selectAll('.input-edge-highlight').classed('input-edge-highlight', false);
- d3.selectAll('.non-input-edge-highlight')
- .classed('non-input-edge-highlight', false);
- d3.selectAll('.input-highlight-selected')
- .classed('input-highlight-selected', false);
- // Extract currently selected node. Return if input tracing disabled or no
- // node is selected.
- var selectedNodeSelectorString = 'g.node.selected,g.op.selected';
- var node = d3.select(selectedNodeSelectorString);
- var currentNode = undefined;
- if (renderGraphInfo && renderGraphInfo.traceInputs && node && node[0] &&
- node[0][0]) {
- currentNode = node[0][0];
- }
- else {
- return;
- }
- var nodeName = currentNode.getAttribute('data-name');
- var opNodes = _getAllContainedOpNodes(nodeName, renderGraphInfo);
- var allTracedNodes = {};
- _.each(opNodes, function (nodeInstance) {
- allTracedNodes =
- traceAllInputsOfOpNode(renderGraphInfo, nodeInstance, allTracedNodes);
- });
- d3.selectAll(selectedNodeSelectorString).classed({
- // Remove the input-highlight from the selected node.
- 'input-highlight': false,
- // Add input-highlight-selected class to selected node, which allows
- // treating the selected not as a special case of an input node.
- 'input-highlight-selected': true
- });
- // Highlight all parent nodes of each OpNode as input parent to allow
- // specific highlighting.
- var highlightedNodes = Object.keys(allTracedNodes);
- var visibleNodes = _findVisibleParentsFromOpNodes(renderGraphInfo, highlightedNodes);
- _markParentsOfNodes(visibleNodes);
- // Attach class to all non-input nodes and edges for styling.
- d3.selectAll('g.node:not(.selected):not(.input-highlight)' +
- ':not(.input-parent):not(.input-children)')
- .classed('non-input', true)
- .each(function (d) {
- // Mark all nodes with the specified name as non-inputs. This
- // results in Annotation nodes which are attached to inputs to be
- // tagged as well.
- var nodeName = d.node.name;
- d3.selectAll("[data-name=\"" + nodeName + "\"]").classed('non-input', true);
- });
- d3.selectAll('g.edge:not(.input-edge-highlight)')
- .classed('non-input-edge-highlight', true);
- }
- node_1.traceInputs = traceInputs;
- /**
- * Recursively find all op nodes contained by the node identified by the
- * provided name.
- * @param nodeName The meta or op node of which the OpNode instances are
- * required.
- * @param renderGraphInfo The rendered graph information object.
- * @returns {Array} An array of OpNodeImpl instances.
- */
- function _getAllContainedOpNodes(nodeName, renderGraphInfo) {
- var opNodes = [];
- // Get current node.
- var node = renderGraphInfo.getNodeByName(nodeName);
- // If node is already OpNode then return the node plus its input embeddings.
- if (node instanceof tf.graph.OpNodeImpl) {
- return [node].concat(node.inEmbeddings);
- }
- // Otherwise, make recursive call for each node contained by the GroupNode.
- var childNodeNames = node.metagraph.nodes();
- _.each(childNodeNames, function (childNodeName) {
- opNodes =
- opNodes.concat(_getAllContainedOpNodes(childNodeName, renderGraphInfo));
- });
- return opNodes;
- }
- node_1._getAllContainedOpNodes = _getAllContainedOpNodes;
- function traceAllInputsOfOpNode(renderGraphInfo, startNode, allTracedNodes) {
- // To prevent infinite loops due to cyclical relationships and improving
- // performance by tracing OpNode which is input to 2+ nodes only once.
- if (allTracedNodes[startNode.name]) {
- return allTracedNodes;
- }
- else {
- allTracedNodes[startNode.name] = true;
- }
- // Extract the inputs.
- var inputs = startNode.inputs;
- // Get visible parent.
- var currentVisibleParent = getVisibleParent(renderGraphInfo, startNode);
- // Mark as input node.
- d3.select(".node[data-name=\"" + currentVisibleParent.name + "\"]")
- .classed('input-highlight', true);
- // Find the visible parent of each input.
- var visibleInputs = {};
- _.each(inputs, function (nodeInstance) {
- var resolvedNode = renderGraphInfo.getNodeByName(nodeInstance.name);
- if (resolvedNode === undefined) {
- // Node could not be found in rendered Hierarchy, which happens when
- // tracing inputs of a SummaryNode.
- return;
- }
- // Ensure node is resolved to OpNode if name collision with Metanode exists.
- if (resolvedNode instanceof graph.MetanodeImpl) {
- var resolvedNodeName = tf.graph.getStrictName(resolvedNode.name);
- resolvedNode = renderGraphInfo.getNodeByName(resolvedNodeName);
- }
- var visibleParent = getVisibleParent(renderGraphInfo, resolvedNode);
- // Append OpNode to visible parent entry.
- var visibleInputsEntry = visibleInputs[visibleParent.name];
- if (visibleInputsEntry) {
- visibleInputsEntry.opNodes.push(resolvedNode);
- }
- else {
- visibleInputs[visibleParent.name] = {
- visibleParent: visibleParent,
- opNodes: [resolvedNode]
- };
- }
- });
- // Find all parents of the start node.
- var startNodeParents = {};
- var indexedStartNodeParents = [currentVisibleParent];
- startNodeParents[currentVisibleParent.name] = {
- traced: false,
- index: 0,
- connectionEndpoints: []
- };
- var currentNode = currentVisibleParent;
- for (var index = 1; currentNode.name !== tf.graph.ROOT_NAME; index++) {
- currentNode = currentNode.parentNode;
- startNodeParents[currentNode.name] = {
- traced: false,
- index: index,
- connectionEndpoints: []
- };
- indexedStartNodeParents[index] = currentNode;
- }
- // Find first mutual parent of each input node and highlight connection.
- _.forOwn(visibleInputs, function (visibleParentInfo, key) {
- var nodeInstance = visibleParentInfo.visibleParent;
- // Make recursive call for each input-OpNode contained by the visible
- // parent.
- _.each(visibleParentInfo.opNodes, function (opNode) {
- allTracedNodes =
- traceAllInputsOfOpNode(renderGraphInfo, opNode, allTracedNodes);
- });
- if (nodeInstance.name !== currentVisibleParent.name) {
- _createVisibleTrace(nodeInstance, startNodeParents, indexedStartNodeParents);
- }
- });
- return allTracedNodes;
- }
- node_1.traceAllInputsOfOpNode = traceAllInputsOfOpNode;
- /**
- * Colors the edges to connect the passed node to the start node. This is
- * done by:
- *
- * a) Finding the first (visible) common parent in the rendered
- * hierarchy.
- * NB: There are 2 types of connections:
- * 1) Direct connections between node A
- * and B, marked below as II,
- * 2) Connections from any node A to its parent, A'. Marked below as I and III.
- * For type 2 connection you need to know the inner-nested node, the
- * direct parent, and the ultimate destination of the connection.
- *
- * A_parent B_parent
- * +--------+ +---------+
- * | | | |
- * | +--+ I| II |III+--+ |
- * | |A +----------\x3e+B | |
- * | +--+ | | +--+ |
- * | | | |
- * +--------+ +---------+
- *
- *
- * b) Highlighting the direct connection between the parents of A and B,
- * called A_parent and B_parent, s.t. A_parent and B_parent are children of the
- * mutual parent of A and B found in a), marked above as II.
- *
- * c) Highlighting the connection from A to A_parent and B to B_parent
- * (through all layers of parents between A and A_parent and B and B_parent,
- * respectively). Marked above as I and III.
- *
- * @param nodeInstance The instance of the node to use as destination node, B.
- * @param startNodeParents Map of startNodeParent names to information objects
- * about the parent.
- * @param indexedStartNodeParents An array of all parents of the start node.
- * This is required to find the child of the mutual parent which is a parent
- * of the start node.
- * @private
- */
- function _createVisibleTrace(nodeInstance, startNodeParents, indexedStartNodeParents) {
- var currentNode = nodeInstance;
- var previousNode = nodeInstance;
- // Ascend through parents until a mutual parent is found with the start
- // node.
- var destinationParentPairs = [];
- while (!startNodeParents[currentNode.name]) {
- if (previousNode.name !== currentNode.name) {
- destinationParentPairs.push([previousNode, currentNode]);
- }
- previousNode = currentNode;
- currentNode = currentNode.parentNode;
- }
- // Connection between nodes is drawn between the parents of each
- // respective node, both of which share the mutual parent.
- var startNodeIndex = startNodeParents[currentNode.name].index;
- var startNodeName = indexedStartNodeParents[Math.max(startNodeIndex - 1, 0)].name;
- var startNodeTopParentName = startNodeName;
- var targetNodeTopParentName = previousNode.name;
- var endNodeName = previousNode.name;
- d3.selectAll("[data-edge=\"" + endNodeName + "--" + startNodeName + "\"]")
- .classed('input-edge-highlight', true);
- // Trace up the parents of the input.
- _.each(destinationParentPairs, function (value) {
- var inner = value[0];
- var outer = value[1];
- var edgeSelector = "[data-edge=\"" + inner.name + "--" + startNodeTopParentName +
- ("~~" + outer.name + "~~OUT\"]");
- d3.selectAll(edgeSelector).classed('input-edge-highlight', true);
- });
- // Trace up the parents of the start node.
- for (var index = 1; index < startNodeIndex; index++) {
- var inner = indexedStartNodeParents[index - 1];
- var outer = indexedStartNodeParents[index];
- var edgeSelector = "[data-edge=\"" + targetNodeTopParentName + "~~" + outer.name +
- ("~~IN--" + inner.name + "\"]");
- d3.selectAll(edgeSelector).classed('input-edge-highlight', true);
- }
- }
- /**
- * Creates map { [name: string] -> Node } of all visible / rendered parents
- * of the nodes identified by the node names passed in.
- *
- * @param renderGraphInfo The information on the rendered graph.
- * @param nodeNames String array of node names.
- * @returns {[nodeName: string]: Node}
- * @private
- */
- function _findVisibleParentsFromOpNodes(renderGraphInfo, nodeNames) {
- var visibleParents = {};
- _.each(nodeNames, function (nodeName) {
- var currentNode = renderGraphInfo.getNodeByName(nodeName);
- var visibleParent = getVisibleParent(renderGraphInfo, currentNode);
- visibleParents[visibleParent.name] = visibleParent;
- });
- return visibleParents;
- }
- /**
- * Traverse through the parents of all nodes in the list and mark each
- * encountered node as input-parent.
- * @param visibleNodes Map of input nodes, have to be visible/rendered when
- * called.
- * @private
- */
- function _markParentsOfNodes(visibleNodes) {
- _.forOwn(visibleNodes, function (nodeInstance) {
- // Mark all parents of the node as input-parents.
- var currentNode = nodeInstance;
- while (currentNode.name !== tf.graph.ROOT_NAME) {
- var renderedElement = d3.select(".node[data-name=\"" + currentNode.name + "\"]");
- // Only mark the element as a parent node to an input if it is not
- // marked as input node itself.
- if (renderedElement[0][0] &&
- !renderedElement.classed('input-highlight') &&
- !renderedElement.classed('selected') &&
- // OpNode only parent if start node is embedded node, in which case
- // the OpNode should be faded as well.
- !renderedElement.classed('op')) {
- renderedElement.classed('input-parent', true);
- }
- currentNode = currentNode.parentNode;
- }
- });
- }
- /**
- * Find the parent of the passed in op node which is expanded. This is done
- * by going through all parents until the parent's parent is expanded, thus
- * finding the first unexpanded parent which is rendered on the screen.
- * @param renderGraphInfo The graph info object used to gain access to the
- * render info of the parents.
- * @param currentNode The node whose parent is to be found.
- * @returns Node
- */
- function getVisibleParent(renderGraphInfo, currentNode) {
- var found = false;
- var currentParent = currentNode;
- while (!found) {
- // Get parent element, to extract name.
- currentNode = currentParent;
- currentParent = currentNode.parentNode;
- if (currentParent === undefined) {
- found = true;
- }
- else {
- var renderNode = renderGraphInfo.getRenderNodeByName(currentParent.name);
- // Found if node is rendered on the screen (renderNode truthy), and
- // the parent is either expanded (i.e. it is a metanode or seriesnode)
- // or the parent is an OpNode in which case currentNode is an embedded
- // node which has another OpNode as parent.
- if (renderNode &&
- (renderNode.expanded || currentParent instanceof graph.OpNodeImpl)) {
- found = true;
- }
- }
- } // Close while loop.
- return currentNode;
- }
- node_1.getVisibleParent = getVisibleParent;
- })(node = scene.node || (scene.node = {}));
- })(scene = graph.scene || (graph.scene = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // Close module.
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph) {
- var scene;
- (function (scene) {
- var svgNamespace = 'http://www.w3.org/2000/svg';
- /** Enums element class of objects in the scene */
- scene.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'
- };
- ;
- ;
- scene.healthPillEntries = [
- {
- background_color: '#CC2F2C',
- label: 'NaN',
- },
- {
- background_color: '#FF8D00',
- label: '- ∞',
- },
- {
- background_color: '#EAEAEA',
- label: '-',
- },
- {
- background_color: '#A5A5A5',
- label: '0',
- },
- {
- background_color: '#262626',
- label: '+',
- },
- {
- background_color: '#003ED4',
- label: '+ ∞',
- },
- ];
- /**
- * 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.
- */
- function fit(svg, zoomG, d3zoom, callback) {
- var svgRect = svg.getBoundingClientRect();
- var 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;
- }
- var scale = 0.9 *
- Math.min(svgRect.width / sceneSize.width, svgRect.height / sceneSize.height, 2);
- var params = graph.layout.PARAMS.graph;
- var zoomEvent = d3zoom.scale(scale)
- .on('zoomend.fitted', function () {
- // 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);
- }
- scene.fit = fit;
- ;
- /**
- * Helper method for panning the graph to center on the provided node,
- * if the node is currently off-screen.
- *
- * @param nodeName The node to center the graph on
- * @param svg The root SVG element for the graph
- * @param zoomG The svg group used for panning and zooming.
- * @param d3zoom The zoom behavior.
- * @return True if the graph had to be panned to display the
- * provided node.
- */
- function panToNode(nodeName, svg, zoomG, d3zoom) {
- var node = d3
- .select('[data-name="' + nodeName + '"].' + scene.Class.Node.GROUP)
- .node();
- if (!node) {
- return false;
- }
- var translate = d3zoom.translate();
- // Check if the selected node is off-screen in either
- // X or Y dimension in either direction.
- var nodeBox = node.getBBox();
- var nodeCtm = node.getScreenCTM();
- var pointTL = svg.createSVGPoint();
- var pointBR = svg.createSVGPoint();
- pointTL.x = nodeBox.x;
- pointTL.y = nodeBox.y;
- pointBR.x = nodeBox.x + nodeBox.width;
- pointBR.y = nodeBox.y + nodeBox.height;
- pointTL = pointTL.matrixTransform(nodeCtm);
- pointBR = pointBR.matrixTransform(nodeCtm);
- var isOutsideOfBounds = function (start, end, bound) {
- return end < 0 || start > bound;
- };
- var svgRect = svg.getBoundingClientRect();
- if (isOutsideOfBounds(pointTL.x, pointBR.x, svgRect.width) ||
- isOutsideOfBounds(pointTL.y, pointBR.y, svgRect.height)) {
- // Determine the amount to transform the graph in both X and Y
- // dimensions in order to center the selected node. This takes into
- // acount the position of the node, the size of the svg scene, the
- // amount the scene has been scaled by through zooming, and any previous
- // transform already performed by this logic.
- var centerX = (pointTL.x + pointBR.x) / 2;
- var centerY = (pointTL.y + pointBR.y) / 2;
- var dx = ((svgRect.width / 2) - centerX);
- var dy = ((svgRect.height / 2) - centerY);
- var zoomEvent = d3zoom.translate([translate[0] + dx, translate[1] + dy])
- .event;
- d3.select(zoomG).transition().duration(500).call(zoomEvent);
- return true;
- }
- return false;
- }
- scene.panToNode = panToNode;
- ;
- /**
- * Given a container d3 selection, select a child svg element of a given tag
- * and class if exists or append / insert one otherwise. If multiple children
- * matches the tag and class name, returns only the first one.
- *
- * @param container
- * @param tagName tag name.
- * @param className (optional) Class name or a list of class names.
- * @param before (optional) reference DOM node for insertion.
- * @return selection of the element
- */
- function selectOrCreateChild(container, tagName, className, before) {
- var child = selectChild(container, tagName, className);
- if (!child.empty()) {
- return child;
- }
- var newElement = document.createElementNS('http://www.w3.org/2000/svg', tagName);
- if (className instanceof Array) {
- for (var i = 0; i < className.length; i++) {
- newElement.classList.add(className[i]);
- }
- }
- else {
- newElement.classList.add(className);
- }
- if (before) {
- container.node().insertBefore(newElement, before);
- }
- else {
- container.node().appendChild(newElement);
- }
- return d3.select(newElement)
- .datum(container.datum());
- }
- scene.selectOrCreateChild = selectOrCreateChild;
- ;
- /**
- * Given a container d3 selection, select a child element of a given tag and
- * class. If multiple children matches the tag and class name, returns only
- * the first one.
- *
- * @param container
- * @param tagName tag name.
- * @param className (optional) Class name or list of class names.
- * @return selection of the element, or an empty selection
- */
- function selectChild(container, tagName, className) {
- var children = container.node().childNodes;
- for (var i = 0; i < children.length; i++) {
- var child = children[i];
- if (child.tagName === tagName) {
- if (className instanceof Array) {
- var hasAllClasses = true;
- for (var j = 0; j < className.length; j++) {
- hasAllClasses =
- hasAllClasses && child.classList.contains(className[j]);
- }
- if (hasAllClasses) {
- return d3.select(child);
- }
- }
- else if ((!className || child.classList.contains(className))) {
- return d3.select(child);
- }
- }
- }
- return d3.select(null);
- }
- scene.selectChild = selectChild;
- ;
- /**
- * Select or create a sceneGroup and build/update its nodes and edges.
- *
- * Structure Pattern:
- *
- * <g class='scene'>
- * <g class='core'>
- * <g class='edges'>
- * ... stuff from tf.graph.scene.edges.build ...
- * </g>
- * <g class='nodes'>
- * ... stuff from tf.graph.scene.nodes.build ...
- * </g>
- * </g>
- * <g class='in-extract'>
- * <g class='nodes'>
- * ... stuff from tf.graph.scene.nodes.build ...
- * </g>
- * </g>
- * <g class='out-extract'>
- * <g class='nodes'>
- * ... stuff from tf.graph.scene.nodes.build ...
- * </g>
- * </g>
- * </g>
- *
- * @param container D3 selection of the parent.
- * @param renderNode render node of a metanode or series node.
- * @param sceneElement <tf-graph-scene> polymer element.
- * @param sceneClass class attribute of the scene (default='scene').
- */
- 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);
- // core
- var coreGroup = selectOrCreateChild(sceneGroup, 'g', scene.Class.Scene.CORE);
- var coreNodes = _.reduce(renderNode.coreGraph.nodes(), function (nodes, name) {
- var node = renderNode.coreGraph.node(name);
- if (!node.excluded) {
- nodes.push(node);
- }
- return nodes;
- }, []);
- if (renderNode.node.type === graph.NodeType.SERIES) {
- // For series, we want the first item on top, so reverse the array so
- // the first item in the series becomes last item in the top, and thus
- // is rendered on the top.
- coreNodes.reverse();
- }
- // Create the layer of edges for this scene (paths).
- scene.edge.buildGroup(coreGroup, renderNode.coreGraph, sceneElement);
- // Create the layer of nodes for this scene (ellipses, rects etc).
- 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, sceneElement);
- }
- else {
- selectChild(sceneGroup, 'g', scene.Class.Scene.INEXTRACT).remove();
- }
- // Out-extract
- if (renderNode.isolatedOutExtract.length > 0) {
- var outExtractGroup = selectOrCreateChild(sceneGroup, 'g', scene.Class.Scene.OUTEXTRACT);
- scene.node.buildGroup(outExtractGroup, renderNode.isolatedOutExtract, sceneElement);
- }
- else {
- selectChild(sceneGroup, 'g', scene.Class.Scene.OUTEXTRACT).remove();
- }
- position(sceneGroup, renderNode);
- // Fade in the scene group if it didn't already exist.
- if (isNewSceneGroup) {
- sceneGroup.attr('opacity', 0).transition().attr('opacity', 1);
- }
- return sceneGroup;
- }
- scene.buildGroup = buildGroup;
- ;
- /**
- * Given a scene's svg group, set g.in-extract, g.coreGraph, g.out-extract svg
- * groups' position relative to the scene.
- *
- * @param sceneGroup
- * @param renderNode render node of a metanode or series node.
- */
- function position(sceneGroup, renderNode) {
- // Translate scenes down by the label height so that when showing graphs in
- // expanded metanodes, the graphs are below the labels. Do not shift them
- // down for series nodes as series nodes don't have labels inside of their
- // bounding boxes.
- var yTranslate = renderNode.node.type === graph.NodeType.SERIES ?
- 0 : graph.layout.PARAMS.subscene.meta.labelHeight;
- // core
- translate(selectChild(sceneGroup, 'g', scene.Class.Scene.CORE), 0, yTranslate);
- // in-extract
- var hasInExtract = renderNode.isolatedInExtract.length > 0;
- var hasOutExtract = renderNode.isolatedOutExtract.length > 0;
- if (hasInExtract) {
- var offset = graph.layout.PARAMS.subscene.meta.extractXOffset;
- var inExtractX = renderNode.coreBox.width -
- renderNode.inExtractBox.width / 2 - renderNode.outExtractBox.width -
- (hasOutExtract ? offset : 0);
- translate(selectChild(sceneGroup, 'g', scene.Class.Scene.INEXTRACT), inExtractX, yTranslate);
- }
- // out-extract
- if (hasOutExtract) {
- var outExtractX = renderNode.coreBox.width -
- renderNode.outExtractBox.width / 2;
- translate(selectChild(sceneGroup, 'g', scene.Class.Scene.OUTEXTRACT), outExtractX, yTranslate);
- }
- }
- ;
- /** Adds a click listener to a group that fires a graph-select event */
- function addGraphClickListener(graphGroup, sceneElement) {
- d3.select(graphGroup).on('click', function () {
- sceneElement.fire('graph-select');
- });
- }
- scene.addGraphClickListener = addGraphClickListener;
- ;
- /** Helper for adding transform: translate(x0, y0) */
- function translate(selection, x0, y0) {
- // If it is already placed on the screen, make it a transition.
- if (selection.attr('transform') != null) {
- selection = selection.transition('position');
- }
- selection.attr('transform', 'translate(' + x0 + ',' + y0 + ')');
- }
- scene.translate = translate;
- ;
- /**
- * Helper for setting position of a svg rect
- * @param rect rect to set position of.
- * @param cx Center x.
- * @param cy Center x.
- * @param width Width to set.
- * @param height Height to set.
- */
- function positionRect(rect, cx, cy, width, height) {
- rect.transition().attr({
- x: cx - width / 2,
- y: cy - height / 2,
- width: width,
- height: height
- });
- }
- scene.positionRect = positionRect;
- ;
- /**
- * Helper for setting position of a svg expand/collapse button
- * @param button container group
- * @param renderNode the render node of the group node to position
- * the button on.
- */
- function positionButton(button, renderNode) {
- var cx = graph.layout.computeCXPositionOfNodeShape(renderNode);
- // Position the button in the top-right corner of the group node,
- // with space given the draw the button inside of the corner.
- var width = renderNode.expanded ?
- renderNode.width : renderNode.coreBox.width;
- var height = renderNode.expanded ?
- renderNode.height : renderNode.coreBox.height;
- var x = cx + width / 2 - 6;
- var y = renderNode.y - height / 2 + 6;
- // For unexpanded series nodes, the button has special placement due
- // to the unique visuals of this group node.
- if (renderNode.node.type === graph.NodeType.SERIES && !renderNode.expanded) {
- x += 10;
- y -= 2;
- }
- var translateStr = 'translate(' + x + ',' + y + ')';
- button.selectAll('path').transition().attr('transform', translateStr);
- button.select('circle').transition().attr({ cx: x, cy: y, r: graph.layout.PARAMS.nodeSize.meta.expandButtonRadius });
- }
- scene.positionButton = positionButton;
- ;
- /**
- * Helper for setting position of a svg ellipse
- * @param ellipse ellipse to set position of.
- * @param cx Center x.
- * @param cy Center x.
- * @param width Width to set.
- * @param height Height to set.
- */
- function positionEllipse(ellipse, cx, cy, width, height) {
- ellipse.transition().attr({
- cx: cx,
- cy: cy,
- rx: width / 2,
- ry: height / 2
- });
- }
- scene.positionEllipse = positionEllipse;
- ;
- /**
- * @param {number} stat A stat for a health pill (such as mean or variance).
- * @param {boolean} shouldRoundOnesDigit Whether to round this number to the
- * ones digit. Useful for say int, uint, and bool output types.
- * @return {string} A human-friendly string representation of that stat.
- */
- function humanizeHealthPillStat(stat, shouldRoundOnesDigit) {
- if (shouldRoundOnesDigit) {
- return stat.toFixed(0);
- }
- if (Math.abs(stat) >= 1) {
- return stat.toFixed(1);
- }
- return stat.toExponential(1);
- }
- scene.humanizeHealthPillStat = humanizeHealthPillStat;
- /**
- * Renders a health pill for an op atop a node.
- */
- function _addHealthPill(nodeGroupElement, healthPill, nodeInfo) {
- // Check if text already exists at location.
- d3.select(nodeGroupElement.parentNode).selectAll('.health-pill').remove();
- if (!nodeInfo || !healthPill) {
- return;
- }
- var lastHealthPillData = healthPill.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.
- var lastHealthPillOverview = lastHealthPillData.slice(2, 8);
- var totalCount = lastHealthPillData[1];
- var healthPillWidth = 60;
- var healthPillHeight = 10;
- if (nodeInfo.node.type === tf.graph.NodeType.OP) {
- // Use a smaller health pill for op nodes (rendered as smaller ellipses).
- healthPillWidth /= 2;
- healthPillHeight /= 2;
- }
- var healthPillGroup = document.createElementNS(svgNamespace, 'g');
- healthPillGroup.classList.add('health-pill');
- // Define the gradient for the health pill.
- var healthPillDefs = document.createElementNS(svgNamespace, 'defs');
- healthPillGroup.appendChild(healthPillDefs);
- var healthPillGradient = document.createElementNS(svgNamespace, 'linearGradient');
- var healthPillGradientId = 'health-pill-gradient';
- healthPillGradient.setAttribute('id', healthPillGradientId);
- var titleOnHoverTextEntries = [];
- var cumulativeCount = 0;
- var previousOffset = '0%';
- for (var i = 0; i < lastHealthPillOverview.length; i++) {
- if (!lastHealthPillOverview[i]) {
- // Exclude empty categories.
- continue;
- }
- cumulativeCount += lastHealthPillOverview[i];
- // Create a color interval using 2 stop elements.
- var stopElement0 = document.createElementNS(svgNamespace, 'stop');
- stopElement0.setAttribute('offset', previousOffset);
- stopElement0.setAttribute('stop-color', scene.healthPillEntries[i].background_color);
- healthPillGradient.appendChild(stopElement0);
- var stopElement1 = document.createElementNS(svgNamespace, 'stop');
- var percent = (cumulativeCount * 100 / totalCount) + '%';
- stopElement1.setAttribute('offset', percent);
- stopElement1.setAttribute('stop-color', scene.healthPillEntries[i].background_color);
- healthPillGradient.appendChild(stopElement1);
- previousOffset = percent;
- // Include this number in the title that appears on hover.
- titleOnHoverTextEntries.push(scene.healthPillEntries[i].label + ': ' + lastHealthPillOverview[i]);
- }
- healthPillDefs.appendChild(healthPillGradient);
- // Create the rectangle for the health pill.
- var rect = document.createElementNS(svgNamespace, 'rect');
- rect.setAttribute('fill', 'url(#' + healthPillGradientId + ')');
- rect.setAttribute('width', String(healthPillWidth));
- rect.setAttribute('height', String(healthPillHeight));
- healthPillGroup.appendChild(rect);
- // Show a title with specific counts on hover.
- var titleSvg = document.createElementNS(svgNamespace, 'title');
- titleSvg.textContent = titleOnHoverTextEntries.join(', ');
- healthPillGroup.appendChild(titleSvg);
- // Center this health pill just right above the node for the op.
- var healthPillX = nodeInfo.x - healthPillWidth / 2;
- var healthPillY = nodeInfo.y - healthPillHeight - nodeInfo.height / 2 - 2;
- if (nodeInfo.labelOffset < 0) {
- // The label is positioned above the node. Do not occlude the label.
- healthPillY += nodeInfo.labelOffset;
- }
- if (lastHealthPillOverview[2] || lastHealthPillOverview[3] ||
- lastHealthPillOverview[4]) {
- // At least 1 "non-Inf and non-NaN" value exists (a -, 0, or + value). Show
- // stats on tensor values.
- // Determine if we should display the output range as integers.
- var shouldRoundOnesDigit = false;
- var node_1 = nodeInfo.node;
- var attributes = node_1.attr;
- if (attributes && attributes.length) {
- // Find the attribute for output type if there is one.
- for (var i = 0; i < attributes.length; i++) {
- if (attributes[i].key === 'T') {
- // Note whether the output type is an integer.
- var outputType = attributes[i].value['type'];
- shouldRoundOnesDigit =
- outputType && /^DT_(BOOL|INT|UINT)/.test(outputType);
- break;
- }
- }
- }
- var statsSvg = document.createElementNS(svgNamespace, 'text');
- var minString = humanizeHealthPillStat(lastHealthPillData[8], shouldRoundOnesDigit);
- var maxString = humanizeHealthPillStat(lastHealthPillData[9], shouldRoundOnesDigit);
- statsSvg.textContent = minString + ' ~ ' + maxString;
- statsSvg.classList.add('health-pill-stats');
- statsSvg.setAttribute('x', String(healthPillWidth / 2));
- statsSvg.setAttribute('y', '-2');
- healthPillGroup.appendChild(statsSvg);
- }
- healthPillGroup.setAttribute('transform', 'translate(' + healthPillX + ', ' + healthPillY + ')');
- Polymer.dom(nodeGroupElement.parentNode).appendChild(healthPillGroup);
- }
- /**
- * 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 name to health pill.
- * @param colors A list of colors to use.
- */
- function addHealthPills(svgRoot, nodeNamesToHealthPills, healthPillStepIndex) {
- if (!nodeNamesToHealthPills) {
- // No health pill information available.
- return;
- }
- var svgRootSelection = d3.select(svgRoot);
- svgRootSelection.selectAll('g.nodeshape')
- .each(function (nodeInfo) {
- // Only show health pill data for this node if it is available.
- var healthPills = nodeNamesToHealthPills[nodeInfo.node.name];
- var healthPill = healthPills ? healthPills[healthPillStepIndex] : null;
- _addHealthPill(this, healthPill, nodeInfo);
- });
- }
- scene.addHealthPills = addHealthPills;
- ;
- })(scene = graph.scene || (graph.scene = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {})); // close module
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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;
- (function (graph_1) {
- var template;
- (function (template) {
- /**
- * Detect repeating patterns of subgraphs.
- * Assign templateId to each subgraph if it belongs to a template.
- * Returns clusters of similar subgraphs .
- *
- * @param graph
- * @param verifyTemplate whether to run the template verification algorithm
- * @return a dict (template id => Array of node names)
- */
- function detect(h, verifyTemplate) {
- // In any particular subgraph, there are either
- // - leaf nodes (which do not have subgraph)
- // - metanode nodes - some of them have only one member (singular metanode)
- // and some have multiple members (non-singular metanode)
- // First, generate a nearest neighbor hash of metanode nodes.
- var nnGroups = clusterSimilarSubgraphs(h);
- // For each metanode, compare its subgraph (starting from shallower groups)
- // and assign template id.
- var templates = groupTemplateAndAssignId(nnGroups, verifyTemplate);
- // Sort the templates by minimum level in the graph at which they appear,
- // as this leads to optimal setting of the colors of each template for
- // maximum differentiation.
- return _(templates)
- .pairs()
- .sortBy(function (pair) {
- return pair[1].level;
- })
- .map(function (pair) {
- return [pair[0], pair[1].nodes];
- })
- .object()
- .value();
- }
- template.detect = detect;
- ;
- /**
- * @return Unique string for a metanode based on depth, |V|, |E| and
- * op type histogram.
- */
- function getSignature(metanode) {
- // depth=<number> |V|=<number> |E|=<number>
- var props = _.map({
- 'depth': metanode.depth,
- '|V|': metanode.metagraph.nodes().length,
- '|E|': metanode.metagraph.edges().length
- }, function (v, k) { return k + '=' + v; })
- .join(' ');
- // optype1=count1,optype2=count2
- var ops = _.map(metanode.opHistogram, function (count, op) {
- return op + '=' + count;
- }).join(',');
- return props + ' [ops] ' + ops;
- }
- /**
- * Generate a nearest neighbor hash of metanodes
- * based on depth, |V|, |E|, and opHistogram of their subgraph
- * (excluding leaf nodes and singular metanodes).
- * @param graph The graph
- * @return Array of pairs of [signature,
- * Object with min level of the template and an Array of tf.graph.Group]
- * sort by ascending order of minimum depth at which metanode appears.
- */
- function clusterSimilarSubgraphs(h) {
- /** a dict from metanode.signature() => Array of tf.graph.Groups */
- var hashDict = _(h.getNodeMap()).reduce(function (hash, node, name) {
- if (node.type !== graph_1.NodeType.META) {
- return hash;
- }
- var levelOfMetaNode = name.split('/').length - 1;
- var signature = getSignature(node);
- var templateInfo = hash[signature] ||
- { nodes: [], level: levelOfMetaNode };
- hash[signature] = templateInfo;
- templateInfo.nodes.push(node);
- if (templateInfo.level > levelOfMetaNode) {
- templateInfo.level = levelOfMetaNode;
- }
- return hash;
- }, {});
- return _(hashDict)
- .pairs()
- .filter(function (pair) {
- return pair[1].nodes.length > 1;
- })
- .sortBy(function (pair) {
- // sort by depth
- // (all members in the same nnGroup has equal depth)
- return pair[1].nodes[0].depth;
- })
- .value();
- }
- function groupTemplateAndAssignId(nnGroups, verifyTemplate) {
- // For each metanode, compare its subgraph (starting from shallower groups)
- // and assign template id.
- var result = {};
- return _.reduce(nnGroups, function (templates, nnGroupPair) {
- var signature = nnGroupPair[0], nnGroup = nnGroupPair[1].nodes, clusters = [];
- nnGroup.forEach(function (metanode) {
- // check with each existing cluster
- for (var i = 0; i < clusters.length; i++) {
- var similar = !verifyTemplate ||
- isSimilarSubgraph(clusters[i].metanode.metagraph, metanode.metagraph);
- // if similar, just add this metanode to the cluster
- if (similar) {
- // get template from the first one
- metanode.templateId = clusters[i].metanode.templateId;
- clusters[i].members.push(metanode.name);
- return;
- }
- }
- // otherwise create a new cluster with id 'signature [count] '
- metanode.templateId = signature + '[' + clusters.length + ']';
- clusters.push({
- metanode: metanode,
- members: [metanode.name]
- });
- });
- clusters.forEach(function (c) {
- templates[c.metanode.templateId] = {
- level: nnGroupPair[1].level,
- nodes: c.members
- };
- });
- return templates;
- }, result);
- }
- function sortNodes(names, graph, prefix) {
- return _.sortByAll(names, function (name) {
- var node = graph.node(name);
- return node.op;
- }, function (name) {
- var node = graph.node(name);
- return node.templateId;
- }, function (name) {
- return graph.neighbors(name).length;
- }, function (name) {
- return graph.predecessors(name).length;
- }, function (name) {
- return graph.successors(name).length;
- }, function (name) {
- return name.substr(prefix.length);
- });
- }
- function isSimilarSubgraph(g1, g2) {
- if (!tf.graph.hasSimilarDegreeSequence(g1, g2)) {
- return false;
- }
- // if we want to skip, just return true here.
- // return true;
- // Verify sequence by running DFS
- var g1prefix = g1.graph().name;
- var g2prefix = g2.graph().name;
- var visited1 = {};
- var visited2 = {};
- var stack = [];
- /**
- * push sources or successors into the stack
- * if the visiting pattern has been similar.
- */
- function stackPushIfNotDifferent(n1, n2) {
- var sub1 = n1.substr(g1prefix.length), sub2 = n2.substr(g2prefix.length);
- /* tslint:disable */
- if (visited1[sub1] ^ visited2[sub1]) {
- console.warn('different visit pattern', '[' + g1prefix + ']', sub1, '[' + g2prefix + ']', sub2);
- return true;
- }
- /* tslint:enable */
- if (!visited1[sub1]) {
- visited1[sub1] = visited2[sub2] = true;
- stack.push({ n1: n1, n2: n2 });
- }
- return false;
- }
- // check if have same # of sources then sort and push
- var sources1 = g1.sources();
- var sources2 = g2.sources();
- if (sources1.length !== sources2.length) {
- /* tslint:disable */
- console.log('different source length');
- /* tslint:enable */
- return false;
- }
- sources1 = sortNodes(sources1, g1, g1prefix);
- sources2 = sortNodes(sources2, g2, g2prefix);
- for (var i = 0; i < sources1.length; i++) {
- var different = stackPushIfNotDifferent(sources1[i], sources2[i]);
- if (different) {
- return false;
- }
- }
- while (stack.length > 0) {
- var cur = stack.pop();
- // check node
- var similar = isSimilarNode(g1.node(cur.n1), g2.node(cur.n2));
- if (!similar) {
- return false;
- }
- // check if have same # of successors then sort and push
- var succ1 = g1.successors(cur.n1), succ2 = g2.successors(cur.n2);
- if (succ1.length !== succ2.length) {
- /* tslint:disable */
- console.log('# of successors mismatch', succ1, succ2);
- /* tslint:enable */
- return false;
- }
- succ1 = sortNodes(succ1, g1, g1prefix);
- succ2 = sortNodes(succ2, g2, g2prefix);
- for (var j = 0; j < succ1.length; j++) {
- var different = stackPushIfNotDifferent(succ1[j], succ2[j]);
- if (different) {
- return false;
- }
- }
- }
- return true;
- }
- /**
- * Returns if two nodes have identical structure.
- */
- function isSimilarNode(n1, n2) {
- if (n1.type === graph_1.NodeType.META) {
- // compare metanode
- var metanode1 = n1;
- var metanode2 = n2;
- return metanode1.templateId && metanode2.templateId &&
- metanode1.templateId === metanode2.templateId;
- }
- else if (n1.type === graph_1.NodeType.OP && n2.type === graph_1.NodeType.OP) {
- // compare leaf node
- return n1.op === n2.op;
- }
- else if (n1.type === graph_1.NodeType.SERIES && n2.type === graph_1.NodeType.SERIES) {
- // compare series node sizes and operations
- // (only need to check one op as all op nodes are identical in series)
- var sn1 = n1;
- var sn2 = n2;
- var seriesnode1Count = sn1.metagraph.nodeCount();
- return (seriesnode1Count === sn2.metagraph.nodeCount() &&
- (seriesnode1Count === 0 ||
- (sn1.metagraph.node(sn1.metagraph.nodes()[0]).op ===
- sn2.metagraph.node(sn2.metagraph.nodes()[0]).op)));
- }
- return false;
- }
- })(template = graph_1.template || (graph_1.template = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {}));
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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.
-==============================================================================*/
-/**
- * @fileoverview Utility functions for the tensorflow graph visualizer.
- */
-var tf;
-(function (tf) {
- var graph;
- (function (graph) {
- var util;
- (function (util) {
- /**
- * Recommended delay (ms) when running an expensive task asynchronously
- * that gives enough time for the progress bar to update its UI.
- */
- var ASYNC_TASK_DELAY = 20;
- function time(msg, task) {
- var start = Date.now();
- var result = task();
- /* tslint:disable */
- console.log(msg, ':', Date.now() - start, 'ms');
- /* tslint:enable */
- return result;
- }
- util.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 });
- },
- };
- }
- util.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
- * becames relative to the main task.
- */
- function getSubtaskTracker(parentTracker, impactOnTotalProgress, subtaskMsg) {
- return {
- setMessage: function (progressMsg) {
- // The parent should show a concatenation of its message along with
- // its subtask tracker message.
- parentTracker.setMessage(subtaskMsg + ': ' + progressMsg);
- },
- updateProgress: function (incrementValue) {
- // Update the parent progress relative to the child progress.
- // For example, if the sub-task progresses by 30%, and the impact on the
- // total progress is 50%, then the task progresses by 30% * 50% = 15%.
- parentTracker.updateProgress(incrementValue * impactOnTotalProgress / 100);
- },
- reportError: function (msg, err) {
- // The parent should show a concatenation of its message along with
- // its subtask error message.
- parentTracker.reportError(subtaskMsg + ': ' + msg, err);
- }
- };
- }
- util.getSubtaskTracker = getSubtaskTracker;
- /**
- * Runs an expensive task and return the result.
- */
- function runTask(msg, incProgressValue, task, tracker) {
- // Update the progress message to say the current running task.
- tracker.setMessage(msg);
- // Run the expensive task with a delay that gives enough time for the
- // UI to update.
- try {
- var result = tf.graph.util.time(msg, task);
- // Update the progress value.
- tracker.updateProgress(incProgressValue);
- // Return the result to be used by other tasks.
- return result;
- }
- catch (e) {
- // Errors that happen inside asynchronous tasks are
- // reported to the tracker using a user-friendly message.
- tracker.reportError('Failed ' + msg, e);
- }
- }
- util.runTask = runTask;
- /**
- * Runs an expensive task asynchronously and returns a promise of the result.
- */
- function runAsyncTask(msg, incProgressValue, task, tracker) {
- return new Promise(function (resolve, reject) {
- // Update the progress message to say the current running task.
- tracker.setMessage(msg);
- // Run the expensive task with a delay that gives enough time for the
- // UI to update.
- setTimeout(function () {
- try {
- var result = tf.graph.util.time(msg, task);
- // Update the progress value.
- tracker.updateProgress(incProgressValue);
- // Return the result to be used by other tasks.
- resolve(result);
- }
- catch (e) {
- // Errors that happen inside asynchronous tasks are
- // reported to the tracker using a user-friendly message.
- tracker.reportError('Failed ' + msg, e);
- }
- }, ASYNC_TASK_DELAY);
- });
- }
- util.runAsyncTask = runAsyncTask;
- /**
- * Asynchronously runs an expensive task that returns a promise. Updates the
- * tracker's progress after the promise resolves. Returns a new promise that
- * resolves after the progress is updated.
- */
- function runAsyncPromiseTask(msg, incProgressValue, task, tracker) {
- return new Promise(function (resolve, reject) {
- var handleError = function (e) {
- // Errors that happen inside asynchronous tasks are
- // reported to the tracker using a user-friendly message.
- tracker.reportError('Failed ' + msg, e);
- reject(e);
- };
- // Update the progress message to say the current running task.
- tracker.setMessage(msg);
- // Run the expensive task with a delay that gives enough time for the
- // UI to update.
- setTimeout(function () {
- try {
- var start_1 = Date.now();
- task()
- .then(function (value) {
- /* tslint:disable */
- console.log(msg, ':', Date.now() - start_1, 'ms');
- // Update the progress value.
- tracker.updateProgress(incProgressValue);
- // Return the result to be used by other tasks.
- resolve(value);
- })
- .catch(handleError);
- }
- catch (e) {
- handleError(e);
- }
- }, ASYNC_TASK_DELAY);
- });
- }
- util.runAsyncPromiseTask = runAsyncPromiseTask;
- /**
- * Returns a query selector with escaped special characters that are not
- * allowed in a query selector.
- */
- function escapeQuerySelector(querySelector) {
- return querySelector.replace(/([:.\[\],/\\\(\)])/g, '\\$1');
- }
- util.escapeQuerySelector = escapeQuerySelector;
- // For unit conversion.
- util.MEMORY_UNITS = [
- // Atomic unit.
- { symbol: 'B' },
- // numUnits specifies how many previous units this unit contains.
- { symbol: 'KB', numUnits: 1024 }, { symbol: 'MB', numUnits: 1024 },
- { symbol: 'GB', numUnits: 1024 }, { symbol: 'TB', numUnits: 1024 },
- { symbol: 'PB', numUnits: 1024 }
- ];
- util.TIME_UNITS = [
- // Atomic unit. Finest granularity in TensorFlow stat collection.
- { symbol: 'µs' },
- // numUnits specifies how many previous units this unit contains.
- { symbol: 'ms', numUnits: 1000 }, { symbol: 's', numUnits: 1000 },
- { symbol: 'min', numUnits: 60 }, { symbol: 'hr', numUnits: 60 },
- { symbol: 'days', numUnits: 24 }
- ];
- /**
- * Returns the human readable version of the unit.
- * (e.g. 1.35 GB, 23 MB, 34 ms, 6.53 min etc).
- */
- function convertUnitsToHumanReadable(value, units, unitIndex) {
- unitIndex = unitIndex == null ? 0 : unitIndex;
- if (unitIndex + 1 < units.length &&
- value >= units[unitIndex + 1].numUnits) {
- return tf.graph.util.convertUnitsToHumanReadable(value / units[unitIndex + 1].numUnits, units, unitIndex + 1);
- }
- // toPrecision() has the tendency to return a number in scientific
- // notation and (number - 0) brings it back to normal notation.
- return (value.toPrecision(3) - 0) + ' ' + units[unitIndex].symbol;
- }
- util.convertUnitsToHumanReadable = convertUnitsToHumanReadable;
- function hasDisplayableNodeStats(stats) {
- if (stats &&
- (stats.totalBytes > 0 || stats.totalMicros > 0 || stats.outputSize)) {
- return true;
- }
- return false;
- }
- util.hasDisplayableNodeStats = hasDisplayableNodeStats;
- /**
- * Given a list of strings, it returns a new list of strings with the longest
- * common prefix removed. If the common prefix is one of the strings in the
- * list, it returns the original strings.
- */
- function removeCommonPrefix(strings) {
- if (strings.length < 2) {
- return strings;
- }
- var index = 0;
- var largestIndex = 0;
- // Find the shortest name across all strings.
- var minLength = _.min(_.map(strings, function (str) { return str.length; }));
- var _loop_1 = function () {
- index++;
- var prefixes = _.map(strings, function (str) { return str.substring(0, index); });
- var allTheSame = prefixes.every(function (prefix, i) {
- return (i === 0 ? true : prefix === prefixes[i - 1]);
- });
- if (allTheSame) {
- if (index >= minLength) {
- return { value: strings };
- }
- largestIndex = index;
- }
- else {
- return "break";
- }
- };
- while (true) {
- var state_1 = _loop_1();
- if (typeof state_1 === "object")
- return state_1.value;
- if (state_1 === "break")
- break;
- }
- return _.map(strings, function (str) { return str.substring(largestIndex); });
- }
- util.removeCommonPrefix = removeCommonPrefix;
- /**
- * Given a queryString, aka ?foo=1&bar=2, return the object representation.
- */
- function getQueryParams(queryString) {
- if (queryString.charAt(0) === '?') {
- queryString = queryString.slice(1);
- }
- var queryParams = _.chain(queryString.split('&'))
- .map(function (item) {
- if (item) {
- return item.split('=');
- }
- })
- .compact()
- .value();
- return _.object(queryParams);
- }
- util.getQueryParams = getQueryParams;
- })(util = graph.util || (graph.util = {}));
- })(graph = tf.graph || (tf.graph = {}));
-})(tf || (tf = {}));
-</script>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an 'AS IS' BASIS,
-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 scene;
- (function (scene) {
- /** Show minimap when the viewpoint area is less than X% of the whole area. */
- var FRAC_VIEWPOINT_AREA = 0.8;
- var Minimap = (function () {
- /**
- * Constructs a new minimap.
- *
- * @param svg The main svg element.
- * @param zoomG The svg group used for panning and zooming the main svg.
- * @param mainZoom The main zoom behavior.
- * @param minimap The minimap container.
- * @param maxWandH The maximum width/height for the minimap.
- * @param labelPadding Padding in pixels due to the main graph labels.
- */
- function Minimap(svg, zoomG, mainZoom, minimap, maxWandH, labelPadding) {
- var _this = this;
- this.svg = svg;
- this.labelPadding = labelPadding;
- this.zoomG = zoomG;
- this.mainZoom = mainZoom;
- this.maxWandH = maxWandH;
- var $minimap = d3.select(minimap);
- // The minimap will have 2 main components: the canvas showing the content
- // and an svg showing a rectangle of the currently zoomed/panned viewpoint.
- var $minimapSvg = $minimap.select('svg');
- // Make the viewpoint rectangle draggable.
- var $viewpoint = $minimapSvg.select('rect');
- var dragmove = function (d) {
- _this.viewpointCoord.x = d3.event.x;
- _this.viewpointCoord.y = d3.event.y;
- _this.updateViewpoint();
- };
- this.viewpointCoord = { x: 0, y: 0 };
- var drag = d3.behavior.drag().origin(Object).on('drag', dragmove);
- $viewpoint.datum(this.viewpointCoord).call(drag);
- // Make the minimap clickable.
- $minimapSvg.on('click', function () {
- if (d3.event.defaultPrevented) {
- // This click was part of a drag event, so suppress it.
- return;
- }
- // Update the coordinates of the viewpoint.
- var width = Number($viewpoint.attr('width'));
- var height = Number($viewpoint.attr('height'));
- var clickCoords = d3.mouse($minimapSvg.node());
- _this.viewpointCoord.x = clickCoords[0] - width / 2;
- _this.viewpointCoord.y = clickCoords[1] - height / 2;
- _this.updateViewpoint();
- });
- this.viewpoint = $viewpoint.node();
- this.minimapSvg = $minimapSvg.node();
- this.minimap = minimap;
- this.canvas = $minimap.select('canvas.first').node();
- this.canvasBuffer =
- $minimap.select('canvas.second').node();
- this.downloadCanvas =
- $minimap.select('canvas.download').node();
- d3.select(this.downloadCanvas).style('display', 'none');
- this.update();
- }
- /**
- * Updates the position and the size of the viewpoint rectangle.
- * It also notifies the main svg about the new panned position.
- */
- Minimap.prototype.updateViewpoint = function () {
- // Update the coordinates of the viewpoint rectangle.
- d3.select(this.viewpoint)
- .attr('x', this.viewpointCoord.x)
- .attr('y', this.viewpointCoord.y);
- // Update the translation vector of the main svg to reflect the
- // new viewpoint.
- var mainX = -this.viewpointCoord.x * this.scaleMain / this.scaleMinimap;
- var mainY = -this.viewpointCoord.y * this.scaleMain / this.scaleMinimap;
- var zoomEvent = this.mainZoom.translate([mainX, mainY]).event;
- d3.select(this.zoomG).call(zoomEvent);
- };
- /**
- * Redraws the minimap. Should be called whenever the main svg
- * was updated (e.g. when a node was expanded).
- */
- Minimap.prototype.update = function () {
- var _this = this;
- var sceneSize = null;
- try {
- // Get the size of the entire scene.
- sceneSize = this.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;
- }
- var $download = d3.select('#graphdownload');
- this.download = $download.node();
- $download.on('click', function (d) {
- _this.download.href = _this.downloadCanvas.toDataURL('image/png');
- });
- var $svg = d3.select(this.svg);
- // Read all the style rules in the document and embed them into the svg.
- // The svg needs to be self contained, i.e. all the style rules need to be
- // embedded so the canvas output matches the origin.
- var stylesText = '';
- for (var k = 0; k < document.styleSheets.length; k++) {
- try {
- var cssRules = document.styleSheets[k].cssRules ||
- document.styleSheets[k].rules;
- if (cssRules == null) {
- continue;
- }
- for (var i = 0; i < cssRules.length; i++) {
- // Remove tf-* selectors from the styles.
- stylesText +=
- cssRules[i].cssText.replace(/ ?tf-[\w-]+ ?/g, '') + '\n';
- }
- }
- catch (e) {
- if (e.name !== 'SecurityError') {
- throw e;
- }
- }
- }
- // Temporarily add the css rules to the main svg.
- var svgStyle = $svg.append('style');
- svgStyle.text(stylesText);
- // Temporarily remove the zoom/pan transform from the main svg since we
- // want the minimap to show a zoomed-out and centered view.
- var $zoomG = d3.select(this.zoomG);
- var zoomTransform = $zoomG.attr('transform');
- $zoomG.attr('transform', null);
- // Since we add padding, account for that here.
- sceneSize.height += this.labelPadding * 2;
- sceneSize.width += this.labelPadding * 2;
- // Temporarily assign an explicit width/height to the main svg, since
- // it doesn't have one (uses flex-box), but we need it for the canvas
- // to work.
- $svg.attr({
- width: sceneSize.width,
- height: sceneSize.height,
- });
- // Since the content inside the svg changed (e.g. a node was expanded),
- // the aspect ratio have also changed. Thus, we need to update the scale
- // factor of the minimap. The scale factor is determined such that both
- // the width and height of the minimap are <= maximum specified w/h.
- this.scaleMinimap =
- this.maxWandH / Math.max(sceneSize.width, sceneSize.height);
- this.minimapSize = {
- width: sceneSize.width * this.scaleMinimap,
- height: sceneSize.height * this.scaleMinimap
- };
- // Update the size of the minimap's svg, the buffer canvas and the
- // viewpoint rect.
- d3.select(this.minimapSvg).attr(this.minimapSize);
- d3.select(this.canvasBuffer).attr(this.minimapSize);
- // Download canvas width and height are multiples of the style width and
- // height in order to increase pixel density of the PNG for clarity.
- d3.select(this.downloadCanvas).style({ width: sceneSize.width, height: sceneSize.height });
- d3.select(this.downloadCanvas).attr({ width: sceneSize.width * 3, height: sceneSize.height * 3 });
- if (this.translate != null && this.zoom != null) {
- // Update the viewpoint rectangle shape since the aspect ratio of the
- // map has changed.
- requestAnimationFrame(function () { return _this.zoom(); });
- }
- // Serialize the main svg to a string which will be used as the rendering
- // content for the canvas.
- var svgXml = (new XMLSerializer()).serializeToString(this.svg);
- // Now that the svg is serialized for rendering, remove the temporarily
- // assigned styles, explicit width and height and bring back the pan/zoom
- // transform.
- svgStyle.remove();
- $svg.attr({
- width: null,
- height: null
- });
- $zoomG.attr('transform', zoomTransform);
- var image = new Image();
- image.onload = function () {
- // Draw the svg content onto the buffer canvas.
- var context = _this.canvasBuffer.getContext('2d');
- context.clearRect(0, 0, _this.canvasBuffer.width, _this.canvasBuffer.height);
- context.drawImage(image, 0, 0, _this.minimapSize.width, _this.minimapSize.height);
- requestAnimationFrame(function () {
- // Hide the old canvas and show the new buffer canvas.
- d3.select(_this.canvasBuffer).style('display', null);
- d3.select(_this.canvas).style('display', 'none');
- // Swap the two canvases.
- _a = [_this.canvasBuffer, _this.canvas], _this.canvas = _a[0], _this.canvasBuffer = _a[1];
- var _a;
- });
- var downloadContext = _this.downloadCanvas.getContext('2d');
- downloadContext.clearRect(0, 0, _this.downloadCanvas.width, _this.downloadCanvas.height);
- downloadContext.drawImage(image, 0, 0, _this.downloadCanvas.width, _this.downloadCanvas.height);
- };
- image.onerror = function () {
- var blob = new Blob([svgXml], { type: 'image/svg+xml;charset=utf-8' });
- image.src = URL.createObjectURL(blob);
- };
- image.src =
- 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svgXml);
- };
- /**
- * Handles changes in zooming/panning. Should be called from the main svg
- * to notify that a zoom/pan was performed and this minimap will update it's
- * viewpoint rectangle.
- *
- * @param translate The translate vector, or none to use the last used one.
- * @param scale The scaling factor, or none to use the last used one.
- */
- Minimap.prototype.zoom = function (translate, scale) {
- if (this.scaleMinimap == null) {
- // Scene is not ready yet.
- return;
- }
- // Update the new translate and scale params, only if specified.
- this.translate = translate || this.translate;
- this.scaleMain = scale || this.scaleMain;
- // Update the location of the viewpoint rectangle.
- var svgRect = this.svg.getBoundingClientRect();
- var $viewpoint = d3.select(this.viewpoint);
- this.viewpointCoord.x = -this.translate[0] * this.scaleMinimap /
- this.scaleMain;
- this.viewpointCoord.y = -this.translate[1] * this.scaleMinimap /
- this.scaleMain;
- var viewpointWidth = svgRect.width * this.scaleMinimap / this.scaleMain;
- var viewpointHeight = svgRect.height * this.scaleMinimap / this.scaleMain;
- $viewpoint.attr({
- x: this.viewpointCoord.x,
- y: this.viewpointCoord.y,
- width: viewpointWidth,
- height: viewpointHeight
- });
- // Show/hide the minimap depending on the viewpoint area as fraction of the
- // whole minimap.
- var mapWidth = this.minimapSize.width;
- var mapHeight = this.minimapSize.height;
- var x = this.viewpointCoord.x;
- var y = this.viewpointCoord.y;
- var w = Math.min(Math.max(0, x + viewpointWidth), mapWidth) -
- Math.min(Math.max(0, x), mapWidth);
- var h = Math.min(Math.max(0, y + viewpointHeight), mapHeight) -
- Math.min(Math.max(0, y), mapHeight);
- var fracIntersect = (w * h) / (mapWidth * mapHeight);
- if (fracIntersect < FRAC_VIEWPOINT_AREA) {
- this.minimap.classList.remove('hidden');
- }
- else {
- this.minimap.classList.add('hidden');
- }
- };
- return Minimap;
- }());
- scene.Minimap = Minimap;
- })(scene = tf.scene || (tf.scene = {}));
-})(tf || (tf = {})); // close module tf.scene
-</script>
-
-<dom-module id="tf-graph-minimap" assetpath="../tf-graph/">
-<template>
-<style>
-:host {
- background-color:white;
- transition: opacity .3s linear;
- pointer-events: auto;
-}
-
-:host.hidden {
- opacity: 0;
- pointer-events: none;
-}
-
-canvas {
- border: 1px solid #999;
-}
-
-rect {
- fill: white;
- stroke: #111111;
- stroke-width: 1px;
- fill-opacity: 0;
- filter: url("#minimapDropShadow");
- cursor: move;
-}
-
-svg {
- position: absolute;
-}
-</style>
-<svg>
- <defs>
- <filter id="minimapDropShadow" x="-20%" y="-20%" width="150%" height="150%">
- <feOffset result="offOut" in="SourceGraphic" dx="1" dy="1"></feOffset>
- <feColorMatrix result="matrixOut" in="offOut" type="matrix" values="0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.1 0 0 0 0 0 0.5 0"></feColorMatrix>
- <feGaussianBlur result="blurOut" in="matrixOut" stdDeviation="2"></feGaussianBlur>
- <feBlend in="SourceGraphic" in2="blurOut" mode="normal"></feBlend>
- </filter>
- </defs>
- <rect></rect>
-</svg>
-<canvas class="first"></canvas>
-
-<canvas class="second"></canvas>
-<canvas class="download"></canvas>
-</template>
-<script>
-Polymer({
- is: 'tf-graph-minimap',
-
- /**
- * Initializes the minimap and returns a minimap object to notify when
- * things update.
- *
- * @param svg The main svg element.
- * @param zoomG The svg group used for panning and zooming the main svg.
- * @param mainZoom The main zoom behavior.
- * @param maxWandH The maximum width/height for the minimap.
- * @param labelPadding Padding in pixels due to the main graph labels.
- */
- init: function(svg, zoomG, mainZoom, maxWAndH, labelPadding) {
- return new tf.scene.Minimap(svg, zoomG, mainZoom, this, maxWAndH,
- labelPadding);
- }
-});
-</script>
-</dom-module>
-
-<dom-module id="tf-graph-scene" assetpath="../tf-graph/">
-<template>
-<style>
-:host {
- display: flex;
- width: 100%;
- font-size: 20px;
-}
-
-::content #svg {
- overflow: hidden;
- flex: 1;
- height: 100%;
- width: 100%;
-}
-
-::content #hidden {
- position: fixed;
- top: 0px;
- visibility: hidden;
-}
-
-/* --- Node and annotation-node for Metanode --- */
-
-::content .meta > .nodeshape > rect,
-::content .meta > .annotation-node > rect {
- cursor: pointer;
- fill: hsl(0, 0%, 70%);
-}
-
-::content .node.meta.highlighted > .nodeshape > rect,
-::content .node.meta.highlighted > .annotation-node > rect {
- stroke-width: 2;
-}
-
-::content .annotation.meta.highlighted > .nodeshape > rect,
-::content .annotation.meta.highlighted > .annotation-node > rect {
- stroke-width: 1;
-}
-
-::content .meta.selected > .nodeshape > rect,
-::content .meta.selected > .annotation-node > rect {
- stroke: red;
- stroke-width: 2;
-}
-
-::content .node.meta.selected.expanded > .nodeshape > rect,
-::content .node.meta.selected.expanded > .annotation-node > rect {
- stroke: red;
- stroke-width: 3;
-}
-
-::content .annotation.meta.selected > .nodeshape > rect,
-::content .annotation.meta.selected > .annotation-node > rect {
- stroke: red;
- stroke-width: 2;
-}
-
-::content .node.meta.selected.expanded.highlighted > .nodeshape > rect,
-::content .node.meta.selected.expanded.highlighted > .annotation-node > rect {
- stroke: red;
- stroke-width: 4;
-}
-
-::content .faded,
-::content .faded rect,
-::content .faded ellipse,
-::content .faded path,
-::content .faded use,
-::content #rectHatch line,
-::content #ellipseHatch line {
- color: #e0d4b3 !important;
- fill: white;
- stroke: #e0d4b3 !important;
-}
-
-
-::content .faded path {
- stroke-width: 1px !important;
-}
-
-::content .faded rect {
- fill: url("#rectHatch") !important;
-}
-
-::content .faded ellipse,
-::content .faded use {
- fill: url("#ellipseHatch") !important;
-}
-
-::content .faded text {
- opacity: 0;
-}
-
-/* Rules used for input-tracing. */
-::content .input-highlight > * > rect,
-::content .input-highlight > * > ellipse,
-::content .input-highlight > * > use
-{
- fill: white;
- stroke: #ff9800 !important;
-}
-
-/* - Faded non-input styling */
-::content .non-input > * > rect,
-::content .non-input > * > ellipse,
-::content .non-input > * > use,
-/* For Const nodes. */
-::content .non-input > * > .constant:not([class*="input-highlight"]) >
- .annotation-node > ellipse,
-/* For styling of annotation nodes of non-input nodes. */
-::content .non-input > g > .annotation > .annotation-node > rect {
- stroke: #e0d4b3 !important;
- stroke-width: inherit;
- stroke-dasharray: inherit;
-}
-
-
-::content .non-input path {
- visibility: hidden;
-}
-
-::content .non-input > .nodeshape > rect,
-::content .non-input > .annotation-node > rect,
-/* For styling of annotation nodes of non-input nodes. */
-::content .non-input > g > .annotation > .annotation-node > rect
-{
- fill: url("#rectHatch") !important;
-}
-
-::content .non-input ellipse,
-::content .non-input use {
- fill: url("#ellipseHatch") !important;
-}
-
-::content .non-input > text {
- opacity: 0;
-}
-
-::content .non-input .annotation > .annotation-edge {
- marker-end: url("#annotation-arrowhead-faded");
-}
-
-::content .non-input .annotation > .annotation-edge.refline {
- marker-start: url("#ref-annotation-arrowhead-faded");
-}
-
-/* Input edges. */
-::content .input-edge-highlight > text {
- fill: black !important;
-}
-::content .input-edge-highlight > path,
-::content .input-highlight > .in-annotations > .annotation > .annotation-edge,
-::content .input-highlight-selected > .in-annotations > .annotation >
-.annotation-edge {
- stroke: #999 !important;
-}
-
-/* Non-input edges. */
-::content .non-input-edge-highlight,
-::content .non-input > g > .annotation > path,
-/* Annotation styles (label and edges respectively). */
-::content .non-input > g >
-.annotation:not(.input-highlight):not(.input-highlight-selected) >
-.annotation-label
-/*.annotation-edge*/
-{
- visibility: hidden;
-}
-
-/* --- Op Node --- */
-
-::content .op > .nodeshape > ellipse,
-::content .op > .annotation-node > ellipse {
- cursor: pointer;
- fill: #fff;
- stroke: #ccc;
-}
-
-::content .op.selected > .nodeshape > ellipse,
-::content .op.selected > .annotation-node > ellipse {
- stroke: red;
- stroke-width: 2;
-}
-
-::content .op.highlighted > .nodeshape > ellipse,
-::content .op.highlighted > .annotation-node > ellipse {
- stroke-width: 2;
-}
-
-/* --- Series Node --- */
-
-/* By default, don't show the series background <rect>. */
-::content .series > .nodeshape > rect {
- fill: hsl(0, 0%, 70%);
- fill-opacity: 0;
- stroke-dasharray: 5, 5;
- stroke-opacity: 0;
- cursor: pointer;
-}
-
-/* Once expanded, show the series background <rect> and hide the <use>. */
-::content .series.expanded > .nodeshape > rect {
- fill-opacity: 0.15;
- stroke: hsl(0, 0%, 70%);
- stroke-opacity: 1;
-}
-::content .series.expanded > .nodeshape > use {
- visibility: hidden;
-}
-
-/**
- * TODO(jimbo): Simplify this by applying a stable class name to all <g>
- * elements that currently have either the nodeshape or annotation-node classes.
- */
-::content .series > .nodeshape > use ,
-::content .series > .annotation-node > use {
- stroke: #ccc;
-}
-::content .series.highlighted > .nodeshape > use ,
-::content .series.highlighted > .annotation-node > use {
- stroke-width: 2;
-}
-::content .series.selected > .nodeshape > use ,
-::content .series.selected > .annotation-node > use {
- stroke: red;
- stroke-width: 2;
-}
-
-::content .series.selected > .nodeshape > rect {
- stroke: red;
- stroke-width: 2;
-}
-
-::content .annotation.series.selected > .annotation-node > use {
- stroke: red;
- stroke-width: 2;
-}
-
-/* --- Bridge Node --- */
-::content .bridge > .nodeshape > rect {
- stroke: #f0f;
- opacity: 0.2;
- display: none;
-}
-
-/* --- Structural Elements --- */
-::content .edge > path.edgeline.structural {
- stroke: #f0f;
- opacity: 0.2;
- display: none;
-}
-
-/* --- Series Nodes --- */
-
-/* Hide the rect for a series' annotation. */
-::content .series > .annotation-node > rect {
- display: none;
-}
-
-/* --- Node label --- */
-
-
-::content .node > text.nodelabel {
- cursor: pointer;
- fill: #444;
-}
-
-::content .meta.expanded > text.nodelabel {
- font-size: 9px;
-}
-
-::content .series > text.nodelabel {
- font-size: 8px;
-}
-
-::content .op > text.nodelabel {
- font-size: 6px;
-}
-
-::content .bridge > text.nodelabel {
- display: none;
-}
-
-::content .node.meta.expanded > text.nodelabel{
- cursor: normal;
-}
-
-::content .annotation.meta.highlighted > text.annotation-label {
- fill: #50A3F7;
-}
-
-::content .annotation.meta.selected > text.annotation-label {
- fill: #4285F4;
-}
-
-/* --- Annotation --- */
-
-/* only applied for annotations that are not summary or constant.
-(.summary, .constant gets overriden below) */
-::content .annotation > .annotation-node > * {
- stroke-width: 0.5;
- stroke-dasharray: 1, 1;
-}
-
-::content .annotation.summary > .annotation-node > *,
-::content .annotation.constant > .annotation-node > * {
- stroke-width: 1;
- stroke-dasharray: none;
-}
-
-::content .annotation > .annotation-edge {
- fill: none;
- stroke: #aaa;
- stroke-width: 0.5;
- marker-end: url("#annotation-arrowhead");
-}
-
-::content .faded .annotation > .annotation-edge {
- marker-end: url("#annotation-arrowhead-faded");
-}
-
-::content .annotation > .annotation-edge.refline {
- marker-start: url("#ref-annotation-arrowhead");
-}
-
-::content .faded .annotation > .annotation-edge.refline {
- marker-start: url("#ref-annotation-arrowhead-faded");
-}
-
-::content .annotation > .annotation-control-edge {
- stroke-dasharray: 1, 1;
-}
-
-::content #annotation-arrowhead {
- fill: #aaa;
-}
-
-::content #annotation-arrowhead-faded {
- fill: #e0d4b3;
-}
-
-::content #ref-annotation-arrowhead {
- fill: #aaa;
-}
-
-::content #ref-annotation-arrowhead-faded {
- fill: #e0d4b3;
-}
-
-::content .annotation > .annotation-label {
- font-size: 5px;
- cursor: pointer;
-}
-::content .annotation > .annotation-label.annotation-ellipsis {
- cursor: default;
-}
-
-/* Hide annotations on expanded meta nodes since they're redundant. */
-::content .expanded > .in-annotations,
-::content .expanded > .out-annotations {
- display: none;
-}
-
-/* --- Annotation: Constant --- */
-
-::content .constant > .annotation-node > ellipse {
- cursor: pointer;
- fill: white;
- stroke: #848484;
-}
-
-::content .constant.selected > .annotation-node > ellipse {
- fill: white;
- stroke: red;
-}
-
-::content .constant.highlighted > .annotation-node > ellipse {
- stroke-width: 1.5;
-}
-
-/* --- Annotation: Summary --- */
-
-::content .summary > .annotation-node > ellipse {
- cursor: pointer;
- fill: #DB4437;
- stroke: #DB4437;
-}
-
-::content .summary.selected > .annotation-node > ellipse {
- fill: #A52714;
- stroke: #A52714;
-}
-
-::content .summary.highlighted > .annotation-node > ellipse {
- stroke-width: 1.5;
-}
-
-/* --- Edge --- */
-
-::content .edge > path.edgeline {
- fill: none;
- stroke: #bbb;
- stroke-linecap: round;
- stroke-width: 0.75;
-}
-
-/* Labels showing tensor shapes on edges */
-::content .edge > text {
- font-size: 3.5px;
- fill: #666;
-}
-
-::content .ref-arrowhead {
- fill: #bbb;
-}
-
-::content .edge .control-dep {
- stroke-dasharray: 2, 2;
-}
-
-/* --- Group node expand/collapse button --- */
-
-/* Hides expand/collapse buttons when a node isn't expanded or highlighted. Using
- incredibly small opacity so that the bounding box of the <g> parent still takes
- this container into account even when it isn't visible */
-::content .node:not(.highlighted):not(.expanded) > .nodeshape > .buttoncontainer {
- opacity: 0.01;
-}
-::content .node.highlighted > .nodeshape > .buttoncontainer {
- cursor: pointer;
-}
-::content .buttoncircle {
- fill: #E7811D;
-}
-::content .buttoncircle:hover {
- fill: #B96717;
-}
-::content .expandbutton,
-::content .collapsebutton {
- stroke: white;
-}
-/* Do not let the path elements in the button take pointer focus */
-::content .node > .nodeshape > .buttoncontainer > .expandbutton,
-::content .node > .nodeshape > .buttoncontainer > .collapsebutton {
- pointer-events: none;
-}
-/* Only show the expand button when a node is collapsed and only show the
- collapse button when a node is expanded. */
-::content .node.expanded > .nodeshape > .buttoncontainer > .expandbutton {
- display: none;
-}
-::content .node:not(.expanded) > .nodeshape > .buttoncontainer > .collapsebutton {
- display: none;
-}
-
-::content .health-pill-stats {
- font-size: 4px;
- text-anchor: middle;
-}
-
-::content .health-pill rect {
- filter: url("#health-pill-shadow");
- rx: 3;
- ry: 3;
-}
-
-.titleContainer {
- position: relative;
- top: 20px;
-}
-
-.title {
- position: absolute;
-}
-
-.auxTitle {
- position: absolute;
-}
-
-#minimap {
- position: absolute;
- right: 20px;
- bottom: 20px;
-}
-</style>
-<div class="titleContainer">
- <div id="title" class="title">Main Graph</div>
- <div id="auxTitle" class="auxTitle">Auxiliary Nodes</div>
-</div>
-<svg id="svg">
- <defs>
-
-
- <path id="ref-arrowhead-path" d="M 10,0 L 0,5 L 10,10 C 7,7 7,3 10,0"></path>
- <marker class="ref-arrowhead" id="ref-arrowhead-small" viewBox="0 0 10 10" markerWidth="10" markerHeight="10" refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
- <use xlink:href="#ref-arrowhead-path"></use>
- </marker>
- <marker class="ref-arrowhead" id="ref-arrowhead-medium" viewBox="0 0 10 10" markerWidth="13" markerHeight="13" refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
- <use xlink:href="#ref-arrowhead-path"></use>
- </marker>
- <marker class="ref-arrowhead" id="ref-arrowhead-large" viewBox="0 0 10 10" markerWidth="16" markerHeight="16" refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
- <use xlink:href="#ref-arrowhead-path"></use>
- </marker>
- <marker class="ref-arrowhead" id="ref-arrowhead-xlarge" viewBox="0 0 10 10" markerWidth="20" markerHeight="20" refX="8" refY="5" orient="auto" markerUnits="userSpaceOnUse">
- <use xlink:href="#ref-arrowhead-path"></use>
- </marker>
-
-
- <marker id="annotation-arrowhead" markerWidth="5" markerHeight="5" refX="5" refY="2.5" orient="auto">
- <path d="M 0,0 L 5,2.5 L 0,5 L 0,0"></path>
- </marker>
- <marker id="annotation-arrowhead-faded" markerWidth="5" markerHeight="5" refX="5" refY="2.5" orient="auto">
- <path d="M 0,0 L 5,2.5 L 0,5 L 0,0"></path>
- </marker>
- <marker id="ref-annotation-arrowhead" markerWidth="5" markerHeight="5" refX="0" refY="2.5" orient="auto">
- <path d="M 5,0 L 0,2.5 L 5,5 L 5,0"></path>
- </marker>
- <marker id="ref-annotation-arrowhead-faded" markerWidth="5" markerHeight="5" refX="0" refY="2.5" orient="auto">
- <path d="M 5,0 L 0,2.5 L 5,5 L 5,0"></path>
- </marker>
-
- <ellipse id="op-node-stamp" rx="7.5" ry="3" stroke="inherit" fill="inherit"></ellipse>
-
- <ellipse id="op-node-annotation-stamp" rx="5" ry="2" stroke="inherit" fill="inherit"></ellipse>
-
- <g id="op-series-vertical-stamp">
- <use xlink:href="#op-node-stamp" x="8" y="9"></use>
- <use xlink:href="#op-node-stamp" x="8" y="6"></use>
- <use xlink:href="#op-node-stamp" x="8" y="3"></use>
- </g>
-
- <g id="op-series-horizontal-stamp">
- <use xlink:href="#op-node-stamp" x="16" y="4"></use>
- <use xlink:href="#op-node-stamp" x="12" y="4"></use>
- <use xlink:href="#op-node-stamp" x="8" y="4"></use>
- </g>
-
- <g id="op-series-annotation-stamp">
- <use xlink:href="#op-node-annotation-stamp" x="9" y="2"></use>
- <use xlink:href="#op-node-annotation-stamp" x="7" y="2"></use>
- <use xlink:href="#op-node-annotation-stamp" x="5" y="2"></use>
- </g>
- <svg id="summary-icon" fill="#848484" height="12" viewBox="0 0 24 24" width="12">
- <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"></path>
- </svg>
-
- <g id="linearGradients"></g>
-
-
- <pattern id="rectHatch" patternTransform="rotate(45 0 0)" width="5" height="5" patternUnits="userSpaceOnUse">
- <line x1="0" y1="0" x2="0" y2="5" style="stroke-width: 1"></line>
- </pattern>
- <pattern id="ellipseHatch" patternTransform="rotate(45 0 0)" width="2" height="2" patternUnits="userSpaceOnUse">
- <line x1="0" y1="0" x2="0" y2="2" style="stroke-width: 1"></line>
- </pattern>
-
-
- <filter id="health-pill-shadow" x="-40%" y="-40%" width="180%" height="180%">
- <feGaussianBlur in="SourceAlpha" stdDeviation="0.8"></feGaussianBlur>
- <feOffset dx="0" dy="0" result="offsetblur"></feOffset>
- <feFlood flood-color="#000000"></feFlood>
- <feComposite in2="offsetblur" operator="in"></feComposite>
- <feMerge>
- <feMergeNode></feMergeNode>
- <feMergeNode in="SourceGraphic"></feMergeNode>
- </feMerge>
- </filter>
- </defs>
-
- <rect fill="white" width="10000" height="10000"></rect>
- <g id="root"></g>
-</svg>
-<tf-graph-minimap id="minimap"></tf-graph-minimap>
-</template>
-<script>
-Polymer({
- is: 'tf-graph-scene',
- properties: {
- renderHierarchy: Object,
- name: String,
- colorBy: String,
-
- // For each render hierarchy, we only fit it to the viewport once (when the scene is attached to
- // the DOM). We do not fit the hierarchy again (unless the user clicks the reset button). For
- // instance, if the user enters a certain view in the graph, switches to another dashboard, and
- // returns to the graph dashboard, the user expects the previous view. These properties enable
- // that behavior.
-
- /** Whether the scene has fit the current render hierarchy (to the viewport) at least once. */
- _hasRenderHierarchyBeenFitOnce: Boolean,
- /** Whether this scene element is currently attached to a parent element. */
- _isAttached: Boolean,
-
- /** @type {d3_zoom} d3 zoom object */
- _zoom: Object,
- highlightedNode: {
- type: String,
- observer: '_highlightedNodeChanged'
- },
- selectedNode: {
- type: String,
- observer: '_selectedNodeChanged'
- },
- /** Keeps track of if the graph has been zoomed/panned since loading */
- _zoomed: {
- type: Boolean,
- observer: '_onZoomChanged',
- value: false
- },
- /** Keeps track of the starting coordinates of a graph zoom/pan */
- _zoomStartCoords: {
- type: Array,
- value: null
- },
- /** Keeps track of the current coordinates of a graph zoom/pan */
- _zoomCoords: {
- type: Array,
- value: null
- },
- /** Maximum distance of a zoom event for it to be interpreted as a click */
- _maxZoomDistanceForClick: {
- type: Number,
- value: 20
- },
- /**
- * @type {d3.scale.ordinal}
- * Scale mapping from template name to a number between 0 and N-1
- * where N is the number of different template names. Used by
- * tf.graph.scene.node when computing node color by structure.
- */
- templateIndex: Function,
- /**
- * @type {tf.scene.Minimap}
- * A minimap object to notify for zoom events.
- */
- minimap: Object,
- /*
- * Dictionary for easily stylizing nodes when state changes.
- * _nodeGroupIndex[nodeName] = d3_selection of the nodeGroup
- */
- _nodeGroupIndex: {
- type: Object,
- value: function() { return {}; }
- },
- /*
- * Dictionary for easily stylizing annotation nodes when state changes.
- * _annotationGroupIndex[nodeName][hostNodeName] =
- * d3_selection of the annotationGroup
- */
- _annotationGroupIndex: {
- type: Object,
- value: function() { return {}; }
- },
- /*
- * Dictionary for easily stylizing edges when state changes.
- * _edgeGroupIndex[edgeName] = d3_selection of the edgeGroup
- */
- _edgeGroupIndex: {
- type: Object,
- value: function() { return {}; }
- },
- /**
- * Max font size for metanode label strings.
- */
- maxMetanodeLabelLengthFontSize: {
- type: Number,
- value: 9
- },
- /**
- * Min font size for metanode label strings.
- */
- minMetanodeLabelLengthFontSize: {
- type: Number,
- value: 6
- },
- /**
- * Metanode label strings longer than this are given smaller fonts.
- */
- maxMetanodeLabelLengthLargeFont: {
- type: Number,
- value: 11
- },
- /**
- * Metanode label strings longer than this are truncated with ellipses.
- */
- maxMetanodeLabelLength: {
- type: Number,
- value: 18
- },
- progress: Object,
- // A mapping between node name to the tf.graph.scene.HealthPill to render.
- nodeNamesToHealthPills: Object,
- // The step of health pills to show throughout the graph.
- healthPillStepIndex: Number,
- },
- observers: [
- '_colorByChanged(colorBy)',
- '_renderHierarchyChanged(renderHierarchy)',
- // Animation and fitting must come after the observer for the hierarchy changing because we must
- // first build the render hierarchy.
- '_animateAndFit(_isAttached, renderHierarchy)',
- '_updateHealthPills(nodeNamesToHealthPills, healthPillStepIndex)',
- ],
- getNode: function(nodeName) {
- return this.renderHierarchy.getRenderNodeByName(nodeName);
- },
- isNodeExpanded: function(node) {
- return node.expanded;
- },
- setNodeExpanded: function(renderNode) {
- this._build(this.renderHierarchy);
- this._updateLabels(!this._zoomed);
- },
- /**
- * Resets the state of the component. Called whenever the whole graph
- * (dataset) changes.
- */
- _resetState: function() {
- // Reset the state of the component.
- this._nodeGroupIndex = {};
- this._annotationGroupIndex = {};
- this._edgeGroupIndex = {};
- this._updateLabels(false);
- // Remove all svg elements under the 'root' svg group.
- d3.select(this.$.svg).select('#root').selectAll('*').remove();
- // And the defs.
- d3.select(this.$.svg).select('defs #linearGradients')
- .selectAll('*').remove();
- },
- /** Main method for building the scene */
- _build: function(renderHierarchy) {
- this.templateIndex = renderHierarchy.hierarchy.getTemplateIndex();
- tf.graph.util.time('tf-graph-scene (layout):', function() {
- // layout the scene for this meta / series node
- tf.graph.layout.layoutScene(renderHierarchy.root, this);
- }.bind(this));
-
- tf.graph.util.time('tf-graph-scene (build scene):', function() {
- tf.graph.scene.buildGroup(d3.select(this.$.root), renderHierarchy.root, this);
- tf.graph.scene.addGraphClickListener(this.$.svg, this);
- tf.graph.scene.node.traceInputs(renderHierarchy);
- }.bind(this));
- // Update the minimap again when the graph is done animating.
- setTimeout(function() {
- this._updateHealthPills(this.nodeNamesToHealthPills, this.healthPillStepIndex);
- this.minimap.update();
- }.bind(this), tf.graph.layout.PARAMS.animation.duration);
- },
- ready: function() {
- this._zoom = d3.behavior.zoom()
- .on('zoomend', function() {
- if (this._zoomStartCoords) {
- // Calculate the total distance dragged during the zoom event.
- // If it is sufficiently small, then fire an event indicating
- // that zooming has ended. Otherwise wait to fire the zoom end
- // event, so that a mouse click registered as part of this zooming
- // is ignored (as this mouse click was part of a zooming, and should
- // not be used to indicate an actual click on the graph).
- var dragDistance = Math.sqrt(
- Math.pow(this._zoomStartCoords[0] - this._zoomCoords[0], 2) +
- Math.pow(this._zoomStartCoords[1] - this._zoomCoords[1], 2));
- if (dragDistance < this._maxZoomDistanceForClick) {
- this._fireEnableClick();
- } else {
- setTimeout(this._fireEnableClick.bind(this), 50);
- }
- }
- this._zoomStartCoords = null;
- }.bind(this))
- .on('zoom', function() {
- // Store the coordinates of the zoom event
- this._zoomCoords = d3.event.translate;
-
- // If this is the first zoom event after a zoom-end, then
- // store the coordinates as the start coordinates as well,
- // and fire an event to indicate that zooming has started.
- // This doesn't use the zoomstart event, as d3 sends this
- // event on mouse-down, even if there has been no dragging
- // done to translate the graph around.
- if (!this._zoomStartCoords) {
- this._zoomStartCoords = this._zoomCoords.slice();
- this.fire('disable-click');
- }
- this._zoomed = true;
- d3.select(this.$.root).attr('transform',
- 'translate(' + d3.event.translate + ')' +
- 'scale(' + d3.event.scale + ')');
- // Notify the minimap.
- this.minimap.zoom(d3.event.translate, d3.event.scale);
- }.bind(this));
- d3.select(this.$.svg).call(this._zoom)
- .on('dblclick.zoom', null);
- d3.select(window).on('resize', function() {
- // Notify the minimap that the user's window was resized.
- // The minimap will figure out the new dimensions of the main svg
- // and will use the existing translate and scale params.
- this.minimap.zoom();
- }.bind(this));
- // Initialize the minimap.
- this.minimap = this.$.minimap.init(this.$.svg, this.$.root, this._zoom,
- tf.graph.layout.PARAMS.minimap.size,
- tf.graph.layout.PARAMS.subscene.meta.labelHeight);
- },
- attached: function() {
- this.set('_isAttached', true);
- },
- detached: function() {
- this.set('_isAttached', false);
- },
- _renderHierarchyChanged: function(renderHierarchy) {
- this._hasRenderHierarchyBeenFitOnce = false;
- this._resetState();
- this._build(renderHierarchy);
- },
- _animateAndFit: function(isAttached, renderHierarchy) {
- if (this._hasRenderHierarchyBeenFitOnce || !isAttached) {
- // Do not animate and fit if the scene has already fitted this render hierarchy once. Or if
- // the graph dashboard is not attached (in which case the scene lacks DOM info for fitting).
- return;
- }
-
- // Fit to screen after the graph is done animating.
- setTimeout(this.fit.bind(this), tf.graph.layout.PARAMS.animation.duration);
- },
- _updateLabels: function(showLabels) {
- var mainGraphTitleElement = this.getElementsByClassName('title')[0];
- var titleStyle = mainGraphTitleElement.style;
- var auxTitleStyle = this.getElementsByClassName('auxTitle')[0].style;
- var core = d3.select("." + tf.graph.scene.Class.Scene.GROUP + ">." +
- tf.graph.scene.Class.Scene.CORE)[0][0];
- // Only show labels if the graph is fully loaded.
- if (showLabels && core && this.progress && this.progress.value === 100) {
- var aux =
- d3.select("." + tf.graph.scene.Class.Scene.GROUP + ">." +
- tf.graph.scene.Class.Scene.INEXTRACT)[0][0] ||
- d3.select("." + tf.graph.scene.Class.Scene.GROUP + ">." +
- tf.graph.scene.Class.Scene.OUTEXTRACT)[0][0];
- var coreX = core.getCTM().e;
- var auxX = aux ? aux.getCTM().e : null;
- titleStyle.display = 'inline';
- titleStyle.left = coreX + 'px';
- if (auxX !== null && auxX !== coreX) {
- auxTitleStyle.display = 'inline';
-
- // Make sure that the aux title is positioned rightwards enough so as to
- // prevent overlap with the main graph title.
- auxX = Math.max(
- coreX + mainGraphTitleElement.getBoundingClientRect().width, auxX);
-
- auxTitleStyle.left = auxX + 'px';
- } else {
- auxTitleStyle.display = 'none';
- }
- } else {
- titleStyle.display='none';
- auxTitleStyle.display = 'none';
- }
- },
- /**
- * Called whenever the user changed the 'color by' option in the
- * UI controls.
- */
- _colorByChanged: function() {
- if (this.renderHierarchy != null) {
- // We iterate through each svg node and update its state.
- _.each(this._nodeGroupIndex, function(nodeGroup, nodeName) {
- this._updateNodeState(nodeName);
- }, this);
- // Notify also the minimap.
- this.minimap.update();
- }
- },
- fit: function() {
- this._hasRenderHierarchyBeenFitOnce = true;
- tf.graph.scene.fit(this.$.svg, this.$.root, this._zoom, function() {
- this._zoomed = false;
- }.bind(this));
- },
- isNodeSelected: function(n) {
- return n === this.selectedNode;
- },
- isNodeHighlighted: function(n) {
- return n === this.highlightedNode;
- },
- addAnnotationGroup: function(a, d, selection) {
- var an = a.node.name;
- this._annotationGroupIndex[an] = this._annotationGroupIndex[an] || {};
- this._annotationGroupIndex[an][d.node.name] = selection;
- },
- getAnnotationGroupsIndex: function(a) {
- return this._annotationGroupIndex[a];
- },
- removeAnnotationGroup: function(a, d) {
- delete this._annotationGroupIndex[a.node.name][d.node.name];
- },
- addNodeGroup: function(n, selection) {
- this._nodeGroupIndex[n] = selection;
- },
- getNodeGroup: function(n) {
- return this._nodeGroupIndex[n];
- },
- removeNodeGroup: function(n) {
- delete this._nodeGroupIndex[n];
- },
- addEdgeGroup: function(n, selection) {
- this._edgeGroupIndex[e] = selection;
- },
- getEdgeGroup: function(e) {
- return this._edgeGroupIndex[e];
- },
- _updateHealthPills: function(nodeNamesToHealthPills, healthPillStepIndex) {
- tf.graph.scene.addHealthPills(
- this.$.svg, nodeNamesToHealthPills, healthPillStepIndex);
- },
- /**
- * Update node and annotation node of the given name.
- * @param {String} n node name
- */
- _updateNodeState: function(n) {
- var node = this.getNode(n);
- var nodeGroup = this.getNodeGroup(n);
-
- if (nodeGroup) {
- tf.graph.scene.node.stylize(nodeGroup, node, this);
- }
-
- var annotationGroupIndex = this.getAnnotationGroupsIndex(n);
- _.each(annotationGroupIndex, function(aGroup, hostName) {
- tf.graph.scene.node.stylize(aGroup, node, this,
- tf.graph.scene.Class.Annotation.NODE);
- }, this);
- },
-
- /**
- * Handles new node selection. 1) Updates the selected-state of each node,
- * 2) triggers input tracing.
- * @param selectedNode {string} The name of the newly selected node.
- * @param oldSelectedNode {string} The name of the previously selected node.
- * @private
- */
- _selectedNodeChanged: function(selectedNode, oldSelectedNode) {
- if (selectedNode === oldSelectedNode) {
- return;
- }
-
- if (selectedNode) {
- this._updateNodeState(selectedNode);
- }
- if (oldSelectedNode) {
- this._updateNodeState(oldSelectedNode);
- }
-
- tf.graph.scene.node.traceInputs(this.renderHierarchy);
-
- if (!selectedNode) {
- return;
- }
-
-
- // Update the minimap to reflect the highlighted (selected) node.
- this.minimap.update();
- var node = this.renderHierarchy.hierarchy.node(selectedNode);
- var nodeParents = [];
- // Create list of all metanode parents of the selected node.
- while (node.parentNode != null
- && node.parentNode.name != tf.graph.ROOT_NAME) {
- node = node.parentNode;
- nodeParents.push(node.name);
- }
- // Ensure each parent metanode is built and expanded.
- var topParentNodeToBeExpanded;
- _.forEachRight(nodeParents, function(parentName) {
- this.renderHierarchy.buildSubhierarchy(parentName);
- var renderNode = this.renderHierarchy.getRenderNodeByName(parentName);
- if (renderNode.node.isGroupNode && !renderNode.expanded) {
- renderNode.expanded = true;
- if (!topParentNodeToBeExpanded) {
- topParentNodeToBeExpanded = renderNode;
- }
- }
- }, this);
- // If any expansion was needed to display this selected node, then
- // inform the scene of the top-most expansion.
- if (topParentNodeToBeExpanded) {
- this.setNodeExpanded(topParentNodeToBeExpanded);
- this._zoomed = true;
- }
-
- if (tf.graph.scene.panToNode(selectedNode, this.$.svg, this.$.root,
- this._zoom)) {
- this._zoomed = true;
- }
- },
- _highlightedNodeChanged: function(highlightedNode, oldHighlightedNode) {
- if (highlightedNode === oldHighlightedNode) {
- return;
- }
-
- if (highlightedNode) {
- this._updateNodeState(highlightedNode);
- }
- if (oldHighlightedNode) {
- this._updateNodeState(oldHighlightedNode);
- }
- },
- _onZoomChanged: function() {
- this._updateLabels(!this._zoomed);
- },
- _fireEnableClick: function() {
- this.fire('enable-click');
- },
-});
-</script>
-</dom-module>
-<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
-<dom-module id="tf-graph" assetpath="../tf-graph/">
-<template>
-<style>
-.container {
- width: 100%;
- height: 100%;
- background: white;
- box-shadow: 0 1px 5px rgba(0,0,0,0.2);
-}
-
-.vertical {
- width:100%;
- height:100%;
- @apply(--layout-vertical);
-}
-
-.auto {
- @apply(--layout-flex-auto);
- @apply(--layout-vertical);
-}
-
-h2 {
- text-align: center;
-}
-
-paper-button {
- text-transform: none;
-}
-</style>
-<div class="container">
- <div class="vertical">
- <template is="dom-if" if="[[title]]">
- <h2>[[title]]</h2>
- </template>
- <tf-graph-scene id="scene" class="auto" render-hierarchy="[[renderHierarchy]]" highlighted-node="[[_getVisible(highlightedNode)]]" selected-node="{{selectedNode}}" color-by="[[colorBy]]" progress="[[progress]]" node-names-to-health-pills="[[nodeNamesToHealthPills]]" health-pill-step-index="{{healthPillStepIndex}}"></tf-graph-scene>
- </div>
-</div>
-</template>
-</dom-module>
-
-<script>
-Polymer({
-
- is: 'tf-graph',
-
- properties: {
- graphHierarchy: {
- type: Object,
- notify: true,
- observer: '_graphChanged'
- },
- basicGraph: Object,
- stats: Object,
- devicesForStats: Object,
- hierarchyParams: Object,
- progress: {
- type: Object,
- notify: true,
- },
- title: String,
- selectedNode: {
- type: String,
- notify: true,
- },
- highlightedNode: {
- type: String,
- notify: true
- },
- /** What to color the nodes by (compute time, memory, device etc.) */
- colorBy: String,
- colorByParams: {
- type: Object,
- notify: true,
- readOnly: true, // Produces and doesn't consume.
- },
- renderHierarchy: {
- type: Object,
- readOnly: true,
- notify: true,
- },
- _renderDepth: {
- type: Number,
- value: 1
- },
- _allowGraphSelect: {
- type: Boolean,
- value: true
- },
- // A mapping between node name to the tf.graph.scene.HealthPill to render.
- nodeNamesToHealthPills: Object,
- // The step of health pills to show throughout the graph.
- healthPillStepIndex: Number,
- },
- observers: [
- '_statsChanged(stats, devicesForStats)',
- '_buildRenderHierarchy(graphHierarchy)'
- ],
- _statsChanged: function(stats, devicesForStats) {
- if (this.graphHierarchy) {
- if (stats && devicesForStats) {
- tf.graph.joinStatsInfoWithGraph(this.basicGraph, stats, devicesForStats);
- tf.graph.hierarchy.joinAndAggregateStats(this.graphHierarchy, stats);
- }
- // Recompute the rendering information.
- this._buildRenderHierarchy(this.graphHierarchy);
- }
- },
- _buildRenderHierarchy: function(graphHierarchy) {
- tf.graph.util.time('new tf.graph.render.Hierarchy', function() {
- if (graphHierarchy.root.type !== tf.graph.NodeType.META) {
- // root must be metanode but sometimes Polymer's dom-if has not
- // remove tf-graph element yet in <tf-node-info>
- // and thus mistakenly pass non-metanode to this module.
- return;
- }
- var renderGraph = new tf.graph.render.RenderGraphInfo(
- graphHierarchy, !!this.stats /** displayingStats */);
- // Producing the 'color by' parameters to be consumed
- // by the tf-graph-controls panel. It contains information about the
- // min and max values and their respective colors, as well as list
- // of devices with their respective colors.
-
- function getColorParamsFromScale(scale) {
- return {
- minValue: scale.domain()[0],
- maxValue: scale.domain()[1],
- startColor: scale.range()[0],
- endColor: scale.range()[1]
- };
- }
-
- this._setColorByParams({
- compute_time: getColorParamsFromScale(renderGraph.computeTimeScale),
- memory: getColorParamsFromScale(renderGraph.memoryUsageScale),
- device: _.map(renderGraph.deviceColorMap.domain(),
- function(deviceName) {
- return {
- device: deviceName,
- color: renderGraph.deviceColorMap(deviceName)
- };
- }),
- xla_cluster: _.map(renderGraph.xlaClusterColorMap.domain(),
- function(xlaClusterName) {
- return {
- xla_cluster: xlaClusterName,
- color: renderGraph.xlaClusterColorMap(xlaClusterName)
- };
- }),
- });
- this._setRenderHierarchy(renderGraph);
- this.async(function() {
- this.fire("rendered");
- });
- }.bind(this));
- },
- _getVisible: function(name) {
- if (!name) {
- return name;
- }
- return this.renderHierarchy.getNearestVisibleAncestor(name);
- },
- listeners: {
- 'graph-select': '_graphSelected',
- 'disable-click': '_disableClick',
- 'enable-click': '_enableClick',
- // Nodes
- 'node-toggle-expand': '_nodeToggleExpand',
- 'node-select': '_nodeSelected',
- 'node-highlight': '_nodeHighlighted',
- 'node-unhighlight': '_nodeUnhighlighted',
- 'node-toggle-extract': '_nodeToggleExtract',
- 'node-toggle-seriesgroup': '_nodeToggleSeriesGroup',
-
- // Annotations
-
- /* Note: currently highlighting/selecting annotation node has the same
- * behavior as highlighting/selecting actual node so we point to the same
- * set of event listeners. However, we might redesign this to be a bit
- * different.
- */
- 'annotation-select': '_nodeSelected',
- 'annotation-highlight': '_nodeHighlighted',
- 'annotation-unhighlight': '_nodeUnhighlighted',
- },
- _graphChanged: function() {
- // When a new graph is loaded, fire this event so that there is no
- // info-card being displayed for the previously-loaded graph.
- this.fire('graph-select');
- },
- _graphSelected: function(event) {
- // Graph selection is not allowed during an active zoom event, as the
- // click seen during a zoom/pan is part of the zooming and does not
- // indicate a user desire to click on a specific section of the graph.
- if (this._allowGraphSelect) {
- this.set('selectedNode', null);
- }
- // Reset this variable as a bug in d3 zoom behavior can cause zoomend
- // callback not to be called if a right-click happens during a zoom event.
- this._allowGraphSelect = true;
- },
- _disableClick: function(event) {
- this._allowGraphSelect = false;
- },
- _enableClick: function(event) {
- this._allowGraphSelect = true;
- },
- _nodeSelected: function(event) {
- if (this._allowGraphSelect) {
- this.set('selectedNode', event.detail.name);
- }
- // Reset this variable as a bug in d3 zoom behavior can cause zoomend
- // callback not to be called if a right-click happens during a zoom event.
- this._allowGraphSelect = true;
- },
- _nodeHighlighted: function(event) {
- this.set('highlightedNode', event.detail.name);
- },
- _nodeUnhighlighted: function(event) {
- 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.
- if (renderNode.node.type === tf.graph.NodeType.OP) {
- return;
- }
- this.renderHierarchy.buildSubhierarchy(nodeName);
- renderNode.expanded = !renderNode.expanded;
-
- // 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.
- var nodeName = event.detail.name;
- var renderNode = this.renderHierarchy.getRenderNodeByName(nodeName);
- if (renderNode.node.include == tf.graph.InclusionType.INCLUDE) {
- renderNode.node.include = tf.graph.InclusionType.EXCLUDE;
- } else if (renderNode.node.include == tf.graph.InclusionType.EXCLUDE) {
- renderNode.node.include = tf.graph.InclusionType.INCLUDE;
- } else {
- renderNode.node.include =
- this.renderHierarchy.isNodeAuxiliary(renderNode)
- ? tf.graph.InclusionType.INCLUDE : tf.graph.InclusionType.EXCLUDE;
- }
-
- // Rebuild the render hierarchy.
- this._buildRenderHierarchy(this.graphHierarchy);
- },
- _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.graph.util.getTracker(this);
- var hierarchyTracker = tf.graph.util.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);
- }.bind(this));
- },
- not: function(x) {
- return !x;
- }
-});
-</script>
-<dom-module id="tf-graph-icon" assetpath="../tf-graph/">
- <style>
- .faded-rect {
- fill: url("#rectHatch");
- }
-
- .faded-ellipse {
- fill: url("#ellipseHatch");
- }
-
- .faded-rect, .faded-ellipse, .faded-series {
- stroke: var(--tb-graph-faded) !important;
- }
- </style>
- <template>
- <template is="dom-if" if="[[_isType(node, type, 'OP')]]">
- <template is="dom-if" if="[[_isConst(node, const)]]">
- <svg height$="[[height]]" preserveAspectRatio="xMinYMid meet" viewBox="0 0 10 10">
- <circle cx="5" cy="5" r="3" fill$="[[_getFill(_computedFill, 'OP')]]" stroke$="[[_getStroke(_computedFill, 'OP')]]"></circle>
- </svg>
- </template>
- <template is="dom-if" if="[[_isSummary(node, summary)]]">
- <svg width$="[[height]]" height$="[[height]]" viewBox="0 0 12 12">
- <use x="0" y="0" xlink:href="#summary-icon"></use>
- </svg>
- </template>
- <template is="dom-if" if="[[_isRegularOp(node, const, summary)]]">
- <svg height$="[[height]]" preserveAspectRatio="xMinYMid meet" viewBox="0 0 16 8">
- <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#op-node-stamp" fill$="[[_getFill(_computedFill, 'OP')]]" stroke$="[[_getStroke(_computedFill, 'OP')]]" class$="{{_fadedClass(renderInfo, 'ellipse')}}" x="8" y="4"></use>
- </svg>
- </template>
- </template>
- <template is="dom-if" if="[[_isType(node, type, 'META')]]">
- <svg height$="[[height]]" preserveAspectRatio="xMinYMid meet" viewBox="0 0 37 16">
- <rect x="1" y="1" fill$="[[_getFill(_computedFill, 'META')]]" stroke$="[[_getStroke(_computedFill, 'META')]]" class$="{{_fadedClass(renderInfo, 'rect')}}" stroke-width="2px" height="14" width="35" rx="5" ry="5"></rect>
- </svg>
- </template>
- <template is="dom-if" if="[[_isType(node, type, 'SERIES')]]">
- <template is="dom-if" if="[[_isVertical(node, vertical)]]">
- <svg height$="[[height]]" preserveAspectRatio="xMinYMid meet" viewBox="0 0 16 15">
- <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#op-series-vertical-stamp" fill$="[[_getFill(_computedFill, 'SERIES')]]" stroke$="[[_getStroke(_computedFill, 'SERIES')]]" class$="{{_fadedClass(renderInfo, 'series')}}" x="0" y="2"></use>
- </svg>
- </template>
- <template is="dom-if" if="[[!_isVertical(node, vertical)]]">
- <svg height$="[[height]]" preserveAspectRatio="xMinYMid meet" viewBox="0 0 24 10">
- <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#op-series-horizontal-stamp" fill$="[[_getFill(_computedFill, 'SERIES')]]" stroke$="[[_getStroke(_computedFill, 'SERIES')]]" class$="{{_fadedClass(renderInfo, 'series')}}" x="0" y="1"></use>
- </svg>
- </template>
- </template>
- </template>
-
- <script>
- (function() {
- Polymer({
- is: 'tf-graph-icon',
-
- properties: {
- /**
- * Node to represent with an icon. Optional, but if specified, its
- * properties override those defined in the type, vertical, const and
- * summary properties.
- * @type {tf.graph.Node}
- */
- node: {
- type: Object,
- value: null
- },
-
- /**
- * Render node information associated with this node. Optional. If
- * specified, this is only used when computing the fill of the icon
- * element.
- * @type {tf.graph.render.RenderNodeInfo}
- */
- renderInfo: {
- type: Object,
- value: null
- },
-
- /**
- * String indicating the type of coloring to use for this node, used
- * only for determining the fill.
- */
- colorBy: {
- type: Object,
- value: "structural"
- },
-
- /**
- * Function used by structural coloring algorithm to determine which
- * color to use based on the template ID of the node. Optional.
- */
- templateIndex: {
- type: Function,
- value: null
- },
-
- /** Type of node to draw (ignored if node is set). */
- type: {
- type: String,
- value: null
- },
-
- /** Direction for series (ignored for other types). */
- vertical: {
- type: Boolean,
- value: false
- },
-
- /** Whether the op is Const (ignored for non-ops). */
- const: {
- type: Boolean,
- value: false
- },
-
- /** Whether the op is a Summary (ignored for non-ops). */
- summary: {
- type: Boolean,
- value: false
- },
-
- /**
- * Fill for the icon, optional. If fill is specified and node is not
- * specified, then this value will override the default for the
- * element. However, if node is specified, this value will be ignored.
- */
- fill: {
- type: String,
- value: null
- },
-
- /** Height of the SVG element in pixels, used for scaling. */
- height: {
- type: Number,
- value: 20
- },
-
- /** The computed fill for the node. **/
- _computedFill: {
- type: String,
- computed:
- "_getComputedFill(node, renderInfo, colorBy, templateIndex, fill)"
- }
-
- },
-
- /**
- * Get the computed fill value for the element.
- */
- _getComputedFill: function(inputNode, inputRenderInfo, inputColorBy,
- inputTemplateIndex, inputFill) {
- if (inputNode && inputRenderInfo &&
- inputColorBy && inputTemplateIndex) {
- var ns = tf.graph.scene.node;
- var colorBy = ns.ColorBy[inputColorBy.toUpperCase()];
- return ns.getFillForNode(inputTemplateIndex, colorBy,
- inputRenderInfo, false);
- }
- return inputFill;
- },
-
- /**
- * Get the fill value for the element, or if that's not possible, return
- * the default fill value for the node type.
- */
- _getFill: function(inputComputedFill, inputNodeType) {
- return inputComputedFill || ({
- OP: tf.graph.render.OpNodeColors.DEFAULT_FILL,
- META: tf.graph.render.MetanodeColors.DEFAULT_FILL,
- SERIES: tf.graph.render.SeriesNodeColors.DEFAULT_FILL
- })[inputNodeType];
- },
-
- /**
- * Get the stroke value for the element, or if that's not possible,
- * return the default stroke value for the node type.
- */
- _getStroke: function(inputComputedFill, inputNodeType) {
- return inputComputedFill ?
- tf.graph.scene.node.getStrokeForFill(inputComputedFill) :
- ({
- OP: tf.graph.render.OpNodeColors.DEFAULT_STROKE,
- META: tf.graph.render.MetanodeColors.DEFAULT_STROKE,
- SERIES: tf.graph.render.SeriesNodeColors.DEFAULT_STROKE
- })[inputNodeType];
- },
-
- /**
- * Test whether the specified node's type, or the literal type string,
- * match a particular other type.
- */
- _isType: function(inputNode, inputType, targetType) {
- if (inputNode) {
- return tf.graph.NodeType[inputNode.type] === targetType;
- }
- return inputType === targetType;
- },
-
- /**
- * Test whether the specified node should be represented as a vertical
- * series. Defaults to the value of the vertical property if node is
- * not specified.
- */
- _isVertical: function(inputNode, inputVertical) {
- if (inputNode) {
- return inputNode.hasNonControlEdges;
- }
- return !!inputVertical;
- },
-
- /**
- * Test whether the specified node is a constant. Defaults to the value
- * of the const property if node is not specified.
- */
- _isConst: function(inputNode, inputConst) {
- if (inputNode) {
- return inputNode.op === 'Const';
- }
- return !!inputConst;
- },
-
- /**
- * Test whether the specified node is a summary. Defaults to the value
- * of the summary property if node is not specified.
- */
- _isSummary: function(inputNode, inputSummary) {
- if (inputNode) {
- return this._isType(inputNode, null, 'OP') &&
- inputNode.op.substr(-7) === 'Summary';
- }
- return !!inputSummary;
- },
-
- /**
- * Test whether the op node is a regular non-summary non-const node.
- */
- _isRegularOp: function(inputNode, inputConst, inputSummary) {
- return !this._isConst(inputNode, inputConst) &&
- !this._isSummary(inputNode, inputSummary);
- },
-
- _fadedClass: function(itemRenderInfo, shape) {
- return itemRenderInfo && itemRenderInfo.isFadedOut ? 'faded-' + shape : '';
- }
- });
- })();
- </script>
-</dom-module>
-<dom-module id="tf-node-list-item" assetpath="../tf-graph-info/">
- <style>
- #list-item {
- width: 100%;
- color: #565656;
- font-size: 11pt;
- font-weight: 400;
- position: relative;
- display: inline-block;
- }
-
- #list-item:hover {
- background-color: var(--google-yellow-100);
- }
-
- .clickable {
- cursor: pointer;
- }
-
- #list-item span {
- margin-left: 40px;
- }
-
- #list-item.excluded span {
- color: #999;
- }
-
- #list-item span.edge-label {
- float: right;
- font-size: 10px;
- margin-left: 3px;
- margin-right: 5px;
- }
-
- .node-icon {
- position: absolute;
- top: 1px;
- left: 2px;
- }
-
- .faded span {
- color: var(--tb-graph-faded);
- }
- </style>
- <template>
- <div id="list-item" on-mouseover="_nodeListener" on-mouseout="_nodeListener" on-click="_nodeListener">
- <div class$="{{_fadedClass(itemRenderInfo)}}">
- <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>
- </div>
- </template>
-
- <script>
- (function() {
- Polymer({
- is: 'tf-node-list-item',
-
- properties: {
- /**
- * The Node for the card itself, on which this item is being drawn.
- * @type {tf.graph.Node}
- */
- cardNode: Object,
- /**
- * The Node for the item within the card, somehow related to cardNode.
- * @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.
- */
- itemRenderInfo: Object,
- name: String,
- itemType: {
- type: String,
- observer: '_itemTypeChanged'
- },
- colorBy: String,
- colorByParams: Object,
- templateIndex: Function
- },
-
- _itemTypeChanged: function() {
- if (this.itemType !== 'subnode') {
- this.$['list-item'].classList.add('clickable');
- } else {
- this.$['list-item'].classList.remove('clickable');
- }
- },
-
- _nodeListener: function(event) {
- // fire node.click/mouseover/mouseout
- this.fire('node-list-item-' + event.type, {
- cardNode: this.cardNode.name,
- nodeName: this.name,
- type: this.itemType
- });
- },
-
- _fadedClass: function(itemRenderInfo) {
- return itemRenderInfo && itemRenderInfo.isFadedOut ? 'faded' : '';
- }
- });
- })();
- </script>
-</dom-module>
-<link rel="import" href="../iron-list/iron-list.html">
-<link rel="import" href="../paper-item/all-imports.html">
-<dom-module id="tf-node-info" assetpath="../tf-graph-info/">
- <style>
- .sub-list-group {
- font-weight: 500;
- font-size: 12pt;
- padding-bottom: 8px;
- width: 100%;
- }
-
- .sub-list {
- max-height: 300px;
- overflow-y: scroll;
- }
-
- .attr-left {
- float: left;
- width: 30%;
- word-wrap: break-word;
- color: #565656;
- font-size: 11pt;
- font-weight: 400;
- }
-
- .attr-right {
- margin-left: 30%;
- word-wrap: break-word;
- color: #565656;
- font-weight: 400;
- }
-
- .sub-list-table {
- display: table;
- width: 100%;
- }
-
- .sub-list-table-row {
- display: table-row;
- }
-
- .sub-list-table-row .sub-list-table-cell:last-child {
- text-align: right;
- }
-
- .sub-list-table-cell {
- color: #565656;
- display: table-cell;
- font-size: 11pt;
- font-weight: 400;
- max-width: 200px;
- padding: 0 4px;
- }
-
- paper-item {
- padding: 0;
- background: #e9e9e9;
- }
-
- paper-item-body[two-line] {
- min-height: 0;
- padding: 8px 12px 4px;
- }
-
- .expandedInfo {
- padding: 8px 12px;
- }
-
- .controlDeps {
- padding: 0 0 0 8px;
- }
-
- .node-name {
- white-space: normal;
- word-wrap: break-word;
- font-size: 14pt;
- font-weight: 500;
- }
-
- .node-icon {
- float: right;
- }
-
- .subtitle {
- font-size: 12pt;
- color: #5e5e5e;
- }
-
- .controlLine {
- font-size: 11pt;
- font-weight: 400;
- }
-
- .toggle-button {
- float: right;
- max-height: 20px;
- max-width: 20px;
- padding: 0;
- }
-
- .control-toggle-button {
- float: left;
- max-height: 20px;
- max-width: 20px;
- padding: 0;
- }
-
- .toggle-include-group {
- padding-top: 4px;
- }
-
- .toggle-include {
- margin: 5px 6px;
- text-transform: none;
- padding: 4px 6px;
- font-size: 10pt;
- background-color: #fafafa;
- color: #666;
- }
-
- .toggle-include:hover {
- background-color: var(--google-yellow-100);
- }
-
- .non-control-list-item {
- padding-left: 10px;
- }
- </style>
- <template>
- <paper-item>
- <paper-item-body two-line="">
- <div>
- <paper-icon-button icon="{{_getToggleIcon(_expanded)}}" on-click="_toggleExpanded" class="toggle-button">
- </paper-icon-button>
- <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>
- <template is="dom-if" if="{{_node.op}}">
- <div class="subtitle">
- Operation:
- <span>[[_node.op]]</span>
- </div>
- </template>
- <template is="dom-if" if="{{_node.metagraph}}">
- <div class="subtitle">
- Subgraph:
- <span>[[_node.cardinality]]</span> nodes
- </div>
- </template>
- </div>
- </paper-item-body>
- </paper-item>
- <iron-collapse opened="{{_expanded}}">
- <template is="dom-if" if="{{_expanded}}" restamp="true">
- <div class="expandedInfo">
- <div class="sub-list-group attributes">
- Attributes
- (<span>[[_attributes.length]]</span>)
- <iron-list class="sub-list" id="attributesList" items="[[_attributes]]">
- <template>
- <div>
- <div class="attr-left">[[item.key]]</div>
- <div class="attr-right">[[item.value]]</div>
- </div>
- </template>
- </iron-list>
- </div>
-
- <template is="dom-if" if="{{_device}}">
- <div class="sub-list-group device">
- <div class="attr-left">Device</div>
- <div class="attr-right">[[_device]]</div>
- </div>
- </template>
-
- <div class="sub-list-group predecessors">
- Inputs
- (<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="[[item.node]]" edge-label="[[item.edgeLabel]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
- </tf-node-list-item>
- </template>
- </iron-list>
- <template is="dom-if" if="[[_predecessors.control.length]]">
- <div class="controlDeps">
- <div class="controlLine">
- <paper-icon-button icon="{{_getToggleIcon(_openedControlPred)}}" on-click="_toggleControlPred" class="control-toggle-button">
- </paper-icon-button>
- Control dependencies
- </div>
- <iron-collapse opened="{{_openedControlPred}}" no-animation="">
- <template is="dom-if" if="{{_openedControlPred}}" restamp="true">
- <iron-list class="sub-list" items="[[_predecessors.control]]">
- <template>
- <tf-node-list-item card-node="[[_node]]" item-node="[[item.node]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
- </tf-node-list-item>
- </template>
- </iron-list>
- </template>
- </iron-collapse>
- </div>
- </template>
- </div>
-
- <div class="sub-list-group successors">
- Outputs
- (<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="[[item.node]]" edge-label="[[item.edgeLabel]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="successor" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
- </tf-node-list-item>
- </template>
- </iron-list>
- <template is="dom-if" if="[[_successors.control.length]]">
- <div class="controlDeps">
- <div class="controlLine">
- <paper-icon-button icon="{{_getToggleIcon(_openedControlSucc)}}" on-click="_toggleControlSucc" class="control-toggle-button">
- </paper-icon-button>
- Control dependencies
- </div>
- <iron-collapse opened="{{_openedControlSucc}}" no-animation="">
- <template is="dom-if" if="{{_openedControlSucc}}" restamp="true">
- <iron-list class="sub-list" items="[[_successors.control]]">
- <template>
- <tf-node-list-item card-node="[[_node]]" item-node="[[item.node]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="successors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
- </tf-node-list-item>
- </template>
- </iron-list>
- </template>
- </iron-collapse>
- </div>
- </template>
- </div>
- <template is="dom-if" if="{{_hasDisplayableNodeStats}}">
- <div class="sub-list-group node-stats">
- Node Stats
- <div class="sub-list-table">
- <template is="dom-if" if="{{_nodeStats.totalBytes}}">
- <div class="sub-list-table-row">
- <div class="sub-list-table-cell">Memory</div>
- <div class="sub-list-table-cell">[[_nodeStatsFormattedBytes]]</div>
- </div>
- </template>
- <template is="dom-if" if="{{_nodeStats.totalMicros}}">
- <div class="sub-list-table-row">
- <div class="sub-list-table-cell">Compute Time</div>
- <div class="sub-list-table-cell">[[_nodeStatsFormattedComputeTime]]</div>
- </div>
- </template>
- <template is="dom-if" if="{{_nodeStats.outputSize}}">
- <div class="sub-list-table-row">
- <div class="sub-list-table-cell">Tensor Output Sizes</div>
- <div class="sub-list-table-cell">
- <template is="dom-repeat" items="{{_nodeStatsFormattedOutputSizes}}">
- [[item]] <br>
- </template>
- </div>
- </div>
- </template>
- </div>
- </div>
- </template>
- <div class="toggle-include-group">
- <paper-button raised="" class="toggle-include" on-click="_toggleInclude">
- <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>
- </template>
-
- <script>
- (function() {
- Polymer({
- is: 'tf-node-info',
-
- properties: {
- nodeName: String,
- graphHierarchy: Object,
- renderHierarchy: Object,
- /** What to color the nodes by (compute time, memory, device etc.) */
- colorBy: String,
- _templateIndex: {
- type: Function,
- computed: '_getTemplateIndex(graphHierarchy)'
- },
- _node: {
- type: Object,
- computed: '_getNode(nodeName, graphHierarchy)',
- observer: '_resetState'
- },
- _nodeStats: {
- type: Object,
- computed: '_getNodeStats(nodeName, graphHierarchy)',
- observer: '_resetState'
- },
- _hasDisplayableNodeStats: {
- type: Object,
- computed: '_getHasDisplayableNodeStats(_nodeStats)',
- },
- _nodeStatsFormattedBytes: {
- type: String,
- computed: '_getNodeStatsFormattedBytes(_nodeStats)',
- },
- _nodeStatsFormattedComputeTime: {
- type: String,
- computed: '_getNodeStatsFormattedComputeTime(_nodeStats)',
- },
- _nodeStatsFormattedOutputSizes: {
- type: Array,
- computed: '_getNodeStatsFormattedOutputSizes(_nodeStats)',
- },
- // The enum value of the include property of the selected node.
- nodeInclude: {
- type: Number,
- observer: '_nodeIncludeStateChanged'
- },
- _attributes: {
- type: Array,
- computed: '_getAttributes(_node)'
- },
- _device: {
- type: String,
- computed: '_getDevice(_node)'
- },
- _successors: {
- type: Object,
- computed: '_getSuccessors(_node, graphHierarchy)'
- },
- _predecessors: {
- type: Object,
- computed: '_getPredecessors(_node, graphHierarchy)'
- },
- _subnodes: {
- type: Array,
- computed: '_getSubnodes(_node)'
- },
- _expanded: {
- type: Boolean,
- value: true
- },
- _totalPredecessors: {
- type: Number,
- computed: '_getTotalPred(_predecessors)'
- },
- _totalSuccessors: {
- type: Number,
- computed: '_getTotalSucc(_successors)'
- },
- _openedControlPred: {
- type: Boolean,
- value: false
- },
- _openedControlSucc: {
- type: Boolean,
- value: false
- },
- _auxButtonText: String,
- _groupButtonText: String
- },
- expandNode: function() {
- this.fire('_node.expand', this.node);
- },
- _getTemplateIndex: function(graphHierarchy) {
- return graphHierarchy.getTemplateIndex();
- },
- _getNode: function(nodeName, graphHierarchy) {
- return graphHierarchy.node(nodeName);
- },
- _getNodeStats: function(nodeName, graphHierarchy) {
- var node = this._getNode(nodeName, graphHierarchy);
- if (node) {
- return node.stats;
- }
- return null;
- },
- _getHasDisplayableNodeStats: function(stats) {
- return tf.graph.util.hasDisplayableNodeStats(stats);
- },
- _getNodeStatsFormattedBytes: function(stats) {
- if (!stats || !stats.totalBytes) {
- return;
- }
-
- return tf.graph.util.convertUnitsToHumanReadable(
- stats.totalBytes, tf.graph.util.MEMORY_UNITS);
- },
- _getNodeStatsFormattedComputeTime: function(stats) {
- if (!stats || !stats.totalMicros) {
- return;
- }
-
- return tf.graph.util.convertUnitsToHumanReadable(
- stats.totalMicros, tf.graph.util.TIME_UNITS);
- },
- _getNodeStatsFormattedOutputSizes: function(stats) {
- if (!stats || !stats.outputSize || !stats.outputSize.length) {
- return;
- }
-
- return _.map(stats.outputSize, function(shape) {
- if (shape.length === 0) {
- return "scalar";
- }
- return "[" + shape.join(", ") + "]";
- });
- },
- _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, '<wbr>/');
- },
- _getRenderInfo: function(nodeName, renderHierarchy) {
- return this.renderHierarchy.getOrCreateRenderNodeByName(nodeName);
- },
- _getAttributes: function(node) {
- this.async(this._resizeList.bind(this, "#attributesList"));
- 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;
- },
- _getSuccessors: function(node, hierarchy) {
- this.async(this._resizeList.bind(this, "#inputsList"));
- if (!node) {
- return {regular: [], control: []}
- }
- return this._convertEdgeListToEdgeInfoList(
- hierarchy.getSuccessors(node.name), false, node.isGroupNode);
- },
- _getPredecessors: function(node, hierarchy) {
- this.async(this._resizeList.bind(this, "#outputsList"));
- if (!node) {
- return {regular: [], control: []}
- }
- return this._convertEdgeListToEdgeInfoList(
- hierarchy.getPredecessors(node.name), true, node.isGroupNode);
- },
- _convertEdgeListToEdgeInfoList: function(list, isPredecessor, isGroupNode) {
-
- /**
- * Unpacks the metaedge into a list of base edge information
- * that can be rendered.
- */
- var unpackMetaedge = function(metaedge) {
- return _.map(metaedge.baseEdgeList, function(baseEdge) {
- name = isPredecessor ? baseEdge.v : baseEdge.w;
- return {
- name: name,
- node: this._getNode(name, this.graphHierarchy),
- edgeLabel: tf.graph.scene.edge.getLabelForBaseEdge(baseEdge,
- this.renderHierarchy),
- renderInfo: this._getRenderInfo(name, this.renderHierarchy)
- };
- }, this);
- }.bind(this);
-
- /**
- * Converts a list of metaedges to a list of edge information
- * that can be rendered.
- */
- var toEdgeInfoList = function(edges) {
- var edgeInfoList = [];
- _.each(edges, function(metaedge) {
- var name = isPredecessor ? metaedge.v : metaedge.w;
- // Enumerate all the base edges if the node is an OpNode, or the
- // metaedge has only 1 edge in it.
- if (!isGroupNode || metaedge.baseEdgeList.length == 1) {
- edgeInfoList = edgeInfoList.concat(unpackMetaedge(metaedge));
- } else {
- edgeInfoList.push({
- name: name,
- node: this._getNode(name, this.graphHierarchy),
- edgeLabel: tf.graph.scene.edge.getLabelForEdge(metaedge,
- this.renderHierarchy),
- renderInfo: this._getRenderInfo(name, this.renderHierarchy)
- });
- }
- }, this);
- return edgeInfoList;
- }.bind(this);
-
- return {
- regular: toEdgeInfoList(list.regular),
- control: toEdgeInfoList(list.control)
- };
- },
- _getSubnodes: function(node) {
- return node && node.metagraph ? node.metagraph.nodes() : null;
- },
- _getTotalPred: function(predecessors) {
- return predecessors.regular.length + predecessors.control.length;
- },
- _getTotalSucc: function(successors) {
- return successors.regular.length + successors.control.length;
- },
- _toggleControlPred: function() {
- this._openedControlPred = !this._openedControlPred;
- },
- _toggleControlSucc: function() {
- this._openedControlSucc = !this._openedControlSucc;
- },
- _toggleExpanded: function() {
- this._expanded = !this._expanded;
- },
- _getToggleIcon: function(expanded) {
- return expanded ? "expand-less" : "expand-more";
- },
- _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);
- if (list) {
- list.fire('iron-resize');
- }
- },
- _toggleInclude: function() {
- var graphElem = document.querySelector("#graph");
- graphElem.fire("node-toggle-extract", { name: this.nodeName });
- var graphBoardElem = document.querySelector("#graphboard");
- graphBoardElem.fire("node-toggle-extract");
- },
- _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);
- }
- });
- })();
- </script>
-</dom-module>
-<dom-module id="tf-graph-info" assetpath="../tf-graph-info/">
-<template>
-<style>
-:host {
- font-size: 12px;
- margin: 0;
- padding: 0;
- display: block;
-}
-
-h2 {
- padding: 0;
- text-align: center;
- margin: 0;
-}
-
-.health-pill-legend {
- padding: 15px;
-}
-
-.health-pill-legend h2 {
- text-align: left;
-}
-
-.health-pill-entry {
- margin: 10px 10px 10px 0;
-}
-
-.health-pill-entry .color-preview {
- width: 26px;
- height: 26px;
- border-radius: 3px;
- display: inline-block;
- margin: 0 10px 0 0;
-}
-
-.health-pill-entry .color-label, .health-pill-entry .tensor-count {
- color: #777;
- display: inline-block;
- height: 26px;
- font-size: 22px;
- line-height: 26px;
- vertical-align: top;
-}
-
-.health-pill-entry .tensor-count {
- float: right;
-}
-
-#health-pill-step-slider {
- width: 100%;
- margin: 0 0 0 -15px;
- /* 31 comes from adding a padding of 15px from both sides of the paper-slider, subtracting
- * 1px so that the slider width aligns with the image (the last slider marker takes up 1px),
- * and adding 2px to account for a border of 1px on both sides of the image. 30 - 1 + 2.
- * Apparently, the paper-slider lacks a mixin for those padding values. */
- width: calc(100% + 31px);
-}
-
-#health-pills-loading-spinner {
- width: 20px;
- height: 20px;
- vertical-align: top;
-}
-
-#health-pill-step-number-input {
- text-align: center;
- vertical-align: top;
-}
-</style>
-<template is="dom-if" if="{{selectedNode}}">
- <paper-material elevation="1" class="card">
- <tf-node-info graph-hierarchy="[[graphHierarchy]]" render-hierarchy="[[renderHierarchy]]" flat-graph="[[graph]]" node-name="[[selectedNode]]" node-include="[[selectedNodeInclude]]" highlighted-node="{{highlightedNode}}" color-by="[[colorBy]]">
- </tf-node-info>
- </paper-material>
-</template>
-<template is="dom-if" if="[[_healthPillsAvailable(debuggerDataEnabled, nodeNamesToHealthPills)]]">
- <paper-material elevation="1" class="card health-pill-legend">
- <div class="title">
- Enable all (not just sampled) steps. Requires slow disk read.
- </div>
- <paper-toggle-button id="enableAllStepsModeToggle" checked="{{allStepsModeEnabled}}">
- </paper-toggle-button>
- <h2>
- Step of Health Pills:
- <template is="dom-if" if="[[allStepsModeEnabled]]">
- <input type="number" id="health-pill-step-number-input" min="0" max="[[_biggestStepEverSeen]]" value="{{specificHealthPillStep::input}}">
- </template>
- <template is="dom-if" if="[[!allStepsModeEnabled]]">
- [[_currentStepDisplayValue]]
- </template>
-
- <paper-spinner-lite active="" hidden$="[[!areHealthPillsLoading]]" id="health-pills-loading-spinner"></paper-spinner-lite>
- </h2>
- <template is="dom-if" if="[[allStepsModeEnabled]]">
- <paper-slider id="health-pill-step-slider" immediate-value="{{specificHealthPillStep}}" max="[[_biggestStepEverSeen]]" snaps="" step="1" value="{{specificHealthPillStep}}"></paper-slider>
- </template>
- <template is="dom-if" if="[[!allStepsModeEnabled]]">
- <template is="dom-if" if="[[_maxStepIndex]]">
- <paper-slider id="health-pill-step-slider" immediate-value="{{healthPillStepIndex}}" max="[[_maxStepIndex]]" snaps="" step="1" value="{{healthPillStepIndex}}"></paper-slider>
- </template>
- </template>
- <h2>
- Health Pill
- <template is="dom-if" if="[[healthPillValuesForSelectedNode]]">
- Counts for Selected Node
- </template>
- <template is="dom-if" if="[[!healthPillValuesForSelectedNode]]">
- Legend
- </template>
- </h2>
- <template is="dom-repeat" items="[[healthPillEntries]]">
- <div class="health-pill-entry">
- <div class="color-preview" style="background:[[item.background_color]]"></div>
- <div class="color-label">[[item.label]]</div>
- <div class="tensor-count">
- [[_computeTensorCountString(healthPillValuesForSelectedNode, index)]]
- </div>
- </div>
- </template>
- </paper-material>
-</template>
-</template>
-<script>
-(function() {
- Polymer({
- is: 'tf-graph-info',
-
- properties: {
- title: String,
- graphHierarchy: Object,
- graph: Object,
- renderHierarchy: Object,
- nodeNamesToHealthPills: Object,
- healthPillStepIndex: {
- type: Number,
- notify: true,
- },
- // Only relevant if we are in all steps mode, in which case the user may want to view health
- // pills for a specific step.
- specificHealthPillStep: {
- type: Number,
- value: 0,
- notify: true,
- },
- colorBy: String,
- // Two-ways
- selectedNode: {
- type: String,
- notify: true
- },
- highlightedNode: {
- type: String,
- notify: true
- },
- // The enum value of the include property of the selected node.
- selectedNodeInclude: {
- type: Number,
- notify: true
- },
- // Whether debugger data is enabled for this instance of Tensorboard.
- debuggerDataEnabled: Boolean,
- // Whether health pills are currently being loaded, in which case we show a spinner (and the
- // current health pills shown might be out of date).
- areHealthPillsLoading: Boolean,
- healthPillEntries: {
- type: Array,
- value: tf.graph.scene.healthPillEntries,
- readOnly: true,
- },
- healthPillValuesForSelectedNode: {
- type: Array,
- computed: '_computeHealthPillForNode(nodeNamesToHealthPills, healthPillStepIndex, selectedNode, allStepsModeEnabled, areHealthPillsLoading)',
- },
- // When all-steps mode is enabled, the user can request health pills for any step. In this
- // mode, Tensorboard makes a request every time the user drags the slider to a different step.
- allStepsModeEnabled: {
- type: Boolean,
- notify: true,
- },
- // The biggest step value ever seen. Used to determine what steps of health pills to let the
- // user fetch in all steps mode.
- _biggestStepEverSeen: {
- type: Number,
- computed: '_computeBiggestStepEverSeen(nodeNamesToHealthPills)',
- },
- _maxStepIndex: {
- type: Number,
- computed: '_computeMaxStepIndex(nodeNamesToHealthPills)',
- },
- _currentStepDisplayValue: {
- type: String,
- computed: '_computeCurrentStepDisplayValue(nodeNamesToHealthPills, healthPillStepIndex, allStepsModeEnabled, specificHealthPillStep, areHealthPillsLoading)',
- },
- },
- listeners: {
- 'node-list-item-click': '_nodeListItemClicked',
- 'node-list-item-mouseover': '_nodeListItemMouseover',
- 'node-list-item-mouseout': '_nodeListItemMouseout'
- },
- _nodeListItemClicked: function(event) {
- this.selectedNode = event.detail.nodeName;
- },
- _nodeListItemMouseover: function(event) {
- this.highlightedNode = event.detail.nodeName;
- },
- _nodeListItemMouseout: function() {
- this.highlightedNode = null;
- },
- _healthPillsAvailable: function(debuggerDataEnabled, nodeNamesToHealthPills) {
- // So long as there is a mapping (even if empty) from node name to health pills, show the
- // legend and slider. We do that because, even if no health pills exist at the current step,
- // the user may desire to change steps, and the slider must show for the user to do that.
- return debuggerDataEnabled && nodeNamesToHealthPills;
- },
- _computeTensorCountString: function(healthPillValuesForSelectedNode, valueIndex) {
- if (!healthPillValuesForSelectedNode) {
- // No health pill data is available.
- return '';
- }
-
- return healthPillValuesForSelectedNode[valueIndex].toFixed(0);
- },
- _computeHealthPillForNode: function(
- nodeNamesToHealthPills, healthPillStepIndex, selectedNode, allStepsModeEnabled, areHealthPillsLoading) {
- if (areHealthPillsLoading) {
- // Health pills are loading. Do not render data that is out of date.
- return null;
- }
-
- if (!selectedNode) {
- // No node is selected.
- return null;
- }
-
- const healthPills = nodeNamesToHealthPills[selectedNode];
- if (!healthPills) {
- // This node lacks a health pill.
- return null;
- }
-
- // If all steps mode is enabled, we use the first health pill in the list because the JSON
- // response from the server is a mapping between node name and a list of 1 health pill.
- const healthPill = healthPills[allStepsModeEnabled ? 0 : healthPillStepIndex];
- if (!healthPill) {
- // This node lacks a health pill at the current step.
- return null;
- }
-
- // The health pill count values start at 2. Each health pill contains 6 values.
- return healthPill.value.slice(2, 8);
- },
- _computeCurrentStepDisplayValue: function(
- nodeNamesToHealthPills,
- healthPillStepIndex,
- allStepsModeEnabled,
- specificHealthPillStep,
- areHealthPillsLoading) {
- if (allStepsModeEnabled) {
- // The user seeks health pills for specific step from the server.
- return specificHealthPillStep.toFixed(0);
- }
-
- if (areHealthPillsLoading) {
- // The current step is undefined.
- return 0;
- }
-
- for (let nodeName in nodeNamesToHealthPills) {
- // All nodes have the same number of steps stored, so only examine 1 node. We cannot
- // directly index into the nodeNamesToHealthPills object because we do not have a key.
- // If all steps mode is enabled, we only have 1 step to show.
- return nodeNamesToHealthPills[nodeName][healthPillStepIndex].step.toFixed(0);
- }
-
- // The current step could not be computed.
- return 0;
- },
- _computeBiggestStepEverSeen: function(nodeNamesToHealthPills) {
- for (let nodeName in nodeNamesToHealthPills) {
- // All nodes have the same number of steps stored, so only examine 1 node.
- // The index is 1 less than the count. Tensorboard backend logic guarantees that the length
- // of the array will be greater than 1.
- var healthPills = nodeNamesToHealthPills[nodeName];
- return Math.max(this._biggestStepEverSeen, healthPills[healthPills.length - 1].step);
- }
-
- // No steps seen so far. Default to 0.
- return this._biggestStepEverSeen || 0;
- },
- _computeMaxStepIndex: function(nodeNamesToHealthPills) {
- for (let nodeName in nodeNamesToHealthPills) {
- // All nodes have the same number of steps stored, so only examine 1 node.
- // The index is 1 less than the count. Tensorboard backend logic guarantees that the length
- // of the array will be greater than 1.
- return nodeNamesToHealthPills[nodeName].length - 1;
- }
-
- // Return a falsy value. The slider should be hidden.
- return 0;
- },
- });
-})();
-</script>
-</dom-module>
-<link rel="import" href="../paper-progress/paper-progress.html">
-
-
-<dom-module id="tf-graph-board" assetpath="../tf-graph-board/">
-<template>
-<style>
-::host {
- display: block;
-}
-
-/deep/ .close {
- position: absolute;
- cursor: pointer;
- left: 15px;
- bottom: 15px;
-}
-
-.container {
- width: 100%;
- height: 100%;
- opacity: 1;
-}
-
-.container.loading {
- cursor: progress;
- opacity: 0.1;
-}
-
-.container.loading.error {
- cursor: auto;
-}
-
-#info {
- position: absolute;
- right: 5px;
- top: 5px;
- padding: 0px;
- max-width: 380px;
- min-width: 320px;
- background-color: rgba(255,255,255,0.9);
- @apply(--shadow-elevation-2dp);
-}
-
-#main {
- width: 100%;
- height: 100%;
-}
-
-#progress-bar {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 100%;
- position: absolute;
- top: 40px;
- left: 0;
- font-size: 13px;
-}
-
-#progress-msg {
- width: 400px;
- margin-bottom: 5px;
-}
-
-paper-progress {
- width: 400px;
- --paper-progress-height: 6px;
- --paper-progress-active-color: #f3913e;
-}
-
-.context-menu {
- position: absolute;
- display: none;
- background-color: #e2e2e2;
- border-radius: 2px;
- font-size: 14px;
- min-width: 150px;
- border: 1px solid #d4d4d4;
-}
-
-/deep/ .context-menu ul {
- list-style-type: none;
- margin: 0;
- padding: 0;
- cursor: default;
-}
-
-/deep/ .context-menu ul li {
- padding: 4px 16px;
-}
-
-/deep/ .context-menu ul li:hover {
- background-color: #f3913e;
- color: white;
-}
-</style>
-<template is="dom-if" if="[[_isNotComplete(progress)]]">
- <div id="progress-bar">
- <div id="progress-msg">[[progress.msg]]</div>
- <paper-progress value="[[progress.value]]"></paper-progress>
- </div>
-</template>
-<div class$="[[_getContainerClass(progress)]]">
- <div id="main">
- <tf-graph id="graph" graph-hierarchy="{{graphHierarchy}}" basic-graph="[[graph]]" hierarchy-params="[[hierarchyParams]]" render-hierarchy="{{renderHierarchy}}" devices-for-stats="[[devicesForStats]]" stats="[[stats]]" selected-node="{{_selectedNode}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="{{colorByParams}}" progress="{{progress}}" node-names-to-health-pills="[[nodeNamesToHealthPills]]" health-pill-step-index="[[healthPillStepIndex]]"></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]]" debugger-data-enabled="[[debuggerDataEnabled]]" are-health-pills-loading="[[areHealthPillsLoading]]" node-names-to-health-pills="[[nodeNamesToHealthPills]]" all-steps-mode-enabled="{{allStepsModeEnabled}}" specific-health-pill-step="{{specificHealthPillStep}}" health-pill-step-index="{{healthPillStepIndex}}"></tf-graph-info>
- </div>
- <div class="context-menu"></div>
-</div>
-</template>
-</dom-module>
-
-<script>
-Polymer({
- is: 'tf-graph-board',
- properties: {
- // Public API.
- graphHierarchy: Object,
- graph: Object,
- stats: Object,
- /**
- * @type {value: number, msg: string}
- *
- * A number between 0 and 100 denoting the % of progress
- * for the progress bar and the displayed message.
- */
- progress: Object,
- colorBy: String,
- colorByParams: {
- type: Object,
- notify: true
- },
- renderHierarchy: {
- type: Object,
- notify: true
- },
- // Whether debugger data is enabled for this instance of Tensorboard.
- debuggerDataEnabled: Boolean,
- // Whether health pills are currently being loaded.
- areHealthPillsLoading: Boolean,
- // A mapping between node name to the tf.graph.scene.HealthPill to render.
- nodeNamesToHealthPills: Object,
- // Whether the user can request health pills for individual steps from the server. This can be
- // slow compared the default of showing sampled health pills.
- allStepsModeEnabled: {
- type: Boolean,
- notify: true,
- value: false,
- },
- // Relevant if allStepsModeEnabled. The specific step for which to fetch health pills from the
- // server for.
- specificHealthPillStep: {
- type: Number,
- notify: true,
- value: 0,
- },
- // The step of health pills to show throughout the graph.
- healthPillStepIndex: Number,
- // Private API: Data routing between child components.
- _selectedNode: String,
- // The enum value of the include property of the selected node.
- _selectedNodeInclude: Number,
- _highlightedNode: String
- },
- listeners: {
- 'node-toggle-extract': '_nodeToggleExtract'
- },
- observers: [
- '_updateNodeInclude(_selectedNode)'
- ],
- /** True if the progress is not complete yet (< 100 %). */
- _isNotComplete: function(progress) {
- return progress.value < 100;
- },
- _getContainerClass: function(progress) {
- var result = 'container';
- if (progress.error) {
- result += ' error';
- }
- if (this._isNotComplete(progress)) {
- result += ' loading';
- }
- return result;
- },
- _updateNodeInclude: function(nodeName) {
- var node = this.graphHierarchy.node(nodeName);
- this.set("_selectedNodeInclude",
- node ? node.include : tf.graph.InclusionType.UNSPECIFIED);
- },
- _nodeToggleExtract: function() {
- this._updateNodeInclude(this._selectedNode);
- }
-});
-</script>
-<link rel="import" href="../paper-radio-group/paper-radio-group.html">
-<link rel="import" href="../paper-tooltip/paper-tooltip.html">
-<dom-module id="tf-graph-controls" assetpath="../tf-graph/">
-<template>
-<style>
-:host {
- font-size: 12px;
- color: gray;
- --paper-font-subhead: {
- font-size: 14px;
- color: gray;
- };
- --paper-dropdown-menu-icon: {
- width: 15px;
- height: 15px;
- };
- --paper-dropdown-menu-button: {
- padding: 0;
- };
- --paper-dropdown-menu-input: {
- padding: 0;
- };
- --paper-item-min-height: 30px;
-}
-
-paper-button[raised].keyboard-focus {
- font-weight: normal;
-}
-
-.run-dropdown {
- --paper-input-container: {
- padding: 9px 0 0 25px;
- };
-}
-
-.color-dropdown {
- --paper-input-container: {
- padding: 9px 0 0 13px;
- };
-}
-
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-table td {
- padding: 0;
- margin: 0;
-}
-
-.allcontrols {
- width: 188px;
- padding: 0 30px;
-}
-
-.legend-holder {
- position: absolute;
- bottom: 0;
- padding-bottom: 10px;
-}
-
-paper-radio-button {
- display: block;
- padding: 5px;
-}
-svg.icon {
- width: 60px;
- height: 18px;
-}
-.icon ellipse {
- rx: 10px;
- ry: 5px;
- stroke: #CCC;
- stroke-width: 1px;
- fill: #FFFFFF;
- cy: 10px;
-}
-.icon rect {
- height: 14px;
- width: 35px;
- rx: 5px;
- ry: 5px;
- stroke: #CCC;
- stroke-width: 2px;
- fill: #D9D9D9;
-}
-.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: 16px;
- margin: 8px 5px 8px 0;
- color: black;
-}
-.title small {
- font-weight: normal;
-}
-.deviceList, .xlaClusterList {
- max-height: 200px;
- overflow-y: auto;
-}
-
-#file {
- padding: 8px 0;
-}
-
-.color-legend-row {
- clear: both;
- height: 20px;
- margin-top: 5px;
- position: relative;
-}
-
-.color-legend-row svg {
- position: absolute;
- top: -1px;
- width: 40px;
-}
-
-.color-legend-row span.color-legend-value {
- margin-left: 60px;
-}
-
-#grey-rect {
- fill: #eee;
- stroke: #a6a6a6;
-}
-
-#faded-rect {
- fill: url("#rectHatch");
- stroke: var(--tb-graph-faded);
-}
-
-.button-text {
- text-transform: none;
- padding: 8px 18px 0 18px;
- font-size: 14px
-}
-
-.upload-button {
- width: 165px;
- height: 25px;
- text-transform: none;
- margin-top: 4px;
-}
-
-.iconbutton {
- padding: 2px;
- width: 30px;
- height: 30px;
- color: var(--paper-orange-500);
-}
-
-.hidden-input {
- height: 0px;
- width: 0px;
- overflow:hidden;
-}
-
-.allcontrols .control-holder {
- display: flex;
- clear: both;
-}
-
-.allcontrols .control-holder paper-radio-group {
- margin-top: 5px;
-}
-
-span.counter {
- font-size: 13px;
- color: gray;
-}
-
-.runs 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;
-}
-
-/** Override inline styles that suppress pointer events for disabled buttons. Otherwise, the */
-/* tooltips do not appear. */
-#color-by-radio-group paper-radio-button {
- pointer-events: auto !important;
-}
-</style>
-<svg width="0" height="0">
- <defs>
- <g id="legend-rect">
- <rect x="1" y="1" stroke-width="2px" height="14" width="35" rx="5" ry="5"></rect>
- </g>
- <g id="grey-rect">
- <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#legend-rect"></use>
- </g>
- <g id="faded-rect">
- <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#legend-rect"></use>
- </g>
- </defs>
-</svg>
-<div class="allcontrols">
- <div class="control-holder">
- <paper-icon-button icon="aspect-ratio" class="iconbutton" on-click="fit" alt="Fit to screen">
- </paper-icon-button>
- <paper-button class="button-text" on-click="fit">Fit to screen
- </paper-button>
- </div>
- <div class="control-holder">
- <paper-icon-button icon="file-download" class="iconbutton" on-click="download" alt="Download PNG">
- </paper-icon-button>
- <paper-button class="button-text" on-click="download">Download PNG
- </paper-button>
- <a href="#" id="graphdownload" class="title" download="graph.png">
- </a>
- </div>
- <div class="control-holder runs">
- <div class="title">Run <span class="counter">([[datasets.length]])</span></div>
- <paper-dropdown-menu no-label-float="" no-animations="" noink="" class="run-dropdown">
- <paper-menu id="select" class="dropdown-content" selected="{{selectedDataset}}">
- <template is="dom-repeat" items="[[datasets]]">
- <paper-item>[[item.name]]</paper-item>
- </template>
- </paper-menu>
- </paper-dropdown-menu>
- </div>
- <template is="dom-if" if="[[showSessionRunsDropdown]]">
- <div class="control-holder">
- <div class="title">Session runs <span class="counter">([[_numSessionRuns(metadataTags)]])</span></div>
- <paper-dropdown-menu no-label-float="" no-animations="" noink="" class="run-dropdown">
- <paper-menu id="select" class="dropdown-content" selected="{{selectedMetadataTag}}">
- <template is="dom-repeat" items="[[metadataTags]]">
- <paper-item>[[item.tag]]</paper-item>
- </template>
- <paper-item>None</paper-item>
- </paper-menu>
- </paper-dropdown-menu>
- </div>
- </template>
- <template is="dom-if" if="[[showUploadButton]]">
- <div class="control-holder">
- <div class="title">Upload</div>
- <paper-button raised="" class="text-button upload-button" on-click="_getFile">Choose File</paper-button>
- <div class="hidden-input">
- <input type="file" id="file" name="file" on-change="_updateFileInput">
- </div>
- </div>
- </template>
- <table class="control-holder">
- <tbody><tr>
- <td class="title">Trace inputs</td>
- <td class="input-element-table-data">
- <paper-toggle-button id="trace-inputs"></paper-toggle-button>
- </td>
- </tr>
- <template is="dom-if" if="[[healthPillsFeatureEnabled]]">
- <tr>
- <td class="title">Show health pills</td>
- <td class="input-element-table-data">
- <paper-toggle-button checked="{{healthPillsToggledOn}}"></paper-toggle-button>
- </td>
- </tr>
- </template>
- </tbody></table>
- <div class="control-holder">
- <div class="title">Color</div>
- <paper-radio-group id="color-by-radio-group" selected="{{colorBy}}">
- <paper-radio-button name="structure">Structure</paper-radio-button>
-
- <paper-radio-button name="device">Device</paper-radio-button>
-
- <paper-radio-button id="xla-cluster-radio-button" name="xla_cluster" disabled="[[!_xlaClustersProvided(renderHierarchy)]]">
- XLA Cluster
- </paper-radio-button>
- <paper-tooltip for="xla-cluster-radio-button" position="right">
- 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="compute_time" disabled="[[!stats]]">
- Compute time
- </paper-radio-button>
- <paper-tooltip for="compute-time-radio-button" position="right">
- 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="memory" disabled="[[!stats]]">
- Memory
- </paper-radio-button>
- <paper-tooltip for="memory-radio-button" position="right">
- 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-group>
- </div>
- <div>
- <template is="dom-if" if="[[_isGradientColoring(stats, colorBy)]]">
- <svg width="140" height="20" style="margin: 0 5px" class="color-text">
- <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">
- <table>
- <template is="dom-repeat" items="[[_getDevices(devicesForStats)]]">
- <tr>
- <td>
- <input type="checkbox" value$="[[item.device]]" checked$="[[item.used]]" on-click="_deviceCheckboxClicked">
- </td>
- <td>
- <div>
- <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" animation-delay="0">[[item.ignoredMsg]]</paper-tooltip>
- </template>
- </div>
- </td>
- </tr>
- </template>
- </table>
- </div>
- </template>
- <template is="dom-if" if="[[_equals(colorBy, 'structure')]]">
- <div class="color-text">
- <div class="color-legend-row">
- <div style="position: absolute;">
- colors
- </div>
- <span class="color-legend-value">same substructure</span>
- </div>
- <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">unique substructure</span>
- </div>
- </div>
- </template>
- <template is="dom-if" if="[[_equals(colorBy, 'device')]]">
- <div class="color-text">
- <div class="deviceList">
- <table>
- <template is="dom-repeat" items="[[colorByParams.device]]">
- <tr>
- <td style$="[[_getBackgroundColor(item.color)]]">
- <div class="colorBox"></div>
- </td>
- <td>
- <div>[[item.device]]</div>
- </td>
- </tr>
- </template>
- </table>
- </div>
- <br>
- <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 device</span>
- </div>
- </div>
- </template>
- <template is="dom-if" if="[[_equals(colorBy, 'xla_cluster')]]">
- <div class="color-text">
- <div class="xlaClusterList">
- <table>
- <template is="dom-repeat" items="[[colorByParams.xla_cluster]]">
- <tr>
- <td style$="[[_getBackgroundColor(item.color)]]">
- <div class="colorBox"></div>
- </td>
- <td>
- <div>[[item.xla_cluster]]</div>
- </td>
- </tr>
- </template>
- </table>
- </div>
- <br>
- <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="[[_statsNotNull(stats)]]">
- <div class="color-legend-row">
- <svg>
- <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#faded-rect" x="0" y="0"></use>
- </svg>
- <span class="color-legend-value">unused substructure</span>
- </div>
- </template>
- </div>
-
- <template is="dom-if" if="[[!_isGradientColoring(stats, colorBy)]]">
- <div class="legend-holder">
- <table>
- <tbody><tr>
- <td><div class="title">Graph</div></td>
- <td>(* = expandable)</td>
- </tr>
- <tr>
- <td>
- <svg class="icon">
- <rect transform="translate(3, 1)" height="14" width="35" rx="5" ry="5"></rect>
- </svg>
- </td>
- <td>Namespace<span class="gray">*</span></td>
- </tr>
- <tr>
- <td>
- <svg class="icon" preserveAspectRatio="xMinYMid meet" viewBox="0 0 10 10">
- <use xlink:href="#op-node-stamp" fill="white" stroke="#ccc" x="9.5" y="6"></use>
- </svg>
- </td>
- <td>OpNode</td>
- </tr>
- <tr>
- <td>
- <svg class="icon" height="15px" preserveAspectRatio="xMinYMid meet" viewBox="0 0 12 12">
- <use xlink:href="#op-series-horizontal-stamp" fill="white" stroke="#ccc" x="2" y="2"></use>
- </svg>
- </td>
- <td>Unconnected series<span class="gray">*</span></td>
- </tr>
- <tr>
- <td>
- <svg class="icon" height="15px" preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15">
- <use xlink:href="#op-series-vertical-stamp" fill="white" stroke="#ccc" x="2" y="2"></use>
- </svg>
- </td>
- <td>Connected series<span class="gray">*</span></td>
- </tr>
- <tr>
- <td>
- <svg class="icon">
- <circle fill="white" stroke="#848484" cx="10" cy="10" r="5"></circle>
- </svg>
- </td>
- <td>Constant</td>
- </tr>
- <tr>
- <td>
- <svg class="image-icon" viewBox="0 0 12 12" width="24" height="24">
- <use x="0" y="0" class="image-icon" xlink:href="#summary-icon"></use>
- </svg>
- </td>
- <td>Summary</td>
- </tr>
- <tr>
- <td>
- <svg class="icon" height="15px" preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15">
- <defs>
- <marker id="ref-arrowhead-legend" fill="#bbb" markerWidth="10" markerHeight="10" refX="1" refY="5" orient="auto">
- <path d="M 10,0 L 0,5 L 10,10 C 7,7 7,3 10,0"></path>
- </marker>
- </defs>
- <path stroke="#bbb" d="M2 9 l 23 0" stroke-linecap="round"></path>
- </svg>
- </td>
- <td>Dataflow edge</td>
- </tr>
- <tr>
- <td>
- <svg class="icon" height="15px" preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15">
- <path stroke="#bbb" d="M2 9 l 23 0" stroke-linecap="round" stroke-dasharray="2, 2"></path>
- </svg>
- </td>
- <td>Control dependency edge</td>
- </tr>
- <tr>
- <td>
- <svg class="icon" height="15px" preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15">
- <path marker-start="url(#ref-arrowhead-legend)" stroke="#bbb" d="M2 9 l 23 0" stroke-linecap="round"></path>
- </svg>
- </td>
- <td>Reference edge</td>
- </tr>
- </tbody></table>
- </div>
- </template>
- </div>
-</template>
-<script>
-(function() { // Private scope.
-/**
- * Stats from device names that match these regexes will be excluded by default.
- * The user can still turn on a device by selecting the checkbox in the device list.
- * See b/29089982 for context.
- */
-var DEVICE_NAMES_EXCLUDE = [
- {
- regex: /gpu:[0-9]+$/,
- msg: 'Excluded by default since this is a CPU thread setting up GPU kernels.'
- }
-];
-
-Polymer({
- is: 'tf-graph-controls',
- properties: {
- // Public API.
- stats: {
- value: null,
- type: Object,
- observer: '_statsChanged'
- },
- devicesForStats: {
- value: null,
- type: Object,
- notify: true,
- readonly: true,
- },
- colorBy: {
- type: String,
- value: 'structure',
- notify: true,
- readonly: true
- },
- colorByParams: Object,
- datasets: {
- type: Array,
- observer: '_datasetsChanged'
- },
- renderHierarchy: {
- type: Object,
- notify: true,
- },
- metadataTags: {
- type: Array,
- computed: '_getMetadataTags(selectedDataset, datasets)'
- },
- selectedDataset: {
- type: Number,
- notify: true,
- value: 0,
- observer: '_selectedDatasetChanged'
- },
- selectedFile: {
- type: Object,
- notify: true
- },
- selectedMetadataTag: {
- type: Number,
- notify: true,
- value: -1
- },
- _currentGradientParams: {
- type: Object,
- computed: '_getCurrentGradientParams(colorByParams, colorBy)'
- },
- showSessionRunsDropdown: {
- type: Boolean,
- value: true
- },
- showUploadButton: {
- type: Boolean,
- value: true
- },
- // This stores whether the feature for showing health pills is enabled in the first place.
- healthPillsFeatureEnabled: Boolean,
- // This stores whether to show health pills. Only relevant if healthPillsFeatureEnabled. The
- // user can toggle this value.
- healthPillsToggledOn: {
- type: Boolean,
- notify: true,
- },
- },
- listeners: {
- 'trace-inputs.change': '_traceInputToggleChanged'
- },
- _traceInputToggleChanged: function(event) {
- // Flip the state of the trace inputs flag.
- this.renderHierarchy.traceInputs = event.target.active;
- tf.graph.scene.node.traceInputs(this.renderHierarchy);
- },
- _xlaClustersProvided: function(renderHierarchy) {
- return renderHierarchy &&
- renderHierarchy.hierarchy &&
- renderHierarchy.hierarchy.xlaClusters.length > 0;
- },
- _statsChanged: function(stats) {
- if (stats == null) {
- return;
- }
- var devicesForStats = {};
- var devices = _.each(stats.dev_stats, function(d) {
- // Avoid device names that are ignored by default.
- var exclude = _.some(DEVICE_NAMES_EXCLUDE, function(rule) {
- return rule.regex.test(d.device);
- });
- if (!exclude) {
- devicesForStats[d.device] = true;
- }
- });
- this.set('devicesForStats', devicesForStats);
- },
- _getDevices: function(devicesForStats) {
- var devices = _.map(this.stats.dev_stats, function(d) {
- return d.device;
- });
- // Devices names can be long so we remove the longest common prefix
- // before showing the devices in a list.
- var suffixes = tf.graph.util.removeCommonPrefix(devices);
- return _.map(devices, function(device, i) {
- var ignoredMsg = null;
- _.each(DEVICE_NAMES_EXCLUDE, function(rule) {
- if (rule.regex.test(device)) {
- ignoredMsg = rule.msg;
- }
- });
- return {
- device: device,
- suffix: suffixes[i],
- used: devicesForStats[device],
- ignoredMsg: ignoredMsg
- };
- });
- },
- _deviceCheckboxClicked: function(checkbox) {
- // Update the device map.
- var devicesForStats = _.extend({}, this.devicesForStats);
- var device = checkbox.target.value;
- if (checkbox.target.checked) {
- devicesForStats[device] = true;
- } else {
- delete devicesForStats[device];
- }
- this.set('devicesForStats', devicesForStats);
- },
- _numSessionRuns: function(metadataTags) {
- return metadataTags != null ? metadataTags.length : 0;
- },
- _getBackgroundColor: function(color) {
- return 'background-color:' + color;
- },
- fit: function() {
- document.querySelector('#scene').fit();
- },
- _isGradientColoring: function(stats, colorBy) {
- return ["compute_time", "memory"].indexOf(colorBy) !== -1
- && stats != null;
- },
- _equals: function(a, b) {
- return a === b;
- },
- _getCurrentGradientParams: function(colorByParams, colorBy) {
- if (!this._isGradientColoring(this.stats, colorBy)) {
- return;
- }
- var params = colorByParams[colorBy];
- var minValue = params.minValue;
- var maxValue = params.maxValue;
- if (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 === '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: minValue,
- maxValue: maxValue,
- startColor: params.startColor,
- endColor: params.endColor
- };
- },
- download: function() {
- this.$.graphdownload.click();
- },
- _updateFileInput: function(e) {
- var file = e.target.files[0];
- if (!file) {
- return;
- }
- this._setDownloadFilename(file.name);
- this.set('selectedFile', e);
- },
- _datasetsChanged: function(newDatasets, oldDatasets) {
- if (oldDatasets != null || this.selected == null) {
- // Select the first dataset by default.
- this.set('selectedDataset', 0);
- this._setDownloadFilename(this.datasets[this.selectedDataset].path);
- }
- },
- _getMetadataTags: function(selectedDataset, datasets) {
- return this.datasets[selectedDataset].runMetadata;
- },
- _selectedDatasetChanged: function(newDataset, oldDataset) {
- if (this.datasets) {
- this.set('selectedMetadataTag', -1);
- this.set('colorBy', 'structure');
- this.$['trace-inputs'].active = false; // Set trace input to off-state.
- this._setDownloadFilename(this.datasets[newDataset].path);
- }
- },
- _getFile: function() {
- this.$$("#file").click();
- },
- _setDownloadFilename: function(graphPath) {
- // Strip off everything before the last "/" and strip off the file
- // extension in order to get the name of the PNG for the graph.
- var dotIndex = graphPath.lastIndexOf('.');
- if (dotIndex) {
- graphPath = graphPath.substring(0, dotIndex);
- }
- var slashIndex = graphPath.lastIndexOf('/');
- if (slashIndex) {
- graphPath = graphPath.substring(slashIndex + 1);
- }
- this.$.graphdownload.setAttribute('download', graphPath + '.png');
- }
-});
-})(); // Closing private scope.
-</script>
-</dom-module>
-
-<dom-module id="tf-graph-dashboard" assetpath="../tf-graph-dashboard/">
-<template>
-<tf-no-data-warning data-type="graph" show-warning="[[_datasetsEmpty(_datasets)]]"></tf-no-data-warning>
-<template is="dom-if" if="[[!_datasetsEmpty(_datasets)]]">
-<tf-dashboard-layout>
-<div class="sidebar">
- <tf-graph-controls id="controls" devices-for-stats="{{_devicesForStats}}" color-by-params="[[_colorByParams]]" stats="[[_stats]]" color-by="{{_colorBy}}" datasets="[[_datasets]]" render-hierarchy="[[_renderHierarchy]]" selected-dataset="{{_selectedDataset}}" selected-file="{{_selectedFile}}" selected-metadata-tag="{{_selectedMetadataTag}}" health-pills-feature-enabled="[[debuggerDataEnabled]]" health-pills-toggled-on="{{healthPillsToggledOn}}"></tf-graph-controls>
- <tf-graph-loader id="loader" datasets="[[_datasets]]" selected-dataset="[[_selectedDataset]]" selected-metadata-tag="[[_selectedMetadataTag]]" selected-file="[[_selectedFile]]" out-graph-hierarchy="{{_graphHierarchy}}" out-graph="{{_graph}}" out-stats="{{_stats}}" progress="{{_progress}}" out-hierarchy-params="{{_hierarchyParams}}"></tf-graph-loader>
-</div>
-<div class="center">
- <tf-graph-board id="graphboard" devices-for-stats="[[_devicesForStats]]" color-by="[[_colorBy]]" color-by-params="{{_colorByParams}}" graph-hierarchy="[[_graphHierarchy]]" graph="[[_graph]]" hierarchy-params="[[_hierarchyParams]]" progress="[[_progress]]" debugger-data-enabled="[[debuggerDataEnabled]]" are-health-pills-loading="[[_areHealthPillsLoading]]" node-names-to-health-pills="[[_nodeNamesToHealthPills]]" all-steps-mode-enabled="{{allStepsModeEnabled}}" specific-health-pill-step="{{specificHealthPillStep}}" health-pill-step-index="[[_healthPillStepIndex]]" render-hierarchy="{{_renderHierarchy}}" stats="[[_stats]]"></tf-graph-board>
-</div>
-</tf-dashboard-layout>
-</template>
-<style>
-
-:host /deep/ {
- font-family: 'Roboto', sans-serif;
-}
-
-.center {
- position: relative;
- height: 100%;
-}
-
-</style>
-</template>
-</dom-module>
-
-<script>
-(function() {
-TF.Dashboard.TfGraphDashboard = Polymer({
- is: 'tf-graph-dashboard',
- factoryImpl: function(backend, debuggerDataEnabled) {
- this.backend = backend;
- this.debuggerDataEnabled = debuggerDataEnabled;
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("graphs"),
- TF.Dashboard.ReloadBehavior("tf-graph-dashboard"),
- TF.Backend.Behavior,
- ],
- properties: {
- _datasets: Object,
- _renderHierarchy: {type: Object, observer: '_renderHierarchyChanged'},
- backend: Object,
- debuggerDataEnabled: Boolean,
- allStepsModeEnabled: Boolean,
- specificHealthPillStep: {type: Number, value: 0},
- healthPillsToggledOn: {type: Boolean, value: true, observer: '_healthPillsToggledOnChanged'},
- _isAttached: Boolean,
- // Whether this dashboard is initialized. This dashboard should only be initialized once.
- _initialized: Boolean,
- // Whether health pills are currently being loaded, in which case we may want to say show a
- // spinner.
- _areHealthPillsLoading: Boolean,
- // Maps the names of nodes to an array of health pills (HealthPillDatums).
- _nodeNamesToHealthPills: {
- type: Object,
- value: {},
- },
- _healthPillStepIndex: Number,
- // A strictly increasing ID. Each request for health pills has a unique ID. This helps us
- // identify stale requests.
- _healthPillRequestId: {type: Number, value: 1},
- // The setTimeout ID for the pending request for health pills at a specific step.
- _healthPillStepRequestTimerId: Number,
- // The request for health pills at a specific step (as opposed to all sampled health pills) may
- // involve slow disk reads. Hence, we throttle to 1 of those requests every this many ms.
- _healthPillStepRequestTimerDelay: {
- type: Number,
- value: 500,
- readOnly: true,
- },
- runs: Array,
- },
- listeners: {
- 'node-toggle-expand': '_handleNodeToggleExpand',
- },
- observers: [
- '_maybeFetchHealthPillsAtSpecificStep(allStepsModeEnabled, specificHealthPillStep)',
- '_maybeInitializeDashboard(backend, _isAttached)',
- ],
- attached: function() {
- this.set('_isAttached', true);
- },
- detached: function() {
- this.set('_isAttached', false);
- },
- reload: function() {
- if (!this.debuggerDataEnabled ||
- !this.healthPillsToggledOn ||
- !this._renderHierarchy ||
- this._datasetsEmpty(this._datasets)) {
- // Do not load debugger data if the feature is disabled, if the user toggled off the feature,
- // or if the graph itself has not loaded yet. We need the graph to load so that we know which
- // nodes to request health pills for.
- return;
- }
-
- // Request debugger data on graph reloads, but do not re-request the graph itself. The graph
- // would not change across reloads.
- this._requestHealthPills();
- },
- _maybeInitializeDashboard: function(backend, isAttached) {
- if (this._initialized || !backend || !isAttached) {
- // Either this dashboard is already initialized ... or we are not yet ready to initialize.
- return;
- }
-
- // Set this to true so we only initialize once.
- this._initialized = true;
- Promise.all([backend.graphRuns(), backend.runMetadataRuns()])
- .then(function(result) {
- var runsWithGraph = result[0].sort(VZ.Sorting.compareTagNames);
- var runToMetadata = result[1];
- var datasets = _.map(runsWithGraph, function(runName) {
- return {
- name: runName,
- path: backend.router.graph(
- runName, tf.graph.LIMIT_ATTR_SIZE, tf.graph.LARGE_ATTRS_KEY),
- runMetadata: runToMetadata[runName] ? _.map(
- runToMetadata[runName].sort(VZ.Sorting.compareTagNames), function(tag) {
- return {
- tag: tag,
- path: backend.router.runMetadata(tag, runName)
- };
- }, this) : []
- };
- }, this);
- this.set('_datasets', datasets);
- }.bind(this));
- },
- _requestHealthPills: function() {
- this.set('_areHealthPillsLoading', true);
- const requestId = ++this._healthPillRequestId;
-
- if (this._healthPillStepRequestTimerId !== null) {
- // A request for health pills is already scheduled to be initiated. Clear it, and schedule a
- // new request.
- window.clearTimeout(this._healthPillStepRequestTimerId);
- this._healthPillStepRequestTimerId = null;
- }
-
- if (this.allStepsModeEnabled) {
- // This path may be slow. Schedule network requests to start some time later. If another
- // request is scheduled in the mean time, drop this current request.
- this._healthPillStepRequestTimerId = setTimeout(function() {
- this._healthPillStepRequestTimerId = null;
- this._initiateNetworkRequestForHealthPills(requestId);
- }.bind(this), this._healthPillStepRequestTimerDelay);
- } else {
- // The user is fetching sampled steps. This path is fast, so no need to throttle. Directly
- // fetch the health pills across the network.
- this._initiateNetworkRequestForHealthPills(requestId);
- }
- },
- // Initiates the network request for health pills. Do not directly call this method - network
- // requests may be throttled. Instead, call _requestHealthPills, which uses this method.
- _initiateNetworkRequestForHealthPills: function(requestId) {
- if (this._healthPillRequestId !== requestId) {
- // This possibly scheduled request was outdated before it was even sent across the network. Do
- // not bother initiating it.
- return;
- }
-
- const specificStep = this.allStepsModeEnabled ? this.specificHealthPillStep : undefined;
- this.backend.healthPills(this._renderHierarchy.getNamesOfRenderedOps(), specificStep).then(
- function(result) {
- if (!this.healthPillsToggledOn) {
- // The user has opted to hide health pills via the toggle button.
- return;
- }
-
- if (requestId !== this._healthPillRequestId) {
- // This response is no longer relevant.
- return;
- }
-
- // Set the index for which step to show for the health pills. By default, show the last step.
- // A precondition we assume (that Tensorboard's reservoir sampling guarantees) is that all
- // node names should be mapped to the same number of steps.
- for (let nodeName in result) {
- this.set('_healthPillStepIndex', result[nodeName].length - 1);
- break;
- }
-
- this.set('_nodeNamesToHealthPills', result);
- this.set('_areHealthPillsLoading', false);
- this.set('_healthPillStepRequestTimerId', null);
- }.bind(this));
- },
- _datasetsEmpty: function(datasets) {
- return !datasets || !datasets.length;
- },
- _renderHierarchyChanged: function(renderHierarchy) {
- // Reload any data on the graph when the render hierarchy (which determines which nodes are
- // rendered) changes.
- this.reload();
- },
- _handleNodeToggleExpand: function() {
- // Nodes were toggled. We may need to request health pills for more nodes.
- this._requestHealthPills();
- },
- _healthPillsToggledOnChanged: function(healthPillsToggledOn) {
- if (healthPillsToggledOn) {
- // Load health pills.
- this.reload();
- } else {
- // Remove all health pills by setting an empty mapping.
- this.set('_nodeNamesToHealthPills', {});
- }
- },
- // Fetch health pills for a specific step if applicable.
- _maybeFetchHealthPillsAtSpecificStep: function(allStepsModeEnabled, specificHealthPillStep) {
- if (!this._renderHierarchy) {
- // The graph is not ready yet.
- return;
- }
-
- this._requestHealthPills();
- },
-});
-})();
-</script>
-<link rel="import" href="../paper-material/paper-material.html">
-
-
-<style>
- tf-text-loader p {
- margin: 0.3em 0;
- }
-
- tf-text-loader table {
- border-collapse: collapse;
- }
-
- tf-text-loader table th {
- font-weight: 600;
- }
-
- tf-text-loader table th,
- tf-text-loader table td {
- padding: 6px 13px;
- border: 1px solid #dfe2e5;
- }
-
- tf-text-loader table tr {
- background-color: #fff;
- border-top: 1px solid #c6cbd1;
- }
-
-</style>
-<dom-module id="tf-text-loader" assetpath="../tf-text-dashboard/">
-
-
- <template>
- <style include="scrollbar-style"></style>
- <paper-material elevation="1" id="outer" class="container scrollbar">
- <template id="repeater" is="dom-repeat" items="[[_texts]]">
- <paper-material elevation="1" class="step-container">
- step <span class="step-value">[[_numfmt(item.step)]]</span>
- </paper-material>
- <paper-material elevation="1" inner-h-t-m-l="[[item.text]]" class="text">
- </paper-material>
- </template>
- </paper-material>
-
-
- <style>
- #outer {
- display: block;
- overflow: auto;
- max-height: 500px;
- position: relative;
- border-radius: 3px;
- border: 2px solid black;
- }
- .text {
- margin: 0 10px 10px 10px;
- border-radius: 0 3px 3px 3px;
- background-color: white;
- padding: 5px;
- word-break: break-word;
- }
- .step-container {
- border-left: 1px solid #ccc;
- border-right: 1px solid #ccc;
- border-top: 1px solid #ccc;
- border-radius: 3px 3px 0 0;
- font-style: italic;
- margin-top: 10px;
- background-color: var(--tb-ui-light-accent);
- display: inline-block;
- margin-left: 9px;
- padding: 3px;
- font-size: 12px;
- }
-
- </style>
-
- </template>
- <script>
- Polymer({
- is: "tf-text-loader",
- properties: {
- colorScale: Object,
- run: String,
- // This is an array of Tensorboard Text&Datum objects (See backend.ts for details). The
- // properties of objects in this array are
- // {
- // wall_time: Date,
- // step: number,
- // text: string,
- // }
- // they are ordered from most recent to oldest
- _texts: {
- type: Array,
- value: [],
- },
-
- },
- redraw: function() {
- // Other dashboards logic requires a redraw method to be defined.
- },
- setVisibleSeries: function(runs) {
- // Do nothing.
- },
- setSeriesData: function(run, texts) {
- this.set("run", run);
- this.set("_texts", texts.reverse());
-
- // Update the border color based on the run.
- var color = this.colorScale.scale(run);
- this.$$("#outer").style.borderColor = color;
- },
- _numfmt: function(n) {
- return d3.format(",")(n);
- }
- });
- </script>
-</dom-module>
-
-<dom-module id="tf-text-dashboard" assetpath="../tf-text-dashboard/">
- <template>
- <paper-dialog with-backdrop="" id="actual-text-size-dialog"></paper-dialog>
- <div id="plumbing">
- <tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{_colorScale}}"></tf-color-scale>
- </div>
-
- <tf-dashboard-layout>
- <div class="sidebar">
- <tf-sidebar-helper backend="[[backend]]" categories="{{_categories}}" color-scale="[[_colorScale]]" run2tag="[[run2tag]]" runs="[[runs]]" selected-runs="{{_selectedRuns}}">
- </tf-sidebar-helper>
- </div>
- <div class="center">
- <tf-panes-helper categories="[[_categories]]" color-scale="[[_colorScale]]" data-type="[[dataType]]" data-provider="[[dataProvider]]" data-not-found="[[dataNotFound]]" run2tag="[[run2tag]]" selected-runs="[[_selectedRuns]]" repeat-for-runs="">
- <template>
- <tf-text-loader color-scale="[[_colorScale]]"></tf-text-loader>
- </template>
- </tf-panes-helper>
- </div>
- </tf-dashboard-layout>
- <style include="dashboard-style"></style>
- <style>
- tf-panes-helper {
- --card-width: 100%;
- --card-height: auto;
- --card-expanded-width: 100%;
- --card-expanded-height: 1000px;
- --card-padding: 0 5px 5px 5px;
- --show-expand-button: none;
- }
-
- </style>
- </template>
- <script>
- TF.Dashboard.TfTextDashboard = Polymer({
- is: "tf-text-dashboard",
- factoryImpl: function(backend) {
- this.backend = backend;
- },
- properties: {
- backend: Object,
- dataType: {
- type: String,
- value: "text"
- },
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("text"),
- TF.Dashboard.ReloadBehavior("tf-chart-scaffold"),
- TF.Backend.Behavior,
- ],
- attached: function() {
- this.async(function() {
- this.fire("rendered");
- });
- },
- });
- </script>
-</dom-module>
-<dom-module id="vz-projector-styles" assetpath="../vz-projector/">
-<template>
-<style>
-:host {
- --paper-input-container-label: {
- font-size: 14px;
- };
- --paper-input-container-input: {
- font-size: 14px;
- };
- /* TODO: Figure out why this doesn't work */
- --paper-dropdown-menu-input: {
- font-size: 14px;
- };
-}
-
-paper-button {
- background: #e3e3e3;
- margin-left: 0;
- text-transform: none;
-}
-
-paper-dropdown-menu paper-item {
- font-size: 13px;
-}
-
-paper-tooltip {
- max-width: 200px;
- --paper-tooltip: {
- font-size: 12px;
- };
-}
-
-paper-checkbox {
- --paper-checkbox-checked-color: #880E4F;
-}
-
-paper-toggle-button {
- --paper-toggle-button-checked-bar-color: #880E4F;
- --paper-toggle-button-checked-button-color: #880E4F;
- --paper-toggle-button-checked-ink-color: #880E4F;
-}
-
-paper-icon-button {
- border-radius: 50%;
-}
-
-paper-icon-button[active] {
- color: white;
- background-color: #880E4F;
-}
-
-.slider {
- display: flex;
- align-items: center;
- margin-bottom: 10px;
- justify-content: space-between;
-}
-
-.slider span {
- width: 35px;
- text-align: right;
-}
-
-.slider label {
- align-items: center;
- display: flex;
-}
-
-.help-icon {
- height: 15px;
- left: 2px;
- min-width: 15px;
- min-height: 15px;
- margin: 0;
- padding: 0;
- top: -2px;
- width: 15px;
-}
-
-.ink-panel {
- display: flex;
- flex-direction: column;
- font-size: 14px;
-}
-
-.ink-panel h4 {
- border-bottom: 1px solid #ddd;
- font-size: 14px;
- font-weight: 500;
- margin: 0;
- margin-bottom: 10px;
- padding-bottom: 5px;
-}
-
-.ink-panel-header {
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
- border-top: 1px solid rgba(0, 0, 0, 0.1);
- height: 50px;
-}
-
-.ink-panel-content {
- display: none;
- height: 100%;
-}
-
-.ink-panel-content.active {
- display: block;
-}
-
-.ink-panel-content h3 {
- font-weight: 500;
- font-size: 14px;
- margin-top: 20px;
- margin-bottom: 5px;
- text-transform: uppercase;
-}
-
-.ink-panel-header h3 {
- font-weight: 500;
- font-size: 14px;
- margin: 0;
- padding: 0 24px;
- text-transform: uppercase;
-}
-
-
-/* - Tabs */
-.ink-tab-group {
- align-items: center;
- box-sizing: border-box;
- display: flex;
- height: 100%;
- justify-content: space-around;
-}
-
-.ink-tab-group .projection-tab {
- color: rgba(0, 0, 0, 0.5);
- cursor: pointer;
- font-weight: 300;
- line-height: 49px;
- padding: 0 12px;
- text-align: center;
- text-transform: uppercase;
-}
-
-.ink-tab-group .projection-tab:hover {
- color: black;
-}
-
-.ink-tab-group .projection-tab.active {
- border-bottom: 2px solid black;
- color: black;
- font-weight: 500;
-}
-
-h4 {
- margin: 30px 0 10px 0;
-}
-
-.dismiss-dialog-note {
- margin-top: 25px;
- font-size: 11px;
- text-align: right;
-}
-</style>
-</template>
-</dom-module>
-<link rel="import" href="../paper-input/paper-textarea.html">
-<dom-module id="vz-projector-bookmark-panel" assetpath="../vz-projector/">
-<template>
-<style include="vz-projector-styles"></style>
-<style>
-#title {
- background-color: #fafafa;
- color: black;
- font-weight: 500;
- left: 0;
- line-height: 60px;
- padding-left: 24px;
- position: absolute;
- width: 276px;
-}
-#bookmark-container {
- background-color: #fafafa;
-}
-#icon-container {
- line-height: 60px;
- position: absolute;
- right: 0;
-}
-#header {
- border-top: 1px solid rgba(0, 0, 0, 0.1);
- position: relative;
-}
-#panel {
- background-color: #fafafa;
- position: relative;
- overflow-y: scroll;
- top: 60px;
- max-height: 50vh;
-}
-
-#save-container {
- text-align: center;
-}
-
-.state-radio {
- display: table-cell;
- vertical-align: middle;
- padding-top: 16px;
-}
-
-.state-label {
- display: table-cell;
- vertical-align: middle;
- top: 14px;
-}
-
-.state-label-input {
- width: 194px;
-}
-
-.state-clear {
- display: table-cell;
- vertical-align: middle;
- padding-top: 20px;
-}
-#state-file {
- display: none;
-}
-#no-bookmarks {
- padding: 0 24px;
-}
-#action-buttons-container .add-icon-button {
- background-color: #03a9f4;
- color: white;
- margin: 0 4px 4px auto;
- right: 7px;
- top: -4px;
-}
-.upload-download-icon-button {
- padding: 0;
-}
-#action-buttons-container {
- display: flex;
- margin-left: 34px;
- margin-top: 6px;
-}
-.ink-fab {
- border-radius: 50%;
- background: white;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
-}
-paper-textarea {
- --paper-input-container-input: {
- font-size: 12px;
- }
- --paper-font-caption: {
- display: none
- }
-}
-</style>
-
-
-<div id="bookmark-container">
- <div id="header">
- <div id="title">
- BOOKMARKS ([[savedStates.length]])
- <paper-icon-button icon="help" class="help-icon"></paper-icon-button>
- <paper-tooltip animation-delay="0" position="top" offset="0">
- Open this drawer to save a set of views of the projection, including
- selected points. A file containing the bookmarks can then be saved and
- later loaded to view them.
- </paper-tooltip>
- </div>
- <div id="icon-container">
-
- <paper-icon-button id="expand-more" icon="expand-less" on-tap="_expandMore"></paper-icon-button>
- <paper-icon-button id="expand-less" style="display: none" icon="expand-more" on-tap="_expandLess"></paper-icon-button>
- </div>
- </div>
- <iron-collapse id="panel">
-
- <div id="state-section">
- <template is="dom-if" if="[[!savedStates.length]]">
- <p id="no-bookmarks">
- No bookmarks yet, upload a bookmarks file or add a new bookmark by clicking the "+" below.
- </p>
- </template>
-
- <template is="dom-repeat" items="{{savedStates}}">
- <div class="state-row">
- <div class="state-radio">
- <template is="dom-if" if="{{item.isSelected}}">
- <paper-icon-button icon="radio-button-checked"></paper-icon-button>
- </template>
- <template is="dom-if" if="{{!item.isSelected}}">
- <paper-icon-button icon="radio-button-unchecked" data-index$="{{index}}" on-tap="_radioButtonHandler"></paper-icon-button>
- </template>
- </div>
- <div class="state-label">
- <paper-textarea value="[[item.label]]" class="state-label-input" on-keyup="_labelChange" data-index$="[[index]]" autoresizing="">
- </paper-textarea></div>
- <div class="state-clear">
- <paper-icon-button icon="clear" data-index$="{{index}}" on-tap="_clearButtonHandler"></paper-icon-button>
- </div>
- </div>
- </template>
-
- <div id="action-buttons-container">
- <paper-icon-button class="upload-download-icon-button" icon="save" title="Save bookmarks" disabled="[[!hasStates]]" on-tap="_downloadFile"></paper-icon-button>
- <paper-icon-button class="upload-download-icon-button" icon="file-upload" title="Load bookmarks" on-tap="_uploadFile"></paper-icon-button>
- <paper-icon-button class="add-icon-button ink-fab" icon="add" title="Add bookmark" on-tap="_addBookmark"></paper-icon-button>
- <input type="file" id="state-file" name="state-file">
- </div>
- </div>
- </iron-collapse>
-</div>
-
-</template>
-</dom-module>
-<dom-module id="vz-projector-legend" assetpath="../vz-projector/">
-<template>
-<style include="vz-projector-styles"></style>
-<style>
-.item {
- display: flex;
- align-items: flex-start;
- margin-bottom: 10px;
-}
-
-.shape {
- width: 10px;
- height: 10px;
- margin-right: 10px;
- margin-top: 5px;
- border-radius: 50%;
-}
-
-.label {
- flex-grow: 1;
-}
-
-.gradient {
- width: 100%;
- height: 10px;
-}
-
-.gradient-boundaries {
- display: flex;
- justify-content: space-between;
-}
-</style>
-
-<template is="dom-repeat" items="[[renderInfo.items]]">
- <div class="item">
- <div class="shape" style="background-color: [[item.color]];"></div>
- <div class="label">[[item.label]]</div>
- <div class="info" style="color: [[item.color]];">[[item.count]]</div>
- </div>
-</template>
-
-<template is="dom-if" if="[[renderInfo.thresholds]]">
- <svg class="gradient">
- <defs>
- <linearGradient id="gradient" x1="0%" y1="100%" x2="100%" y2="100%"></linearGradient>
- </defs>
- <rect height="10" style="fill: url(&quot;#gradient&quot;);"></rect>
- </svg>
- <div class="gradient-boundaries">
- <div>[[renderInfo.thresholds.0.value]]</div>
- <div>[[_getLastThreshold(renderInfo.thresholds)]]</div>
- </div>
-</template>
-
-</template>
-</dom-module><dom-module id="vz-projector-data-panel" assetpath="../vz-projector/">
-<template>
-<style include="vz-projector-styles"></style>
-<style>
-.container {
- padding: 10px 20px 20px 20px;
-}
-
-input[type=file] {
- display: none;
-}
-
-.file-name {
- margin-right: 10px;
-}
-
-.dirs {
- color: rgba(0, 0, 0, 0.7);
- font-size: 12px;
-}
-
-.dirs table tr {
- vertical-align: top;
-}
-
-.dirs table tr td {
- padding-bottom: 10px;
-}
-
-paper-item {
- --paper-item-disabled: {
- border-bottom: 1px solid black;
- justify-content: center;
- font-size: 12px;
- line-height: normal;
- min-height: 0px;
- };
-}
-
-.item-details {
- margin-left: 5px;
- color: gray;
- font-size: 12px;
-}
-
-paper-dropdown-menu {
- width: 100%;
-}
-
-paper-dropdown-menu paper-item {
- justify-content: space-between;
-}
-
-.title {
- align-items: center;
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
- color: black;
- display: flex;
- font-weight: 500;
- height: 59px;
- padding-left: 20px;
-}
-
-#normalize-data-checkbox {
- margin: 10px 0;
-}
-
-#projector-config-template {
- --paper-input-container-input: {
- line-height: 13px;
- font-family: monospace;
- font-size: 12px;
- };
-}
-
-#generate-share-url {
- padding: 16px;
- margin-left: 24px;
-}
-
-#projector-share-button-container {
- margin: 10px 0;
-}
-
-.config-checkbox {
- display: inline-block;
- font-size: 11px;
- margin-left: 10px;
-}
-
-.projector-config-options {
- margin-top: 12px;
-}
-
-.projector-config-dialog-container {
- padding: 24px;
-}
-
-.code {
- background-color: #f7f7f7;
- display: table;
- font-family: monospace;
- margin-top: 7px;
- padding: 15px;
-}
-
-.delimiter {
- color: #B71C1C;
-}
-
-.upload-step {
- display: flex;
- justify-content: space-between;
- margin-bottom: 6px;
-}
-
-.upload-step paper-button {
- margin-left: 30px;
-}
-
-.step-label {
- color: rgb(38, 180, 226);
-}
-
-.scrollable-container {
- margin-top: 0;
- min-width: 400px;
-}
-
-#projectorConfigDialog p {
- margin: 8px 0 8px;
-}
-
-.data-step {
- margin-top: 40px;
-}
-
-.data-step-contents {
- display: table;
- width: 100%;
-}
-
-.data-step-contents-contents {
- display: table-cell;
- margin-top: 6px;
-}
-
-.data-step-contents-upload {
- display: table-cell;
- text-align: right;
- vertical-align: bottom;
-}
-
-#demo-data-buttons-container {
- display: none;
-}
-
-.colorby-container {
- margin-bottom: 10px;
-}
-</style>
-<div class="title">DATA</div>
-<div class="container">
-
- <template is="dom-if" if="[[_hasChoices(runNames)]]">
- <paper-dropdown-menu no-animations="" label="[[_getNumRunsLabel(runNames)]] found">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{selectedRun}}">
- <template is="dom-repeat" items="[[runNames]]">
- <paper-item value="[[item]]" label="[[item]]">
- [[item]]
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- </template>
-
- <template is="dom-if" if="[[tensorNames]]">
-
- <paper-dropdown-menu no-animations="" label="[[_getNumTensorsLabel(tensorNames)]] found">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{selectedTensor}}">
- <template is="dom-repeat" items="[[tensorNames]]">
- <paper-item value="[[item.name]]" label="[[item.name]]">
- [[item.name]]
- <span class="item-details">
- [[item.shape.0]]x[[item.shape.1]]
- </span>
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- </template>
-
- <template is="dom-if" if="[[_hasChoices(labelOptions)]]">
- <paper-dropdown-menu no-animations="" label="Label by">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{selectedLabelOption}}">
- <template is="dom-repeat" items="[[labelOptions]]">
- <paper-item value="[[item]]" label="[[item]]">
- [[item]]
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- </template>
-
-
- <div hidden$="[[!_hasChoices(colorOptions)]]" class="colorby-container">
- <paper-dropdown-menu id="colorby" no-animations="" label="Color by">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{selectedColorOptionName}}">
- <template is="dom-repeat" items="[[colorOptions]]">
- <paper-item class$="[[getSeparatorClass(item.isSeparator)]]" value="[[item.name]]" label="[[item.name]]" disabled="[[item.isSeparator]]">
- [[item.name]]
- <span class="item-details">[[item.desc]]</span>
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- <div hidden$="[[!showForceCategoricalColorsCheckbox]]">
- <paper-checkbox id="force-categorical-checkbox"></paper-checkbox>
- Use categorical coloring
- <paper-icon-button icon="help" class="help-icon"></paper-icon-button>
- <paper-tooltip position="bottom" animation-delay="0" fit-to-visible-bounds="">
- For metadata fields that have many unique values we use a gradient color map
- by default. This checkbox allows you to force categorical coloring by a given
- metadata field.
- </paper-tooltip>
- </div>
- <template dom-if="[[colorLegendRenderInfo]]">
- <vz-projector-legend render-info="[[colorLegendRenderInfo]]"></vz-projector-legend>
- </template>
- </div>
- <paper-checkbox id="normalize-data-checkbox" checked="{{normalizeData}}">
- Sphereize data
- <paper-icon-button icon="help" class="help-icon"></paper-icon-button>
- <paper-tooltip position="bottom" animation-delay="0" fit-to-visible-bounds="">
- The data is normalized by shifting each point by the centroid and making
- it unit norm.
- </paper-tooltip>
- </paper-checkbox>
- <p id="demo-data-buttons-container">
- <span>
- <paper-tooltip position="bottom" animation-delay="0" fit-to-visible-bounds="">
- Load data from your computer
- </paper-tooltip>
- <paper-button id="upload" class="ink-button" onclick="dataDialog.open()">Load data</paper-button>
- </span>
- <span id="publish-container">
- <paper-tooltip position="bottom" animation-delay="0" fit-to-visible-bounds="">
- Publish your embedding visualization and data
- </paper-tooltip>
- <paper-button id="host-embedding" class="ink-button" onclick="projectorConfigDialog.open()">Publish</paper-button>
- </span>
- </p>
- <div>
- <paper-dialog id="dataDialog" with-backdrop="">
- <h2>Load data from your computer</h2>
- <paper-dialog-scrollable class="scrollable-container">
- <div class="data-step" id="upload-tensors-step-container">
- <div class="upload-step">
- <div>
- <b><span class="step-label">Step 1:</span> Load a TSV file of vectors.</b>
- </div>
- </div>
- <div class="data-step-contents">
- <div class="data-step-contents-contents">
- Example of 3 vectors with dimension 4:
- <div class="code">
- 0.1<span class="delimiter">\t</span>0.2<span class="delimiter">\t</span>0.5<span class="delimiter">\t</span>0.9<br>
- 0.2<span class="delimiter">\t</span>0.1<span class="delimiter">\t</span>5.0<span class="delimiter">\t</span>0.2<br>
- 0.4<span class="delimiter">\t</span>0.1<span class="delimiter">\t</span>7.0<span class="delimiter">\t</span>0.8
- </div>
- </div>
- <div class="data-step-contents-upload">
- <paper-button id="upload-tensors" title="Choose a TSV tensor file">Choose file</paper-button>
- <input type="file" id="file" name="file">
- </div>
- </div>
- </div>
- <div class="data-step">
- <div class="upload-step">
- <div>
- <span class="step-label" id="upload-metadata-label"><b>Step 2</b> (optional):</span> <b>Load a TSV file of metadata.</b>
- </div>
- </div>
- <div class="data-step-contents">
- <div class="data-step-contents-contents">
- Example of 3 data points and 2 columns.<br>
- <i>Note: If there is more than one column, the first row will be parsed as column labels.</i>
- <div class="code">
- <b>Pokémon<span class="delimiter">\t</span>Species</b><br>
- Wartortle<span class="delimiter">\t</span>Turtle<br>
- Venusaur<span class="delimiter">\t</span>Seed<br>
- Charmeleon<span class="delimiter">\t</span>Flame
- </div>
- </div>
- <div class="data-step-contents-upload">
- <paper-button id="upload-metadata" title="Choose a TSV metadata file" class="ink-button">Choose file</paper-button>
- <input type="file" id="file-metadata" name="file-metadata">
- </div>
- </div>
- </div>
- </paper-dialog-scrollable>
- <div class="dismiss-dialog-note">Click outside to dismiss.</div>
- </paper-dialog>
- <paper-dialog id="projectorConfigDialog" with-backdrop="">
- <h2>Publish your embedding visualization and data</h2>
- <paper-dialog-scrollable class="scrollable-container">
- <div>
- <p>
- If you'd like to share your visualization with the world, follow these simple steps.
- See <a target="_blank" href="https://www.tensorflow.org/get_started/embedding_viz">this tutorial</a> for more.
- </p>
- <h4><span class="step-label">Step 1:</span> Make data public</h4>
- <p>
- Host tensors, metadata, sprite image, and bookmarks TSV files <i>publicly</i> on the web.
- </p>
- <p>
- One option is using a <a target="_blank" href="https://gist.github.com/">github gist</a>.
- If you choose this approach, make sure to link directly to the raw file.
- </p>
- </div>
- <div>
- <h4><span class="step-label">Step 2:</span> Projector config</h4>
- <div class="projector-config-options">
- <i>Optional:</i>
- <div class="config-checkbox">
- <paper-checkbox id="config-metadata-checkbox" checked="">Metadata</paper-checkbox>
- </div>
- <div class="config-checkbox">
- <paper-checkbox id="config-sprite-checkbox">Sprite</paper-checkbox>
- </div>
- <div class="config-checkbox">
- <paper-checkbox id="config-bookmarks-checkbox">Bookmarks</paper-checkbox>
- </div>
- </div>
- </div>
- <paper-textarea id="projector-config-template" label="template_projector_config.json"></paper-textarea>
- <div>
- <h4><span class="step-label">Step 3:</span> Host projector config</h4>
- After you have hosted the projector config JSON file you built above, paste the URL to the config below.
- </div>
- <paper-input id="projector-config-url" label="Path to projector config"></paper-input>
- <paper-input id="projector-share-url" label="Your shareable URL" readonly=""></paper-input>
- <div id="projector-share-button-container">
- <a target="_blank" id="projector-share-url-link">
- <paper-button title="Test your shareable URL" class="ink-button">Test your shareable URL</paper-button>
- </a>
- </div>
- </paper-dialog-scrollable>
- <div class="dismiss-dialog-note">Click outside to dismiss.</div>
- </paper-dialog>
- </div>
- <div class="dirs">
- <table>
- <tbody><tr>
- <td>Checkpoint:</td>
- <td><span id="checkpoint-file"></span></td>
- </tr>
- <tr>
- <td>Metadata:</td>
- <td><span id="metadata-file"></span></td>
- </tr>
- </tbody></table>
- </div>
-</div>
-
-</template>
-</dom-module>
-<dom-module id="vz-projector-input" assetpath="../vz-projector/">
-<template>
-<style include="vz-projector-styles"></style>
-<style>
-.info {
- color: rgba(0, 0, 0, 0.5);
- display: block;
- font-size: 11px;
-}
-
-.toggle {
- font-size: 12px;
- height: 21px;
- margin: 0px;
- min-width: 0px;
- min-height: 0px;
- padding: 0;
- width: 17px;
-}
-
-.toggle[active] {
- background-color: #880E4F;
- color: white;
-}
-</style>
-
-<paper-input label="[[label]]">
- <div class="slash" prefix="">/</div>
- <div class="slash" suffix="">/</div>
- <div suffix="">
- <paper-button id="regex" toggles="" class="toggle">.*</paper-button>
- </div>
-</paper-input>
-<paper-tooltip for="regex" position="bottom" animation-delay="0" fit-to-visible-bounds="">
- Enable/disable regex mode.
-</paper-tooltip>
-<span class="info">[[message]]</span>
-
-
-</template>
-</dom-module><dom-module id="vz-projector-inspector-panel" assetpath="../vz-projector/">
-<style include="vz-projector-styles"></style>
-<style>
-:host {
- display: flex;
- flex-direction: column;
- /* Account for the bookmark pane at the bottom */
- height: calc(100% - 55px);
-}
-
-.container {
- display: block;
- padding: 10px 20px 0 20px;
-}
-
-.buttons {
- display: flex;
- height: 60px;
-}
-
-.button {
- margin-right: 10px;
- border: none;
- border-radius: 7px;
- font-size: 13px;
- padding: 10px;
- background: #e3e3e3;
-}
-
-.button:last-child {
- margin-right: 0;
-}
-
-.nn {
- display: flex;
- flex-direction: column;
-}
-
-.nn > * {
- padding: 0 20px;
-}
-
-.nn-list {
- overflow-y: auto;
-}
-
-.nn-list .neighbor {
- font-size: 12px;
- margin-bottom: 8px;
-}
-
-.nn-list .label-and-value {
- display: flex;
- justify-content: space-between;
-}
-
-.label {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.nn-list .value {
- color: #666;
- float: right;
- font-weight: 300;
- margin-left: 8px;
-}
-
-.nn-list .bar {
- position: relative;
- border-top: 1px solid rgba(0, 0, 0, 0.15);
- margin: 2px 0;
-}
-
-.nn-list .bar .fill {
- position: absolute;
- top: -1px;
- border-top: 1px solid white;
-}
-
-.nn-list .tick {
- position: absolute;
- top: 0px;
- height: 3px;
- border-left: 1px solid rgba(0, 0, 0, 0.15);
-}
-
-.nn-list .neighbor-link:hover {
- cursor: pointer;
-}
-
-.search-by {
- display: flex;
-}
-
-.search-by vz-projector-input {
- width: 100%;
-}
-
-.search-by paper-dropdown-menu {
- margin-left: 10px;
- width: 100px;
-}
-
-.distance .options {
- float: right;
-}
-
-.options a {
- color: #727272;
- font-size: 13px;
- margin-left: 12px;
- text-decoration: none;
-}
-
-.options a.selected {
- color: #009EFE;
-}
-
-.neighbors {
- margin-bottom: 30px;
-}
-
-.neighbors-options {
- margin-top: 6px;
-}
-
-.neighbors-options .option-label, .distance .option-label {
- color: #727272;
- margin-right: 2px;
- width: auto;
-}
-
-.num-neighbors-container {
- display: inline-block;
-}
-
-#nn-slider {
- margin: 0 -12px 0 10px;
-}
-
-.euclidian {
- margin-right: 10px;
-}
-
-.matches-list {
- padding: 0 20px;
-}
-
-.matches-list .row {
- border-bottom: 1px solid #ddd;
- cursor: pointer;
- display: flex;
- font-size: 12px;
- margin: 5px 0;
- padding: 4px 0;
-}
-
-.results {
- display: flex;
- flex-direction: column;
-}
-</style>
-<template>
-<div class="container">
- <div class="buttons">
- <button class="button reset-filter">Show All Data</button>
- <button class="button set-filter">Isolate selection</button>
- <button class="button clear-selection">Clear selection</button>
- </div>
- <div class="search-by">
- <vz-projector-input id="search-box" label="Search"></vz-projector-input>
- <paper-dropdown-menu no-animations="" label="by">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{selectedMetadataField}}">
- <template is="dom-repeat" items="[[metadataFields]]">
- <paper-item value="[[item]]" label="[[item]]">
- [[item]]
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- </div>
-</div>
-<div class="results">
- <div class="nn" style="display: none">
- <div class="neighbors">
- <div class="neighbors-options">
- <div class="slider num-nn">
- <span class="option-label">neighbors</span>
- <paper-icon-button icon="help" class="help-icon"></paper-icon-button>
- <paper-tooltip position="bottom" animation-delay="0" fit-to-visible-bounds="">
- The number of neighbors (in the original space) to show when clicking on a point.
- </paper-tooltip>
- <paper-slider id="nn-slider" pin="" min="5" max="1000" value="100"></paper-slider>
- <span class="nn-count"></span>
- </div>
- </div>
- <div class="distance">
- <span class="option-label">distance</span>
- <div class="options">
- <a class="selected cosine" href="javascript:void(0);">COSINE</a>
- <a class="euclidean" href="javascript:void(0);">EUCLIDIAN</a>
- </div>
- </div>
- </div>
- <p>Nearest points in the original space:
- </p><div class="nn-list"></div>
- </div>
- <div class="matches-list" style="display: none">
- <div class="list"></div>
- <div class="limit-msg">Showing only the first 100 results...</div>
- </div>
-</div>
-
-</template>
-</dom-module>
-<dom-module id="vz-projector-metadata-card" assetpath="../vz-projector/">
-<template>
-<style>
-#metadata-card {
- background-color: rgba(255,255,255,0.9);
- box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
- 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
- width: 280px;
-}
-
-#header {
- background: #e9e9e9;
-}
-
-#icon-container {
- position: absolute;
- right: 0;
- top: 4px;
-}
-
-#metadata-label {
- font-weight: 400;
- font-size: 14px;
- line-height: 24px;
- padding: 12px 12px 8px;
- width: 230px;
-}
-
-#metadata-table {
- display: table;
- padding: 8px 12px 4px;
-}
-
-.metadata-row {
- display: table-row;
-}
-
-.metadata-key {
- font-weight: bold;
-}
-
-.metadata-key, .metadata-value {
- display: table-cell;
- font-size: 12px;
- padding: 3px 3px;
-}
-</style>
-
-<template is="dom-if" if="[[hasMetadata]]">
- <div id="metadata-card">
- <div id="icon-container">
- <paper-icon-button id="expand-more" style="display: none" icon="expand-more" on-tap="_expandMore"></paper-icon-button>
- <paper-icon-button id="expand-less" on-tap="_expandLess" icon="expand-less"></paper-icon-button>
- </div>
- <div id="header">
- <div id="metadata-label">[[label]]</div>
- </div>
- <iron-collapse id="metadata-container" opened="">
- <div id="metadata-table">
- <template is="dom-repeat" items="[[metadata]]">
- <div class="metadata-row">
- <div class="metadata-key">[[item.key]]</div>
- <div class="metadata-value">[[item.value]]</div>
- </div>
- </template>
- </div>
- </iron-collapse>
- </div>
-</template>
-</template>
-</dom-module>
-<dom-module id="vz-projector-projections-panel" assetpath="../vz-projector/">
-<template>
-<style include="vz-projector-styles"></style>
-<style>
-:host {
- transition: height 0.2s;
-}
-
-.ink-button, ::shadow .ink-button {
- border: none;
- border-radius: 2px;
- font-size: 13px;
- padding: 10px;
- min-width: 100px;
- flex-shrink: 0;
- background: #e3e3e3;
-}
-
-.ink-panel-buttons {
- margin-bottom: 10px;
-}
-
-.two-way-toggle {
- display: flex;
- flex-direction: row;
-}
-
-.two-way-toggle span {
- padding-right: 7px;
-}
-
-.has-border {
- border: 1px solid rgba(0, 0, 0, 0.1);
-}
-
-.toggle {
- min-width: 0px;
- font-size: 12px;
- width: 17px;
- min-height: 0px;
- height: 21px;
- padding: 0;
- margin: 0px;
-}
-
-.toggle[active] {
- background-color: #880E4F;
- color: white;
-}
-
-.two-columns {
- display:flex;
- justify-content: space-between;
-}
-
-.two-columns > :first-child {
- margin-right: 15px;
-}
-
-.two-columns > div {
- width: 50%;
-}
-
-.dropdown-item {
- justify-content: space-between;
- min-height: 35px;
-}
-
-#z-container {
- display: flex;
- align-items: center;
- width: 50%;
-}
-
-#z-checkbox {
- margin: 27px 0 0 5px;
- width: 18px;
-}
-
-#z-dropdown {
- flex-grow: 1;
-}
-
-.notice {
- color: #880E4F;
-}
-
-.container {
- padding: 20px;
-}
-
-.book-icon {
- height: 20px;
- color: rgba(0, 0, 0, 0.7);
-}
-
-.item-details {
- color: gray;
- font-size: 12px;
- margin-left: 5px;
-}
-
-.pca-dropdown {
- width: 100%;
-}
-
-.pca-dropdown paper-listbox {
- width: 135px;
-}
-
-.dropdown-item.header {
- border-bottom: 1px solid #aaa;
- color: #333;
- font-weight: bold;
-}
-
-#total-variance {
- color: rgba(0, 0, 0, 0.7);
-}
-</style>
-<div id="main">
- <div class="ink-panel-header">
- <div class="ink-tab-group">
-
- <div data-tab="tsne" id="tsne-tab" class="ink-tab projection-tab">t-SNE</div>
- <paper-tooltip for="tsne-tab" position="bottom" animation-delay="0" fit-to-visible-bounds="">
- t-distributed stochastic neighbor embedding
- </paper-tooltip>
-
- <div data-tab="pca" id="pca-tab" class="ink-tab projection-tab">PCA</div>
- <paper-tooltip for="pca-tab" position="bottom" animation-delay="0" fit-to-visible-bounds="">
- Principal component analysis
- </paper-tooltip>
-
- <div data-tab="custom" id="custom-tab" class="ink-tab projection-tab" title="Linear projection of two custom vectors">Custom</div>
- <paper-tooltip for="custom-tab" position="bottom" animation-delay="0" fit-to-visible-bounds="">
- Search for two vectors upon which to project all points.
- </paper-tooltip>
-
- </div>
- </div>
- <div class="container">
-
- <div data-panel="tsne" class="ink-panel-content">
- <div class="slider">
- <label>Dimension</label>
- <div class="two-way-toggle">
- <span>2D</span>
- <paper-toggle-button id="tsne-toggle" checked="{{tSNEis3d}}">3D</paper-toggle-button>
- </div>
- </div>
- <div class="slider tsne-perplexity">
- <label>
- Perplexity
- <paper-icon-button icon="help" class="help-icon"></paper-icon-button>
- <paper-tooltip position="right" animation-delay="0" fit-to-visible-bounds="">
- The most appropriate perplexity value depends on the density of the
- data. Loosely speaking, a larger / denser dataset
- requires a larger perplexity. Typical values for perplexity range
- between 5 and 50.
- </paper-tooltip>
- </label>
- <paper-slider id="perplexity-slider" pin="" min="2" max="100" value="30"></paper-slider>
- <span></span>
- </div>
- <div class="slider tsne-learning-rate">
- <label>
- Learning rate
- <paper-icon-button icon="help" class="help-icon"></paper-icon-button>
- <paper-tooltip position="right" animation-delay="0" fit-to-visible-bounds="">
- The ideal learning rate often depends on the size of the data,
- with smaller datasets requiring smaller learning rates.
- </paper-tooltip>
- </label>
- <paper-slider id="learning-rate-slider" snaps="" min="-3" max="2" step="1" value="1" max-markers="6">
- </paper-slider>
- <span></span>
- </div>
- <p>
- <button class="run-tsne ink-button" title="Re-run t-SNE">Re-run</button>
- <button class="stop-tsne ink-button" title="Stop t-SNE">Stop</button>
- </p>
- <p>Iteration: <span class="run-tsne-iter">0</span></p>
- <p id="tsne-sampling" class="notice">
- For fast results, the data will be sampled down to [[getTsneSampleSizeText()]] points.
- </p>
- <p>
- <iron-icon icon="book" class="book-icon"></iron-icon>
- <a target="_blank" href="http://distill.pub/2016/misread-tsne/">
- How to use t-SNE effectively.
- </a>
- </p>
- </div>
-
- <div data-panel="pca" class="ink-panel-content">
- <div class="two-columns">
- <div>
- <paper-dropdown-menu class="pca-dropdown" vertical-align="bottom" no-animations="" label="X">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{pcaX}}">
- <paper-item disabled="" class="dropdown-item header">
- <div>#</div>
- <div>Variance (%)</div>
- </paper-item>
- <template is="dom-repeat" items="[[pcaComponents]]">
- <paper-item class="dropdown-item" value="[[item.id]]" label="Component #[[item.componentNumber]]">
- <div>[[item.componentNumber]]</div>
- <div class="item-details">[[item.percVariance]]</div>
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- <paper-dropdown-menu class="pca-dropdown" no-animations="" vertical-align="bottom" label="Z" disabled="[[!hasPcaZ]]" id="z-dropdown">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{pcaZ}}">
- <paper-item disabled="" class="dropdown-item header">
- <div>#</div>
- <div>Variance (%)</div>
- </paper-item>
- <template is="dom-repeat" items="[[pcaComponents]]">
- <paper-item class="dropdown-item" value="[[item.id]]" label="Component #[[item.componentNumber]]">
- <div>[[item.componentNumber]]</div>
- <div class="item-details">[[item.percVariance]]</div>
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- </div>
- <div>
- <paper-dropdown-menu class="pca-dropdown" vertical-align="bottom" no-animations="" label="Y">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{pcaY}}">
- <paper-item disabled="" class="dropdown-item header">
- <div>#</div>
- <div>Variance (%)</div>
- </paper-item>
- <template is="dom-repeat" items="[[pcaComponents]]">
- <paper-item class="dropdown-item" value="[[item.id]]" label="Component #[[item.componentNumber]]">
- <div>[[item.componentNumber]]</div>
- <div class="item-details">[[item.percVariance]]</div>
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- <paper-checkbox id="z-checkbox" checked="{{pcaIs3d}}"></paper-checkbox>
- </div>
- </div>
- <p id="pca-sampling" class="notice">
- PCA is approximate.
- <paper-icon-button icon="help" class="help-icon"></paper-icon-button>
- </p>
- <div id="total-variance">Total variance</div>
- <paper-tooltip for="pca-sampling" position="top" animation-delay="0" fit-to-visible-bounds="">
- For fast results, the data was sampled to [[getPcaSampleSizeText()]] points and randomly projected down to [[getPcaSampledDimText()]] dimensions.
- </paper-tooltip>
- </div>
-
- <div data-panel="custom" class="ink-panel-content">
- <paper-dropdown-menu style="width: 100%" no-animations="" label="Search by">
- <paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{customSelectedSearchByMetadataOption}}">
- <template is="dom-repeat" items="[[searchByMetadataOptions]]">
- <paper-item class="dropdown-item" value="[[item]]" label="[[item]]">
- [[item]]
- </paper-item>
- </template>
- </paper-listbox>
- </paper-dropdown-menu>
- <div class="two-columns">
- <vz-projector-input id="xLeft" label="Left"></vz-projector-input>
- <vz-projector-input id="xRight" label="Right"></vz-projector-input>
- </div>
- <div class="two-columns">
- <vz-projector-input id="yUp" label="Up"></vz-projector-input>
- <vz-projector-input id="yDown" label="Down"></vz-projector-input>
- </div>
- </div>
- </div>
-</div>
-</template>
-</dom-module>
-<link rel="import" href="../paper-listbox/paper-listbox.html">
-<link rel="import" href="../iron-icons/image-icons.html">
-<link rel="import" href="../paper-toast/paper-toast.html">
-<link rel="import" href="../paper-styles/typography.html">
-<link rel="import" href="../paper-dialog-scrollable/paper-dialog-scrollable.html">
-
-<dom-module id="vz-projector" assetpath="../vz-projector/">
-<template>
-<style include="vz-projector-styles"></style>
-<style>
-:host {
- display: flex;
- width: 100%;
- height: 100%;
-}
-
-#container {
- display: flex;
- width: 100%;
- height: 100%;
- overflow: hidden;
-}
-
-.hidden {
- display: none !important;
-}
-
-/* Main */
-
-#main {
- position: relative;
- flex-grow: 2;
-}
-
-#main .stage {
- position: relative;
- flex-grow: 2;
-}
-
-#scatter {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
-}
-
-#selector {
- display: none;
- height: 100%;
- position: absolute;
- width: 100%;
-}
-
-#left-pane {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- min-width: 312px;
- width: 312px;
- border-right: 1px solid rgba(0, 0, 0, 0.1);
- background: #fafafa;
-}
-
-#right-pane {
- border-left: 1px solid rgba(0, 0, 0, 0.1);
- background: #fafafa;
- display: flex;
- height: 100%;
- min-width: 300px;
- width: 300px;
-}
-
-.file-name {
- margin-right: 5px;
-}
-
-.control input[type=text]:focus {
- outline: none;
- border-bottom: 1px solid rgba(0, 0, 0, 1);
-}
-
-.control {
- display: inline-block;
- width: 45%;
- vertical-align: top;
- margin-right: 10px;
- overflow-x: hidden;
-}
-
-.control.last {
- margin-right: 0;
-}
-
-#notification-dialog {
- width: 400px;
- padding-bottom: 20px;
-}
-
-#notification-dialog paper-button {
- background: none;
- text-transform: uppercase;
-}
-
-#notification-dialog .progress {
- --paper-spinner-color: #880E4F;
- --paper-spinner-stroke-width: 2px;
-}
-
-#notify-msgs {
- text-align: center;
- display: block;
-}
-
-.notify-msg {
- font-weight: 500;
- margin: 0;
- padding: 0;
-}
-
-.notify-msg.error {
- text-align: left;
-}
-
-.brush .extent {
- stroke: #fff;
- fill-opacity: .125;
- shape-rendering: crispEdges;
-}
-
-.origin text {
- font-size: 12px;
- font-weight: 500;
-}
-
-.origin line {
- stroke: black;
- stroke-opacity: 0.2;
-}
-
-/* Ink Framework */
-
-/* - Buttons */
-.ink-button, ::shadow .ink-button {
- border: none;
- border-radius: 2px;
- font-size: 13px;
- padding: 10px;
- min-width: 100px;
- flex-shrink: 0;
- background: #e3e3e3;
-}
-
-.status-bar-panel {
- display: flex;
- align-items: center;
-}
-
-.status-bar-entry {
- border-left: 1px solid rgba(0, 0, 0, 0.5);
- margin-left: 5px;
- padding-left: 5px;
-}
-
-/* - Menubar */
-
-.ink-panel-menubar {
- align-items: center;
- position: relative;
- height: 60px;
- border-bottom: solid 1px #eee;
- padding: 0 24px;
- display: flex;
-}
-
-.ink-panel-menubar .ink-fabs {
- position: absolute;
- right: 12px;
- top: 40px;
- z-index: 1;
-}
-
-#bookmark-panel {
- bottom: 0;
- position: absolute;
- width: 300px;
-}
-#bookmark-panel-container {
- bottom: 60px;
- position: absolute;
-}
-
-.ink-fab {
- margin-left: 8px;
- border: 1px solid rgba(0, 0, 0, 0.02);
- background: white;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
-}
-
-#metadata-card {
- position: absolute;
- right: 5px;
- top: 25px;
-}
-
-#help-3d-icon {
- position: absolute;
- top: 20px;
- left: 20px;
-}
-
-#help3dDialog .main {
- margin: 0;
- padding: 20px;
-}
-
-#help3dDialog h3 {
- margin-top: 20px;
- margin-bottom: 5px;
-}
-
-#help3dDialog h3:first-child {
- margin-top: 0;
-}
-
-#data-panel {
- border-top: 1px solid rgba(0, 0, 0, 0.1);
- overflow-y: auto;
-}
-
-#toast {
- display: flex;
- align-items: center;
- --paper-toast-color: #eeff41;
-}
-</style>
-<paper-dialog id="notification-dialog" modal="">
- <h2 id="notification-title"></h2>
- <paper-dialog-scrollable>
- <div id="notify-msgs"></div>
- </paper-dialog-scrollable>
- <div style="text-align: center;"><paper-spinner-lite active="" class="progress"></paper-spinner-lite></div>
- <div class="buttons">
- <paper-button class="close-button" dialog-confirm="" autofocus="">Close</paper-button>
- </div>
-</paper-dialog>
-<div id="container">
- <div id="left-pane" class="ink-panel">
- <vz-projector-data-panel id="data-panel"></vz-projector-data-panel>
- <vz-projector-projections-panel id="projections-panel"></vz-projector-projections-panel>
- </div>
- <div id="main" class="ink-panel">
- <div class="ink-panel-menubar">
- <paper-icon-button id="selectMode" alt="Bounding box selection" toggles="" icon="image:photo-size-select-small"></paper-icon-button>
- <paper-tooltip for="selectMode" position="bottom" animation-delay="0" fit-to-visible-bounds="">Bounding box selection</paper-tooltip>
-
- <paper-icon-button id="nightDayMode" alt="Enable/disable night mode" toggles="" icon="image:brightness-2"></paper-icon-button>
- <paper-tooltip for="nightDayMode" position="bottom" animation-delay="0" fit-to-visible-bounds="">Enable/disable night mode</paper-tooltip>
-
- <paper-icon-button id="labels3DMode" alt="Enable/disable 3D labels mode" toggles="" icon="font-download"></paper-icon-button>
- <paper-tooltip for="labels3DMode" position="bottom" animation-delay="0" fit-to-visible-bounds="">Enable/disable 3D labels mode</paper-tooltip>
- <div class="status-bar-panel">
- <div class="status-bar-entry">Points: <span class="numDataPoints">Loading...</span></div>
- <div class="status-bar-entry">Dimension: <span class="dim">Loading...</span></div>
- <div id="status-bar" class="status-bar-entry" style="display: none;"></div>
- </div>
- <div class="ink-fabs">
- <paper-icon-button id="reset-zoom" class="ink-fab" alt="Reset zoom to fit all points" icon="home"></paper-icon-button>
- <paper-tooltip for="reset-zoom" position="left" animation-delay="0">Reset zoom to fit all points</paper-tooltip>
- </div>
- </div>
- <div class="stage">
- <div id="scatter">
- <svg id="selector"></svg>
- </div>
- <vz-projector-metadata-card id="metadata-card"></vz-projector-metadata-card>
- <paper-icon-button raised="" onclick="help3dDialog.open()" icon="help-outline" id="help-3d-icon"></paper-icon-button>
- <paper-tooltip animation-delay="0" for="help-3d-icon">Help with interaction controls.</paper-tooltip>
- <paper-dialog id="help3dDialog" with-backdrop="">
- <div class="main" dialog-confirm="" autofocus="">
- <h3>3D controls</h3>
- <b>Rotate</b> Mouse left click.<br>
- <b>Pan</b> Mouse right click.<br>
- <b>Zoom</b> Mouse wheel.<br>
- Holding <b>ctrl</b> reverses the mouse clicks.
- <h3>2D controls</h3>
- <b>Pan</b> Mouse left click.<br>
- <b>Zoom</b> Mouse wheel.
- <div class="dismiss-dialog-note"> Click anywhere to dismiss.</div>
- </div>
- </paper-dialog>
- </div>
- </div>
- <div id="right-pane" class="ink-panel">
- <div class="ink-panel-content active">
- <vz-projector-inspector-panel id="inspector-panel"></vz-projector-inspector-panel>
- </div>
- <div id="bookmark-panel-container">
- <vz-projector-bookmark-panel id="bookmark-panel"></vz-projector-bookmark-panel>
- </div>
- </div>
-</div>
-<paper-toast id="toast" always-on-top=""></paper-toast>
-
-</template>
-</dom-module>
-<dom-module id="vz-projector-dashboard" assetpath="../vz-projector/">
-<template>
- <tf-no-data-warning data-type="projector" show-warning="[[dataNotFound]]"></tf-no-data-warning>
- <template is="dom-if" if="[[!dataNotFound]]">
- <vz-projector id="projector" route-prefix="[[routePrefix]]" serving-mode="server" page-view-logging="" event-logging=""></vz-projector>
- </template>
-</template>
-<script>
-(function() {
-TF.Dashboard.VzProjectorDashboard = Polymer({
- is: 'vz-projector-dashboard',
- factoryImpl: function(routePrefix) {
- this.routePrefix = routePrefix;
- },
- properties: {
- dataNotFound: Boolean,
- routePrefix: String,
- // Whether this dashboard is initialized. This dashboard should only be initialized once.
- _initialized: Boolean,
- },
- behaviors: [
- TF.Dashboard.DashboardBehavior("embeddings"),
- ],
- reload: function() {
- // Do not reload the embedding projector. Reloading could take a long time.
- },
- attached: function() {
- if (this._initialized) {
- return;
- }
-
- // Set this to true so we only initialize once.
- this._initialized = true;
-
- d3.json(this.routePrefix + '/runs', (err, runs) => {
- this.set('dataNotFound', runs.length === 0);
- });
- },
-});
-})();
-</script>
-</dom-module>
-<script>/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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 TensorBoard;
- (function (TensorBoard) {
- TensorBoard.AUTORELOAD_LOCALSTORAGE_KEY = 'TF.TensorBoard.autoReloadEnabled';
- var getAutoReloadFromLocalStorage = function () {
- var val = window.localStorage.getItem(TensorBoard.AUTORELOAD_LOCALSTORAGE_KEY);
- return val === 'true' || val == null; // defaults to true
- };
- TensorBoard.AutoReloadBehavior = {
- properties: {
- autoReloadEnabled: {
- type: Boolean,
- observer: '_autoReloadObserver',
- value: getAutoReloadFromLocalStorage,
- },
- _autoReloadId: {
- type: Number,
- },
- autoReloadIntervalSecs: {
- type: Number,
- value: 30,
- },
- },
- detached: function () {
- window.clearTimeout(this._autoReloadId);
- },
- _autoReloadObserver: function (autoReload) {
- window.localStorage.setItem(TensorBoard.AUTORELOAD_LOCALSTORAGE_KEY, autoReload);
- if (autoReload) {
- var _this = this;
- this._autoReloadId = window.setTimeout(this._doAutoReload.bind(this), this.autoReloadIntervalSecs * 1000);
- }
- else {
- window.clearTimeout(this._autoReloadId);
- }
- },
- _doAutoReload: function () {
- if (this.reload == null) {
- throw new Error('AutoReloadBehavior requires a reload method');
- }
- this.reload();
- this._autoReloadId = window.setTimeout(this._doAutoReload.bind(this), this.autoReloadIntervalSecs * 1000);
- }
- };
- })(TensorBoard = TF.TensorBoard || (TF.TensorBoard = {}));
-})(TF || (TF = {}));
-</script></div><dom-module id="tf-tensorboard">
- <template>
- <paper-dialog with-backdrop="" id="settings">
- <h2>Settings</h2>
- <paper-checkbox id="auto-reload-checkbox" checked="{{autoReloadEnabled}}">
- Reload data every <span>[[autoReloadIntervalSecs]]</span>s.
- </paper-checkbox>
- </paper-dialog>
- <paper-header-panel>
- <paper-toolbar id="toolbar">
- <div id="toolbar-content">
- <div class="toolbar-title">TensorBoard</div>
- <paper-tabs selected="{{modeIndex}}" noink="" class="tabs" id="tabs">
- <template is="dom-repeat" items="[[_dashboards]]">
- <template is="dom-if" if="[[_isTabEnabled(item.name)]]">
- <paper-tab data-mode="[[item.name]]">[[item.name]]</paper-tab>
- </template>
- </template>
- </paper-tabs>
- <div class="global-actions">
- <paper-icon-button icon="refresh" on-tap="reload" disabled$="[[_isReloadDisabled(mode)]]" id="reload-button"></paper-icon-button>
- <paper-icon-button icon="settings" on-tap="openSettings" id="settings-button"></paper-icon-button>
- <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tensorboard/README.md" tabindex="-1">
- <paper-icon-button icon="help-outline"></paper-icon-button>
- </a>
- </div>
- </div>
- </paper-toolbar>
-
- <div id="content" class="fit"></div>
- </paper-header-panel>
-
- <style>
- :host {
- height: 100%;
- display: block;
- background-color: var(--paper-grey-100);
- }
-
- #toolbar {
- background-color: var(--tb-toolbar-background-color, --tb-orange-strong);
- -webkit-font-smoothing: antialiased;
- }
-
- .toolbar-title {
- font-size: 20px;
- margin-left: 10px;
- text-rendering: optimizeLegibility;
- letter-spacing: -0.025em;
- font-weight: 500;
- flex-grow: 2;
- display: var(--tb-toolbar-title-display, block);
- }
-
- .tabs {
- flex-grow: 1;
- text-transform: uppercase;
- height: 100%;
- }
-
- paper-tabs {
- --paper-tabs-selection-bar-color: white;
- }
-
- .global-actions {
- flex-grow: 2;
- display: inline-flex; /* Ensure that icons stay aligned */
- justify-content: flex-end;
- text-align: right;
- color: white;
- }
-
- .global-actions a {
- color: white;
- }
-
- #toolbar-content {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- }
-
- #content {
- height: 100%;
- }
-
- [disabled] {
- opacity: 0.2;
- color: white;
- }
-
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-tensorboard",
- behaviors: [TF.TensorBoard.AutoReloadBehavior],
- properties: {
- router: {
- type: Object,
- value: function() {
- return TF.Backend.router();
- },
- },
- _backend: {
- type: Object,
- computed: "_makeBackend(router, demoDir)",
- },
- _debuggerDataEnabled: {
- type: Boolean,
- value: function() {
- // For now, Tensorboard only shows debugger data if the debugger_data GET param is set
- // to enabled.
- let match = window.location.href.match(/[&\?]debugger_data=enabled/);
- return match && match.length == 1;
- },
- },
- _dashboards: {
- type: Array,
- computed: "_makeDashboardList(_backend, router, _debuggerDataEnabled)",
- },
- // Maps dashboard name to dashboard object.
- _dashboardMapping: {
- type: Object,
- computed: "_makeDashboardMapping(_dashboards)",
- },
- // Which tab is selected (scalars, graph, images etc).
- mode: {
- type: String,
- computed: '_getModeFromIndex(modeIndex)',
- notify: true,
- observer: '_modeChanged',
- },
- // If this is set to a string, TensorBoard will switch to "demo mode"
- // and attempt to load serialized json data from that directory. You can
- // generate conformant json using
- // tensorboard/scripts/serialize_tensorboard.py
- demoDir: {
- type: String,
- value: null,
- },
- // Set this to true to store state in URI hash. Should be true for all non-test purposes.
- useHash: {
- type: Boolean,
- value: false,
- },
- disabledTabs: String,
- },
- _isTabEnabled: function(tab) {
- if (this.disabledTabs != null &&
- this.disabledTabs.split(',').indexOf(tab) >= 0) {
- return false;
- }
- return true;
- },
- _getModeFromIndex: function(modeIndex) {
- var mode = this._dashboards[modeIndex].name;
- TF.URIStorage.setString(TF.URIStorage.TAB, mode);
- return mode;
- },
- _makeBackend: function(router, demoDir) {
- // use the demoDir if it is set, otherwise use the provided router
- if (demoDir != null) {
- router = TF.Backend.router(demoDir, true);
- }
- return new TF.Backend.Backend(router);
- },
- _isReloadDisabled: function(mode) {
- return !this._debuggerDataEnabled && mode == 'graphs';
- },
- selectedDashboard: function() {
- var dashboard = this._dashboardMapping[this.mode];
- if (dashboard == null) {
- throw new Error(`Unable to find dashboard for mode: ${this.mode}`);
- }
- return dashboard;
- },
- ready: function() {
- TF.Globals.USE_HASH = this.useHash;
-
- this._getModeFromHash();
- window.addEventListener('hashchange', function() {
- this._getModeFromHash();
- }.bind(this));
- },
- _makeDashboardList: function(backend, router, debuggerDataEnabled) {
- if (!backend || !router) {
- // The dashboards require these entities. We are not ready to construct dashboards.
- return null;
- }
-
- return [
- new TF.Dashboard.TfScalarDashboard(backend, router),
- new TF.Dashboard.TfImageDashboard(backend),
- new TF.Dashboard.TfAudioDashboard(backend),
- new TF.Dashboard.TfGraphDashboard(backend, debuggerDataEnabled),
- new TF.Dashboard.TfDistributionDashboard(backend),
- new TF.Dashboard.TfHistogramDashboard(backend),
- new TF.Dashboard.VzProjectorDashboard('data/plugin/projector'),
- new TF.Dashboard.TfTextDashboard(backend),
- ];
- },
- _makeDashboardMapping: function(dashboards) {
- if (!dashboards) {
- return null;
- }
-
- let mapping = {};
- dashboards.forEach(function(dashboard) {
- mapping[dashboard.name] = dashboard;
- });
- return mapping;
- },
- _getModeFromHash: function() {
- var tabName = TF.URIStorage.getString(TF.URIStorage.TAB);
- var modeIndex;
- for (var i = 0; i < this._dashboards.length; i++) {
- if (this._dashboards[i].name == tabName) {
- modeIndex = i;
- break;
- }
- }
-
- if (modeIndex === undefined && this.modeIndex == null) {
- // Select the first tab as default.
- this.set('modeIndex', 0);
- }
- if (modeIndex !== undefined && modeIndex != this.modeIndex) {
- this.set('modeIndex', modeIndex);
- }
- },
- _modeChanged: function(mode) {
- let currentDashboard = this.$.content.firstChild;
- if (currentDashboard) {
- this.$.content.removeChild(currentDashboard);
- }
-
- if (!mode || !this._dashboardMapping) {
- return;
- }
-
- // Append the new dashboard.
- const newDashboard = this.selectedDashboard();
- this.$.content.appendChild(newDashboard);
- },
- reload: function() {
- this.selectedDashboard().reload();
- },
- openSettings: function() {
- this.$.settings.open();
- },
- });
- </script>
-
- <script>(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-"use strict";
-var AnalyticsLogger = (function () {
- /**
- * Constructs an event logger using Google Analytics. It assumes there is a
- * Google Analytics script added to the page elsewhere. If there is no such
- * script, the logger acts as a no-op.
- *
- * @param pageViewLogging Whether to log page views.
- * @param eventLogging Whether to log user interaction.
- */
- function AnalyticsLogger(pageViewLogging, eventLogging) {
- if (typeof ga === 'undefined' || ga == null) {
- this.eventLogging = false;
- this.pageViewLogging = false;
- return;
- }
- this.eventLogging = eventLogging;
- this.pageViewLogging = pageViewLogging;
- }
- AnalyticsLogger.prototype.logPageView = function (pageTitle) {
- if (this.pageViewLogging) {
- // Always send a page view.
- ga('send', { hitType: 'pageview', page: "/v/" + pageTitle });
- }
- };
- AnalyticsLogger.prototype.logProjectionChanged = function (projection) {
- if (this.eventLogging) {
- ga('send', {
- hitType: 'event',
- eventCategory: 'Projection',
- eventAction: 'click',
- eventLabel: projection
- });
- }
- };
- AnalyticsLogger.prototype.logWebGLDisabled = function () {
- if (this.eventLogging) {
- ga('send', {
- hitType: 'event',
- eventCategory: 'Error',
- eventAction: 'PageLoad',
- eventLabel: 'WebGL_disabled'
- });
- }
- };
- return AnalyticsLogger;
-}());
-exports.AnalyticsLogger = AnalyticsLogger;
-
-},{}],2:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-/**
- * This is a fork of the Karpathy's TSNE.js (original license below).
- * This fork implements Barnes-Hut approximation and runs in O(NlogN)
- * time, as opposed to the Karpathy's O(N^2) version.
- *
- * @author smilkov@google.com (Daniel Smilkov)
- */
-/**
- * The MIT License (MIT)
- * Copyright (c) 2015 Andrej Karpathy
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-var sptree_1 = require('./sptree');
-/**
- * Barnes-hut approximation level. Higher means more approximation and faster
- * results. Recommended value mentioned in the paper is 0.8.
- */
-var THETA = 0.8;
-var MIN_POSSIBLE_PROB = 1E-9;
-// Variables used for memorizing the second random number since running
-// gaussRandom() generates two random numbers at the cost of 1 atomic
-// computation. This optimization results in 2X speed-up of the generator.
-var return_v = false;
-var v_val = 0.0;
-/** Returns the square euclidean distance between two vectors. */
-function dist2(a, b) {
- if (a.length !== b.length) {
- throw new Error('Vectors a and b must be of same length');
- }
- var result = 0;
- for (var i = 0; i < a.length; ++i) {
- var diff = a[i] - b[i];
- result += diff * diff;
- }
- return result;
-}
-exports.dist2 = dist2;
-/** Returns the square euclidean distance between two 2D points. */
-function dist2_2D(a, b) {
- var dX = a[0] - b[0];
- var dY = a[1] - b[1];
- return dX * dX + dY * dY;
-}
-exports.dist2_2D = dist2_2D;
-/** Returns the square euclidean distance between two 3D points. */
-function dist2_3D(a, b) {
- var dX = a[0] - b[0];
- var dY = a[1] - b[1];
- var dZ = a[2] - b[2];
- return dX * dX + dY * dY + dZ * dZ;
-}
-exports.dist2_3D = dist2_3D;
-function gaussRandom(rng) {
- if (return_v) {
- return_v = false;
- return v_val;
- }
- var u = 2 * rng() - 1;
- var v = 2 * rng() - 1;
- var r = u * u + v * v;
- if (r === 0 || r > 1) {
- return gaussRandom(rng);
- }
- var c = Math.sqrt(-2 * Math.log(r) / r);
- v_val = v * c; // cache this for next function call for efficiency
- return_v = true;
- return u * c;
-}
-;
-// return random normal number
-function randn(rng, mu, std) {
- return mu + gaussRandom(rng) * std;
-}
-;
-// utilitity that creates contiguous vector of zeros of size n
-function zeros(n) {
- return new Float64Array(n);
-}
-;
-// utility that returns a matrix filled with random numbers
-// generated by the provided generator.
-function randnMatrix(n, d, rng) {
- var nd = n * d;
- var x = zeros(nd);
- for (var i = 0; i < nd; ++i) {
- x[i] = randn(rng, 0.0, 1E-4);
- }
- return x;
-}
-;
-// utility that returns a matrix filled with the provided value.
-function arrayofs(n, d, val) {
- var x = [];
- for (var i = 0; i < n; ++i) {
- x.push(d === 3 ? [val, val, val] : [val, val]);
- }
- return x;
-}
-;
-// compute (p_{i|j} + p_{j|i})/(2n)
-function nearest2P(nearest, perplexity, tol) {
- var N = nearest.length;
- var Htarget = Math.log(perplexity); // target entropy of distribution
- var P = zeros(N * N); // temporary probability matrix
- var K = nearest[0].length;
- var pRow = new Array(K); // pij[].
- for (var i = 0; i < N; ++i) {
- var neighbors = nearest[i];
- var betaMin = -Infinity;
- var betaMax = Infinity;
- var beta = 1; // initial value of precision
- var maxTries = 50;
- // perform binary search to find a suitable precision beta
- // so that the entropy of the distribution is appropriate
- var numTries = 0;
- while (true) {
- // compute entropy and kernel row with beta precision
- var psum = 0.0;
- for (var k = 0; k < neighbors.length; ++k) {
- var neighbor = neighbors[k];
- var pij = (i === neighbor.index) ? 0 : Math.exp(-neighbor.dist * beta);
- pij = Math.max(pij, MIN_POSSIBLE_PROB);
- pRow[k] = pij;
- psum += pij;
- }
- // normalize p and compute entropy
- var Hhere = 0.0;
- for (var k = 0; k < pRow.length; ++k) {
- pRow[k] /= psum;
- var pij = pRow[k];
- if (pij > 1E-7) {
- Hhere -= pij * Math.log(pij);
- }
- ;
- }
- // adjust beta based on result
- if (Hhere > Htarget) {
- // entropy was too high (distribution too diffuse)
- // so we need to increase the precision for more peaky distribution
- betaMin = beta; // move up the bounds
- if (betaMax === Infinity) {
- beta = beta * 2;
- }
- else {
- beta = (beta + betaMax) / 2;
- }
- }
- else {
- // converse case. make distrubtion less peaky
- betaMax = beta;
- if (betaMin === -Infinity) {
- beta = beta / 2;
- }
- else {
- beta = (beta + betaMin) / 2;
- }
- }
- numTries++;
- // stopping conditions: too many tries or got a good precision
- if (numTries >= maxTries || Math.abs(Hhere - Htarget) < tol) {
- break;
- }
- }
- // copy over the final prow to P at row i
- for (var k = 0; k < pRow.length; ++k) {
- var pij = pRow[k];
- var j = neighbors[k].index;
- P[i * N + j] = pij;
- }
- } // end loop over examples i
- // symmetrize P and normalize it to sum to 1 over all ij
- var N2 = N * 2;
- for (var i = 0; i < N; ++i) {
- for (var j = i + 1; j < N; ++j) {
- var i_j = i * N + j;
- var j_i = j * N + i;
- var value = (P[i_j] + P[j_i]) / N2;
- P[i_j] = value;
- P[j_i] = value;
- }
- }
- return P;
-}
-;
-// helper function
-function sign(x) {
- return x > 0 ? 1 : x < 0 ? -1 : 0;
-}
-function computeForce_2d(force, mult, pointA, pointB) {
- force[0] += mult * (pointA[0] - pointB[0]);
- force[1] += mult * (pointA[1] - pointB[1]);
-}
-function computeForce_3d(force, mult, pointA, pointB) {
- force[0] += mult * (pointA[0] - pointB[0]);
- force[1] += mult * (pointA[1] - pointB[1]);
- force[2] += mult * (pointA[2] - pointB[2]);
-}
-var TSNE = (function () {
- function TSNE(opt) {
- this.iter = 0;
- opt = opt || { dim: 2 };
- this.perplexity = opt.perplexity || 30;
- this.epsilon = opt.epsilon || 10;
- this.rng = opt.rng || Math.random;
- this.dim = opt.dim;
- if (opt.dim === 2) {
- this.dist2 = dist2_2D;
- this.computeForce = computeForce_2d;
- }
- else if (opt.dim === 3) {
- this.dist2 = dist2_3D;
- this.computeForce = computeForce_3d;
- }
- else {
- throw new Error('Only 2D and 3D is supported');
- }
- }
- // this function takes a fattened distance matrix and creates
- // matrix P from them.
- // D is assumed to be provided as an array of size N^2.
- TSNE.prototype.initDataDist = function (nearest) {
- var N = nearest.length;
- this.nearest = nearest;
- this.P = nearest2P(nearest, this.perplexity, 1E-4);
- this.N = N;
- this.initSolution(); // refresh this
- };
- // (re)initializes the solution to random
- TSNE.prototype.initSolution = function () {
- // generate random solution to t-SNE
- this.Y = randnMatrix(this.N, this.dim, this.rng); // the solution
- this.gains = arrayofs(this.N, this.dim, 1.0); // step gains
- // to accelerate progress in unchanging directions
- this.ystep = arrayofs(this.N, this.dim, 0.0); // momentum accumulator
- this.iter = 0;
- };
- // return pointer to current solution
- TSNE.prototype.getSolution = function () { return this.Y; };
- // perform a single step of optimization to improve the embedding
- TSNE.prototype.step = function () {
- this.iter += 1;
- var N = this.N;
- var grad = this.costGrad(this.Y); // evaluate gradient
- // perform gradient step
- var ymean = this.dim === 3 ? [0, 0, 0] : [0, 0];
- for (var i = 0; i < N; ++i) {
- for (var d = 0; d < this.dim; ++d) {
- var gid = grad[i][d];
- var sid = this.ystep[i][d];
- var gainid = this.gains[i][d];
- // compute gain update
- var newgain = sign(gid) === sign(sid) ? gainid * 0.8 : gainid + 0.2;
- if (newgain < 0.01) {
- newgain = 0.01; // clamp
- }
- this.gains[i][d] = newgain; // store for next turn
- // compute momentum step direction
- var momval = this.iter < 250 ? 0.5 : 0.8;
- var newsid = momval * sid - this.epsilon * newgain * grad[i][d];
- this.ystep[i][d] = newsid; // remember the step we took
- // step!
- var i_d = i * this.dim + d;
- this.Y[i_d] += newsid;
- ymean[d] += this.Y[i_d]; // accumulate mean so that we
- }
- }
- // reproject Y to be zero mean
- for (var i = 0; i < N; ++i) {
- for (var d = 0; d < this.dim; ++d) {
- this.Y[i * this.dim + d] -= ymean[d] / N;
- }
- }
- };
- // return cost and gradient, given an arrangement
- TSNE.prototype.costGrad = function (Y) {
- var _this = this;
- var N = this.N;
- var P = this.P;
- // Trick that helps with local optima.
- var alpha = this.iter < 100 ? 4 : 1;
- // Make data for the SP tree.
- var points = new Array(N); // (x, y)[]
- for (var i = 0; i < N; ++i) {
- var iTimesD = i * this.dim;
- var row = new Array(this.dim);
- for (var d = 0; d < this.dim; ++d) {
- row[d] = Y[iTimesD + d];
- }
- points[i] = row;
- }
- // Make a tree.
- var tree = new sptree_1.SPTree(points);
- var root = tree.root;
- // Annotate the tree.
- var annotateTree = function (node) {
- var numCells = 1;
- if (node.children == null) {
- // Update the current node and tell the parent.
- node.numCells = numCells;
- node.yCell = node.point;
- return { numCells: numCells, yCell: node.yCell };
- }
- // node.point is a 2 or 3-dim number[], so slice() makes a copy.
- var yCell = node.point.slice();
- for (var i = 0; i < node.children.length; ++i) {
- var child = node.children[i];
- if (child == null) {
- continue;
- }
- var result = annotateTree(child);
- numCells += result.numCells;
- for (var d = 0; d < _this.dim; ++d) {
- yCell[d] += result.yCell[d];
- }
- }
- // Update the node and tell the parent.
- node.numCells = numCells;
- node.yCell = yCell.map(function (v) { return v / numCells; });
- return { numCells: numCells, yCell: yCell };
- };
- // Augment the tree with more info.
- annotateTree(root);
- tree.visit(function (node, low, high) {
- node.rCell = high[0] - low[0];
- return false;
- });
- // compute current Q distribution, unnormalized first
- var grad = [];
- var Z = 0;
- var forces = new Array(N);
- var _loop_1 = function(i) {
- var pointI = points[i];
- // Compute the positive forces for the i-th node.
- var Fpos = this_1.dim === 3 ? [0, 0, 0] : [0, 0];
- var neighbors = this_1.nearest[i];
- for (var k = 0; k < neighbors.length; ++k) {
- var j = neighbors[k].index;
- var pij = P[i * N + j];
- var pointJ = points[j];
- var squaredDistItoJ = this_1.dist2(pointI, pointJ);
- var premult = pij / (1 + squaredDistItoJ);
- this_1.computeForce(Fpos, premult, pointI, pointJ);
- }
- // Compute the negative forces for the i-th node.
- var FnegZ = this_1.dim === 3 ? [0, 0, 0] : [0, 0];
- tree.visit(function (node) {
- var squaredDistToCell = _this.dist2(pointI, node.yCell);
- // Squared distance from point i to cell.
- if (node.children == null ||
- (squaredDistToCell > 0 &&
- node.rCell / Math.sqrt(squaredDistToCell) < THETA)) {
- var qijZ_1 = 1 / (1 + squaredDistToCell);
- var dZ = node.numCells * qijZ_1;
- Z += dZ;
- dZ *= qijZ_1;
- _this.computeForce(FnegZ, dZ, pointI, node.yCell);
- return true;
- }
- // Cell is too close to approximate.
- var squaredDistToPoint = _this.dist2(pointI, node.point);
- var qijZ = 1 / (1 + squaredDistToPoint);
- Z += qijZ;
- qijZ *= qijZ;
- _this.computeForce(FnegZ, qijZ, pointI, node.point);
- return false;
- }, true);
- forces[i] = [Fpos, FnegZ];
- };
- var this_1 = this;
- for (var i = 0; i < N; ++i) {
- _loop_1(i);
- }
- // Normalize the negative forces and compute the gradient.
- var A = 4 * alpha;
- var B = 4 / Z;
- for (var i = 0; i < N; ++i) {
- var _a = forces[i], FPos = _a[0], FNegZ = _a[1];
- var gsum = new Array(this.dim);
- for (var d = 0; d < this.dim; ++d) {
- gsum[d] = A * FPos[d] - B * FNegZ[d];
- }
- grad.push(gsum);
- }
- return grad;
- };
- return TSNE;
-}());
-exports.TSNE = TSNE;
-
-},{"./sptree":23}],3:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var data_1 = require('./data');
-var data_provider_1 = require('./data-provider');
-var dataProvider = require('./data-provider');
-var logging = require('./logging');
-var BYTES_EXTENSION = '.bytes';
-/** Data provider that loads data from a demo folder. */
-var DemoDataProvider = (function () {
- function DemoDataProvider(projectorConfigPath) {
- this.projectorConfigPath = projectorConfigPath;
- }
- DemoDataProvider.prototype.getEmbeddingInfo = function (tensorName) {
- var embeddings = this.projectorConfig.embeddings;
- for (var i = 0; i < embeddings.length; i++) {
- var embedding = embeddings[i];
- if (embedding.tensorName === tensorName) {
- return embedding;
- }
- }
- return null;
- };
- DemoDataProvider.prototype.retrieveRuns = function (callback) {
- callback(['Demo']);
- };
- DemoDataProvider.prototype.retrieveProjectorConfig = function (run, callback) {
- var _this = this;
- var msgId = logging.setModalMessage('Fetching projector config...');
- d3.json(this.projectorConfigPath, function (err, projectorConfig) {
- if (err) {
- var errorMessage = err;
- // If the error is a valid XMLHttpResponse, it's possible this is a
- // cross-origin error.
- if (err.responseText != null) {
- errorMessage = 'Cannot fetch projector config, possibly a ' +
- 'Cross-Origin request error.';
- }
- logging.setErrorMessage(errorMessage, 'fetching projector config');
- return;
- }
- logging.setModalMessage(null, msgId);
- _this.projectorConfig = projectorConfig;
- callback(projectorConfig);
- });
- };
- DemoDataProvider.prototype.retrieveTensor = function (run, tensorName, callback) {
- var embedding = this.getEmbeddingInfo(tensorName);
- var url = "" + embedding.tensorPath;
- if (embedding.tensorPath.substr(-1 * BYTES_EXTENSION.length) ===
- BYTES_EXTENSION) {
- dataProvider.retrieveTensorAsBytes(this, this.getEmbeddingInfo(tensorName), run, tensorName, url, callback);
- }
- else {
- logging.setModalMessage('Fetching tensors...', data_provider_1.TENSORS_MSG_ID);
- var request_1 = new XMLHttpRequest();
- request_1.open('GET', url);
- request_1.responseType = 'arraybuffer';
- request_1.onerror = function () {
- logging.setErrorMessage(request_1.responseText, 'fetching tensors');
- };
- request_1.onload = function () {
- dataProvider.parseTensors(request_1.response).then(function (points) {
- callback(new data_1.DataSet(points));
- });
- };
- request_1.send();
- }
- };
- DemoDataProvider.prototype.retrieveSpriteAndMetadata = function (run, tensorName, callback) {
- var embedding = this.getEmbeddingInfo(tensorName);
- var spriteImagePath = null;
- if (embedding.sprite && embedding.sprite.imagePath) {
- spriteImagePath = embedding.sprite.imagePath;
- }
- dataProvider.retrieveSpriteAndMetadataInfo(embedding.metadataPath, spriteImagePath, embedding.sprite, callback);
- };
- DemoDataProvider.prototype.getBookmarks = function (run, tensorName, callback) {
- var embedding = this.getEmbeddingInfo(tensorName);
- var msgId = logging.setModalMessage('Fetching bookmarks...');
- d3.json(embedding.bookmarksPath, function (err, bookmarks) {
- if (err) {
- logging.setErrorMessage(err.responseText);
- return;
- }
- logging.setModalMessage(null, msgId);
- callback(bookmarks);
- });
- };
- return DemoDataProvider;
-}());
-exports.DemoDataProvider = DemoDataProvider;
-
-},{"./data":7,"./data-provider":6,"./logging":12}],4:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var data_1 = require('./data');
-var data_provider_1 = require('./data-provider');
-var ProtoDataProvider = (function () {
- function ProtoDataProvider(dataProto) {
- this.dataProto = dataProto;
- }
- ProtoDataProvider.prototype.retrieveRuns = function (callback) {
- callback(['proto']);
- };
- ProtoDataProvider.prototype.retrieveProjectorConfig = function (run, callback) {
- callback({
- modelCheckpointPath: 'proto',
- embeddings: [{
- tensorName: 'proto',
- tensorShape: this.dataProto.shape,
- metadataPath: 'proto'
- }]
- });
- };
- ProtoDataProvider.prototype.retrieveTensor = function (run, tensorName, callback) {
- callback(this.flatArrayToDataset(this.dataProto.tensor));
- };
- ProtoDataProvider.prototype.retrieveSpriteAndMetadata = function (run, tensorName, callback) {
- var columnNames = this.dataProto.metadata.columns.map(function (c) { return c.name; });
- var n = this.dataProto.shape[0];
- var pointsMetadata = new Array(n);
- this.dataProto.metadata.columns.forEach(function (c) {
- var values = c.numericValues || c.stringValues;
- for (var i = 0; i < n; i++) {
- pointsMetadata[i] = pointsMetadata[i] || {};
- pointsMetadata[i][c.name] = values[i];
- }
- });
- callback({
- stats: data_provider_1.analyzeMetadata(columnNames, pointsMetadata),
- pointsInfo: pointsMetadata
- });
- };
- ProtoDataProvider.prototype.getBookmarks = function (run, tensorName, callback) {
- return callback([]);
- };
- ProtoDataProvider.prototype.flatArrayToDataset = function (tensor) {
- var points = [];
- var n = this.dataProto.shape[0];
- var d = this.dataProto.shape[1];
- if (n * d !== tensor.length) {
- throw 'The shape doesn\'t match the length of the flattened array';
- }
- for (var i = 0; i < n; i++) {
- var offset = i * d;
- points.push({
- vector: new Float32Array(tensor.slice(offset, offset + d)),
- metadata: {},
- projections: null,
- index: i
- });
- }
- return new data_1.DataSet(points);
- };
- return ProtoDataProvider;
-}());
-exports.ProtoDataProvider = ProtoDataProvider;
-
-},{"./data":7,"./data-provider":6}],5:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var dataProvider = require('./data-provider');
-var logging = require('./logging');
-// Limit for the number of data points we receive from the server.
-exports.LIMIT_NUM_POINTS = 100000;
-/**
- * Data provider that loads data provided by a python server (usually backed
- * by a checkpoint file).
- */
-var ServerDataProvider = (function () {
- function ServerDataProvider(routePrefix) {
- this.runProjectorConfigCache = {};
- this.routePrefix = routePrefix;
- }
- ServerDataProvider.prototype.getEmbeddingInfo = function (run, tensorName, callback) {
- this.retrieveProjectorConfig(run, function (config) {
- var embeddings = config.embeddings;
- for (var i = 0; i < embeddings.length; i++) {
- var embedding = embeddings[i];
- if (embedding.tensorName === tensorName) {
- callback(embedding);
- return;
- }
- }
- callback(null);
- });
- };
- ServerDataProvider.prototype.retrieveRuns = function (callback) {
- var msgId = logging.setModalMessage('Fetching runs...');
- d3.json(this.routePrefix + "/runs", function (err, runs) {
- if (err) {
- logging.setErrorMessage(err.responseText, 'fetching runs');
- return;
- }
- logging.setModalMessage(null, msgId);
- callback(runs);
- });
- };
- ServerDataProvider.prototype.retrieveProjectorConfig = function (run, callback) {
- var _this = this;
- if (run in this.runProjectorConfigCache) {
- callback(this.runProjectorConfigCache[run]);
- return;
- }
- var msgId = logging.setModalMessage('Fetching projector config...');
- d3.json(this.routePrefix + "/info?run=" + run, function (err, config) {
- if (err) {
- logging.setErrorMessage(err.responseText, 'fetching projector config');
- return;
- }
- logging.setModalMessage(null, msgId);
- _this.runProjectorConfigCache[run] = config;
- callback(config);
- });
- };
- ServerDataProvider.prototype.retrieveTensor = function (run, tensorName, callback) {
- var _this = this;
- this.getEmbeddingInfo(run, tensorName, function (embedding) {
- dataProvider.retrieveTensorAsBytes(_this, embedding, run, tensorName, (_this.routePrefix + "/tensor?run=" + run + "&name=" + tensorName) +
- ("&num_rows=" + exports.LIMIT_NUM_POINTS), callback);
- });
- };
- ServerDataProvider.prototype.retrieveSpriteAndMetadata = function (run, tensorName, callback) {
- var _this = this;
- this.getEmbeddingInfo(run, tensorName, function (embedding) {
- var metadataPath = null;
- if (embedding.metadataPath) {
- metadataPath =
- (_this.routePrefix + "/metadata?") +
- ("run=" + run + "&name=" + tensorName + "&num_rows=" + exports.LIMIT_NUM_POINTS);
- }
- var spriteImagePath = null;
- if (embedding.sprite && embedding.sprite.imagePath) {
- spriteImagePath =
- _this.routePrefix + "/sprite_image?run=" + run + "&name=" + tensorName;
- }
- dataProvider.retrieveSpriteAndMetadataInfo(metadataPath, spriteImagePath, embedding.sprite, callback);
- });
- };
- ServerDataProvider.prototype.getBookmarks = function (run, tensorName, callback) {
- var msgId = logging.setModalMessage('Fetching bookmarks...');
- d3.json(this.routePrefix + "/bookmarks?run=" + run + "&name=" + tensorName, function (err, bookmarks) {
- logging.setModalMessage(null, msgId);
- if (!err) {
- callback(bookmarks);
- }
- });
- };
- return ServerDataProvider;
-}());
-exports.ServerDataProvider = ServerDataProvider;
-
-},{"./data-provider":6,"./logging":12}],6:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var data_1 = require('./data');
-var logging = require('./logging');
-var util_1 = require('./util');
-/** Maximum number of colors supported in the color map. */
-var NUM_COLORS_COLOR_MAP = 50;
-var MAX_SPRITE_IMAGE_SIZE_PX = 8192;
-exports.METADATA_MSG_ID = 'metadata';
-exports.TENSORS_MSG_ID = 'tensors';
-function retrieveTensorAsBytes(dp, embedding, run, tensorName, tensorsPath, callback) {
- // Get the tensor.
- logging.setModalMessage('Fetching tensor values...', exports.TENSORS_MSG_ID);
- var xhr = new XMLHttpRequest();
- xhr.open('GET', tensorsPath);
- xhr.responseType = 'arraybuffer';
- xhr.onprogress = function (ev) {
- if (ev.lengthComputable) {
- var percent = (ev.loaded * 100 / ev.total).toFixed(1);
- logging.setModalMessage('Fetching tensor values: ' + percent + '%', exports.TENSORS_MSG_ID);
- }
- };
- xhr.onload = function () {
- if (xhr.status !== 200) {
- var msg = String.fromCharCode.apply(null, new Uint8Array(xhr.response));
- logging.setErrorMessage(msg, 'fetching tensors');
- return;
- }
- var data;
- try {
- data = new Float32Array(xhr.response);
- }
- catch (e) {
- logging.setErrorMessage(e, 'parsing tensor bytes');
- return;
- }
- var dim = embedding.tensorShape[1];
- var N = data.length / dim;
- if (embedding.tensorShape[0] > N) {
- logging.setWarningMessage(("Showing the first " + N.toLocaleString()) +
- (" of " + embedding.tensorShape[0].toLocaleString() + " data points"));
- }
- parseTensorsFromFloat32Array(data, dim).then(function (dataPoints) {
- callback(new data_1.DataSet(dataPoints));
- });
- };
- xhr.send();
-}
-exports.retrieveTensorAsBytes = retrieveTensorAsBytes;
-function parseRawTensors(content, callback) {
- parseTensors(content).then(function (data) {
- callback(new data_1.DataSet(data));
- });
-}
-exports.parseRawTensors = parseRawTensors;
-function parseRawMetadata(contents, callback) {
- parseMetadata(contents).then(function (result) { return callback(result); });
-}
-exports.parseRawMetadata = parseRawMetadata;
-/**
- * Parse an ArrayBuffer in a streaming fashion line by line (or custom delim).
- * Can handle very large files.
- *
- * @param content The array buffer.
- * @param callback The callback called on each line.
- * @param chunkSize The size of each read chunk, defaults to ~1MB. (optional)
- * @param delim The delimiter used to split a line, defaults to '\n'. (optional)
- * @returns A promise for when it is finished.
- */
-function streamParse(content, callback, chunkSize, delim) {
- if (chunkSize === void 0) { chunkSize = 1000000; }
- if (delim === void 0) { delim = '\n'; }
- return new Promise(function (resolve, reject) {
- var offset = 0;
- var bufferSize = content.byteLength - 1;
- var data = '';
- function readHandler(str) {
- offset += chunkSize;
- var parts = str.split(delim);
- var first = data + parts[0];
- if (parts.length === 1) {
- data = first;
- readChunk(offset, chunkSize);
- return;
- }
- data = parts[parts.length - 1];
- callback(first);
- for (var i = 1; i < parts.length - 1; i++) {
- callback(parts[i]);
- }
- if (offset >= bufferSize) {
- if (data) {
- callback(data);
- }
- resolve();
- return;
- }
- readChunk(offset, chunkSize);
- }
- function readChunk(offset, size) {
- var contentChunk = content.slice(offset, offset + size);
- var blob = new Blob([contentChunk]);
- var file = new FileReader();
- file.onload = function (e) { return readHandler(e.target.result); };
- file.readAsText(blob);
- }
- readChunk(offset, chunkSize);
- });
-}
-/** Parses a tsv text file. */
-function parseTensors(content, valueDelim) {
- if (valueDelim === void 0) { valueDelim = '\t'; }
- logging.setModalMessage('Parsing tensors...', exports.TENSORS_MSG_ID);
- return new Promise(function (resolve, reject) {
- var data = [];
- var numDim;
- streamParse(content, function (line) {
- line = line.trim();
- if (line === '') {
- return;
- }
- var row = line.split(valueDelim);
- var dataPoint = {
- metadata: {},
- vector: null,
- index: data.length,
- projections: null,
- };
- // If the first label is not a number, take it as the label.
- if (isNaN(row[0]) || numDim === row.length - 1) {
- dataPoint.metadata['label'] = row[0];
- dataPoint.vector = new Float32Array(row.slice(1).map(Number));
- }
- else {
- dataPoint.vector = new Float32Array(row.map(Number));
- }
- data.push(dataPoint);
- if (numDim == null) {
- numDim = dataPoint.vector.length;
- }
- if (numDim !== dataPoint.vector.length) {
- logging.setModalMessage('Parsing failed. Vector dimensions do not match');
- throw Error('Parsing failed');
- }
- if (numDim <= 1) {
- logging.setModalMessage('Parsing failed. Found a vector with only one dimension?');
- throw Error('Parsing failed');
- }
- }).then(function () {
- logging.setModalMessage(null, exports.TENSORS_MSG_ID);
- resolve(data);
- });
- });
-}
-exports.parseTensors = parseTensors;
-/** Parses a tsv text file. */
-function parseTensorsFromFloat32Array(data, dim) {
- return util_1.runAsyncTask('Parsing tensors...', function () {
- var N = data.length / dim;
- var dataPoints = [];
- var offset = 0;
- for (var i = 0; i < N; ++i) {
- dataPoints.push({
- metadata: {},
- vector: data.subarray(offset, offset + dim),
- index: i,
- projections: null,
- });
- offset += dim;
- }
- return dataPoints;
- }, exports.TENSORS_MSG_ID).then(function (dataPoints) {
- logging.setModalMessage(null, exports.TENSORS_MSG_ID);
- return dataPoints;
- });
-}
-exports.parseTensorsFromFloat32Array = parseTensorsFromFloat32Array;
-function analyzeMetadata(columnNames, pointsMetadata) {
- var columnStats = columnNames.map(function (name) {
- return {
- name: name,
- isNumeric: true,
- tooManyUniqueValues: false,
- min: Number.POSITIVE_INFINITY,
- max: Number.NEGATIVE_INFINITY
- };
- });
- var mapOfValues = columnNames.map(function () { return d3.map(); });
- pointsMetadata.forEach(function (metadata) {
- columnNames.forEach(function (name, colIndex) {
- var stats = columnStats[colIndex];
- var map = mapOfValues[colIndex];
- var value = metadata[name];
- // Skip missing values.
- if (value == null) {
- return;
- }
- if (!stats.tooManyUniqueValues) {
- if (map.has(value)) {
- map.set(value, map.get(value) + 1);
- }
- else {
- map.set(value, 1);
- }
- if (map.size() > NUM_COLORS_COLOR_MAP) {
- stats.tooManyUniqueValues = true;
- }
- }
- if (isNaN(value)) {
- stats.isNumeric = false;
- }
- else {
- metadata[name] = +value;
- stats.min = Math.min(stats.min, +value);
- stats.max = Math.max(stats.max, +value);
- }
- });
- });
- columnStats.forEach(function (stats, colIndex) {
- stats.uniqueEntries = mapOfValues[colIndex].entries().map(function (e) {
- return { label: e.key, count: e.value };
- });
- });
- return columnStats;
-}
-exports.analyzeMetadata = analyzeMetadata;
-function parseMetadata(content) {
- logging.setModalMessage('Parsing metadata...', exports.METADATA_MSG_ID);
- return new Promise(function (resolve, reject) {
- var pointsMetadata = [];
- var hasHeader = false;
- var lineNumber = 0;
- var columnNames = ['label'];
- streamParse(content, function (line) {
- if (line.trim().length === 0) {
- return;
- }
- if (lineNumber === 0) {
- hasHeader = line.indexOf('\t') >= 0;
- // If the first row doesn't contain metadata keys, we assume that the
- // values are labels.
- if (hasHeader) {
- columnNames = line.split('\t');
- lineNumber++;
- return;
- }
- }
- lineNumber++;
- var rowValues = line.split('\t');
- var metadata = {};
- pointsMetadata.push(metadata);
- columnNames.forEach(function (name, colIndex) {
- var value = rowValues[colIndex];
- // Normalize missing values.
- value = (value === '' ? null : value);
- metadata[name] = value;
- });
- }).then(function () {
- logging.setModalMessage(null, exports.METADATA_MSG_ID);
- resolve({
- stats: analyzeMetadata(columnNames, pointsMetadata),
- pointsInfo: pointsMetadata
- });
- });
- });
-}
-exports.parseMetadata = parseMetadata;
-function fetchImage(url) {
- return new Promise(function (resolve, reject) {
- var image = new Image();
- image.onload = function () { return resolve(image); };
- image.onerror = function (err) { return reject(err); };
- image.crossOrigin = '';
- image.src = url;
- });
-}
-exports.fetchImage = fetchImage;
-function retrieveSpriteAndMetadataInfo(metadataPath, spriteImagePath, spriteMetadata, callback) {
- var metadataPromise = Promise.resolve({});
- if (metadataPath) {
- metadataPromise = new Promise(function (resolve, reject) {
- logging.setModalMessage('Fetching metadata...', exports.METADATA_MSG_ID);
- var request = new XMLHttpRequest();
- request.open('GET', metadataPath);
- request.responseType = 'arraybuffer';
- request.onerror = function () {
- logging.setErrorMessage(request.responseText, 'fetching metadata');
- reject();
- };
- request.onload = function () {
- resolve(parseMetadata(request.response));
- };
- request.send(null);
- });
- }
- var spriteMsgId = null;
- var spritesPromise = null;
- if (spriteImagePath) {
- spriteMsgId = logging.setModalMessage('Fetching sprite image...');
- spritesPromise = fetchImage(spriteImagePath);
- }
- // Fetch the metadata and the image in parallel.
- Promise.all([metadataPromise, spritesPromise]).then(function (values) {
- if (spriteMsgId) {
- logging.setModalMessage(null, spriteMsgId);
- }
- var metadata = values[0], spriteImage = values[1];
- if (spriteImage && (spriteImage.height > MAX_SPRITE_IMAGE_SIZE_PX ||
- spriteImage.width > MAX_SPRITE_IMAGE_SIZE_PX)) {
- logging.setModalMessage(("Error: Sprite image of dimensions " + spriteImage.width + "px x ") +
- (spriteImage.height + "px exceeds maximum dimensions ") +
- (MAX_SPRITE_IMAGE_SIZE_PX + "px x " + MAX_SPRITE_IMAGE_SIZE_PX + "px"));
- }
- else {
- metadata.spriteImage = spriteImage;
- metadata.spriteMetadata = spriteMetadata;
- callback(metadata);
- }
- });
-}
-exports.retrieveSpriteAndMetadataInfo = retrieveSpriteAndMetadataInfo;
-
-},{"./data":7,"./logging":12,"./util":24}],7:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var bh_tsne_1 = require('./bh_tsne');
-var knn = require('./knn');
-var logging = require('./logging');
-var util = require('./util');
-var vector = require('./vector');
-var IS_FIREFOX = navigator.userAgent.toLowerCase().indexOf('firefox') >= 0;
-/** Controls whether nearest neighbors computation is done on the GPU or CPU. */
-var KNN_GPU_ENABLED = util.hasWebGLSupport() && !IS_FIREFOX;
-exports.TSNE_SAMPLE_SIZE = 10000;
-exports.PCA_SAMPLE_SIZE = 50000;
-/** Number of dimensions to sample when doing approximate PCA. */
-exports.PCA_SAMPLE_DIM = 200;
-/** Number of pca components to compute. */
-var NUM_PCA_COMPONENTS = 10;
-/**
- * Reserved metadata attributes used for sequence information
- * NOTE: Use "__seq_next__" as "__next__" is deprecated.
- */
-var SEQUENCE_METADATA_ATTRS = ['__next__', '__seq_next__'];
-function getSequenceNextPointIndex(pointMetadata) {
- var sequenceAttr = null;
- for (var _i = 0, SEQUENCE_METADATA_ATTRS_1 = SEQUENCE_METADATA_ATTRS; _i < SEQUENCE_METADATA_ATTRS_1.length; _i++) {
- var metadataAttr = SEQUENCE_METADATA_ATTRS_1[_i];
- if (metadataAttr in pointMetadata && pointMetadata[metadataAttr] !== '') {
- sequenceAttr = pointMetadata[metadataAttr];
- break;
- }
- }
- if (sequenceAttr == null) {
- return null;
- }
- return +sequenceAttr;
-}
-/**
- * Dataset contains a DataPoints array that should be treated as immutable. This
- * acts as a working subset of the original data, with cached properties
- * from computationally expensive operations. Because creating a subset
- * requires normalizing and shifting the vector space, we make a copy of the
- * data so we can still always create new subsets based on the original data.
- */
-var DataSet = (function () {
- /** Creates a new Dataset */
- function DataSet(points, spriteAndMetadataInfo) {
- this.shuffledDataIndices = [];
- /**
- * This keeps a list of all current projections so you can easily test to see
- * if it's been calculated already.
- */
- this.projections = d3.set();
- this.tSNEIteration = 0;
- this.tSNEShouldStop = true;
- this.dim = [0, 0];
- this.hasTSNERun = false;
- this.points = points;
- this.shuffledDataIndices = util.shuffle(d3.range(this.points.length));
- this.sequences = this.computeSequences(points);
- this.dim = [this.points.length, this.points[0].vector.length];
- this.spriteAndMetadataInfo = spriteAndMetadataInfo;
- }
- DataSet.prototype.computeSequences = function (points) {
- // Keep a list of indices seen so we don't compute sequences for a given
- // point twice.
- var indicesSeen = new Int8Array(points.length);
- // Compute sequences.
- var indexToSequence = {};
- var sequences = [];
- for (var i = 0; i < points.length; i++) {
- if (indicesSeen[i]) {
- continue;
- }
- indicesSeen[i] = 1;
- // Ignore points without a sequence attribute.
- var next = getSequenceNextPointIndex(points[i].metadata);
- if (next == null) {
- continue;
- }
- if (next in indexToSequence) {
- var existingSequence = indexToSequence[next];
- // Pushing at the beginning of the array.
- existingSequence.pointIndices.unshift(i);
- indexToSequence[i] = existingSequence;
- continue;
- }
- // The current point is pointing to a new/unseen sequence.
- var newSequence = { pointIndices: [] };
- indexToSequence[i] = newSequence;
- sequences.push(newSequence);
- var currentIndex = i;
- while (points[currentIndex]) {
- newSequence.pointIndices.push(currentIndex);
- var next_1 = getSequenceNextPointIndex(points[currentIndex].metadata);
- if (next_1 != null) {
- indicesSeen[next_1] = 1;
- currentIndex = next_1;
- }
- else {
- currentIndex = -1;
- }
- }
- }
- return sequences;
- };
- DataSet.prototype.projectionCanBeRendered = function (projection) {
- if (projection !== 'tsne') {
- return true;
- }
- return this.tSNEIteration > 0;
- };
- /**
- * Returns a new subset dataset by copying out data. We make a copy because
- * we have to modify the vectors by normalizing them.
- *
- * @param subset Array of indices of points that we want in the subset.
- *
- * @return A subset of the original dataset.
- */
- DataSet.prototype.getSubset = function (subset) {
- var _this = this;
- var pointsSubset = ((subset != null) && (subset.length > 0)) ?
- subset.map(function (i) { return _this.points[i]; }) :
- this.points;
- var points = pointsSubset.map(function (dp) {
- return {
- metadata: dp.metadata,
- index: dp.index,
- vector: dp.vector.slice(),
- projections: {}
- };
- });
- return new DataSet(points, this.spriteAndMetadataInfo);
- };
- /**
- * Computes the centroid, shifts all points to that centroid,
- * then makes them all unit norm.
- */
- DataSet.prototype.normalize = function () {
- // Compute the centroid of all data points.
- var centroid = vector.centroid(this.points, function (a) { return a.vector; });
- if (centroid == null) {
- throw Error('centroid should not be null');
- }
- // Shift all points by the centroid and make them unit norm.
- for (var id = 0; id < this.points.length; ++id) {
- var dataPoint = this.points[id];
- dataPoint.vector = vector.sub(dataPoint.vector, centroid);
- vector.unit(dataPoint.vector);
- }
- };
- /** Projects the dataset onto a given vector and caches the result. */
- DataSet.prototype.projectLinear = function (dir, label) {
- this.projections.add(label);
- this.points.forEach(function (dataPoint) {
- dataPoint.projections[label] = vector.dot(dataPoint.vector, dir);
- });
- };
- /** Projects the dataset along the top 10 principal components. */
- DataSet.prototype.projectPCA = function () {
- var _this = this;
- if (this.projections.has('pca-0')) {
- return Promise.resolve(null);
- }
- return util.runAsyncTask('Computing PCA...', function () {
- // Approximate pca vectors by sampling the dimensions.
- var dim = _this.points[0].vector.length;
- var vectors = _this.shuffledDataIndices.map(function (i) { return _this.points[i].vector; });
- if (dim > exports.PCA_SAMPLE_DIM) {
- vectors = vector.projectRandom(vectors, exports.PCA_SAMPLE_DIM);
- }
- var sampledVectors = vectors.slice(0, exports.PCA_SAMPLE_SIZE);
- var sigma = numeric.div(numeric.dot(numeric.transpose(sampledVectors), sampledVectors), sampledVectors.length);
- var svd = numeric.svd(sigma);
- var variances = svd.S;
- var totalVariance = 0;
- for (var i = 0; i < variances.length; ++i) {
- totalVariance += variances[i];
- }
- for (var i = 0; i < variances.length; ++i) {
- variances[i] /= totalVariance;
- }
- _this.fracVariancesExplained = variances;
- var U = svd.U;
- var pcaVectors = vectors.map(function (vector) {
- var newV = new Float32Array(NUM_PCA_COMPONENTS);
- for (var newDim = 0; newDim < NUM_PCA_COMPONENTS; newDim++) {
- var dot = 0;
- for (var oldDim = 0; oldDim < vector.length; oldDim++) {
- dot += vector[oldDim] * U[oldDim][newDim];
- }
- newV[newDim] = dot;
- }
- return newV;
- });
- for (var d = 0; d < NUM_PCA_COMPONENTS; d++) {
- var label = 'pca-' + d;
- _this.projections.add(label);
- for (var i = 0; i < pcaVectors.length; i++) {
- var pointIndex = _this.shuffledDataIndices[i];
- _this.points[pointIndex].projections[label] = pcaVectors[i][d];
- }
- }
- });
- };
- /** Runs tsne on the data. */
- DataSet.prototype.projectTSNE = function (perplexity, learningRate, tsneDim, stepCallback) {
- var _this = this;
- this.hasTSNERun = true;
- var k = Math.floor(3 * perplexity);
- var opt = { epsilon: learningRate, perplexity: perplexity, dim: tsneDim };
- this.tsne = new bh_tsne_1.TSNE(opt);
- this.tSNEShouldStop = false;
- this.tSNEIteration = 0;
- var sampledIndices = this.shuffledDataIndices.slice(0, exports.TSNE_SAMPLE_SIZE);
- var step = function () {
- if (_this.tSNEShouldStop) {
- stepCallback(null);
- _this.tsne = null;
- return;
- }
- _this.tsne.step();
- var result = _this.tsne.getSolution();
- sampledIndices.forEach(function (index, i) {
- var dataPoint = _this.points[index];
- dataPoint.projections['tsne-0'] = result[i * tsneDim + 0];
- dataPoint.projections['tsne-1'] = result[i * tsneDim + 1];
- if (tsneDim === 3) {
- dataPoint.projections['tsne-2'] = result[i * tsneDim + 2];
- }
- });
- _this.tSNEIteration++;
- stepCallback(_this.tSNEIteration);
- requestAnimationFrame(step);
- };
- // Nearest neighbors calculations.
- var knnComputation;
- if (this.nearest != null && k === this.nearestK) {
- // We found the nearest neighbors before and will reuse them.
- knnComputation = Promise.resolve(this.nearest);
- }
- else {
- var sampledData = sampledIndices.map(function (i) { return _this.points[i]; });
- this.nearestK = k;
- knnComputation = KNN_GPU_ENABLED ?
- knn.findKNNGPUCosine(sampledData, k, (function (d) { return d.vector; })) :
- knn.findKNN(sampledData, k, (function (d) { return d.vector; }), function (a, b, limit) { return vector.cosDistNorm(a, b); });
- }
- knnComputation.then(function (nearest) {
- _this.nearest = nearest;
- util.runAsyncTask('Initializing T-SNE...', function () {
- _this.tsne.initDataDist(_this.nearest);
- }).then(step);
- });
- };
- /**
- * Merges metadata to the dataset and returns whether it succeeded.
- */
- DataSet.prototype.mergeMetadata = function (metadata) {
- var _this = this;
- if (metadata.pointsInfo.length !== this.points.length) {
- var errorMessage = ("Number of tensors (" + this.points.length + ") do not") +
- " match the number of lines in metadata" +
- (" (" + metadata.pointsInfo.length + ").");
- if (metadata.stats.length === 1 &&
- this.points.length + 1 === metadata.pointsInfo.length) {
- // If there is only one column of metadata and the number of points is
- // exactly one less than the number of metadata lines, this is due to an
- // unnecessary header line in the metadata and we can show a meaningful
- // error.
- logging.setErrorMessage(errorMessage + ' Single column metadata should not have a header ' +
- 'row.', 'merging metadata');
- return false;
- }
- else if (metadata.stats.length > 1 &&
- this.points.length - 1 === metadata.pointsInfo.length) {
- // If there are multiple columns of metadata and the number of points is
- // exactly one greater than the number of lines in the metadata, this
- // means there is a missing metadata header.
- logging.setErrorMessage(errorMessage + ' Multi-column metadata should have a header ' +
- 'row with column labels.', 'merging metadata');
- return false;
- }
- logging.setWarningMessage(errorMessage);
- }
- this.spriteAndMetadataInfo = metadata;
- metadata.pointsInfo.slice(0, this.points.length)
- .forEach(function (m, i) { return _this.points[i].metadata = m; });
- return true;
- };
- DataSet.prototype.stopTSNE = function () {
- this.tSNEShouldStop = true;
- };
- /**
- * Finds the nearest neighbors of the query point using a
- * user-specified distance metric.
- */
- DataSet.prototype.findNeighbors = function (pointIndex, distFunc, numNN) {
- // Find the nearest neighbors of a particular point.
- var neighbors = knn.findKNNofPoint(this.points, pointIndex, numNN, (function (d) { return d.vector; }), distFunc);
- // TODO(smilkov): Figure out why we slice.
- var result = neighbors.slice(0, numNN);
- return result;
- };
- /**
- * Search the dataset based on a metadata field.
- */
- DataSet.prototype.query = function (query, inRegexMode, fieldName) {
- var predicate = util.getSearchPredicate(query, inRegexMode, fieldName);
- var matches = [];
- this.points.forEach(function (point, id) {
- if (predicate(point)) {
- matches.push(id);
- }
- });
- return matches;
- };
- return DataSet;
-}());
-exports.DataSet = DataSet;
-var Projection = (function () {
- function Projection(projectionType, projectionComponents, dimensionality, dataSet) {
- this.projectionType = projectionType;
- this.projectionComponents = projectionComponents;
- this.dimensionality = dimensionality;
- this.dataSet = dataSet;
- }
- return Projection;
-}());
-exports.Projection = Projection;
-/**
- * An interface that holds all the data for serializing the current state of
- * the world.
- */
-var State = (function () {
- function State() {
- /** A label identifying this state. */
- this.label = '';
- /** Whether this State is selected in the bookmarks pane. */
- this.isSelected = false;
- /** t-SNE parameters */
- this.tSNEIteration = 0;
- this.tSNEPerplexity = 0;
- this.tSNELearningRate = 0;
- this.tSNEis3d = true;
- /** PCA projection component dimensions */
- this.pcaComponentDimensions = [];
- /** The computed projections of the tensors. */
- this.projections = [];
- /** The indices of selected points. */
- this.selectedPoints = [];
- }
- return State;
-}());
-exports.State = State;
-function getProjectionComponents(projection, components) {
- if (components.length > 3) {
- throw new RangeError('components length must be <= 3');
- }
- var projectionComponents = [null, null, null];
- var prefix = (projection === 'custom') ? 'linear' : projection;
- for (var i = 0; i < components.length; ++i) {
- if (components[i] == null) {
- continue;
- }
- projectionComponents[i] = prefix + '-' + components[i];
- }
- return projectionComponents;
-}
-exports.getProjectionComponents = getProjectionComponents;
-function stateGetAccessorDimensions(state) {
- var dimensions;
- switch (state.selectedProjection) {
- case 'pca':
- dimensions = state.pcaComponentDimensions.slice();
- break;
- case 'tsne':
- dimensions = [0, 1];
- if (state.tSNEis3d) {
- dimensions.push(2);
- }
- break;
- case 'custom':
- dimensions = ['x', 'y'];
- break;
- default:
- throw new Error('Unexpected fallthrough');
- }
- return dimensions;
-}
-exports.stateGetAccessorDimensions = stateGetAccessorDimensions;
-
-},{"./bh_tsne":2,"./knn":10,"./logging":12,"./util":24,"./vector":25}],8:[function(require,module,exports){
-
-},{}],9:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-/**
- * Min-heap data structure. Provides O(1) for peek, returning the smallest key.
- */
-// TODO(jart): Rename to Heap and use Comparator.
-var MinHeap = (function () {
- function MinHeap() {
- this.arr = [];
- }
- /** Push an element with the provided key. */
- MinHeap.prototype.push = function (key, value) {
- this.arr.push({ key: key, value: value });
- this.bubbleUp(this.arr.length - 1);
- };
- /** Pop the element with the smallest key. */
- MinHeap.prototype.pop = function () {
- if (this.arr.length === 0) {
- throw new Error('pop() called on empty binary heap');
- }
- var item = this.arr[0];
- var last = this.arr.length - 1;
- this.arr[0] = this.arr[last];
- this.arr.pop();
- if (last > 0) {
- this.bubbleDown(0);
- }
- return item;
- };
- ;
- /** Returns, but doesn't remove the element with the smallest key */
- MinHeap.prototype.peek = function () { return this.arr[0]; };
- /**
- * Pops the element with the smallest key and at the same time
- * adds the newly provided element. This is faster than calling
- * pop() and push() separately.
- */
- MinHeap.prototype.popPush = function (key, value) {
- if (this.arr.length === 0) {
- throw new Error('pop() called on empty binary heap');
- }
- var item = this.arr[0];
- this.arr[0] = { key: key, value: value };
- if (this.arr.length > 0) {
- this.bubbleDown(0);
- }
- return item;
- };
- /** Returns the number of elements in the heap. */
- MinHeap.prototype.size = function () { return this.arr.length; };
- /** Returns all the items in the heap. */
- MinHeap.prototype.items = function () { return this.arr; };
- MinHeap.prototype.swap = function (a, b) {
- var temp = this.arr[a];
- this.arr[a] = this.arr[b];
- this.arr[b] = temp;
- };
- MinHeap.prototype.bubbleDown = function (pos) {
- var left = (pos << 1) + 1;
- var right = left + 1;
- var largest = pos;
- if (left < this.arr.length && this.arr[left].key < this.arr[largest].key) {
- largest = left;
- }
- if (right < this.arr.length &&
- this.arr[right].key < this.arr[largest].key) {
- largest = right;
- }
- if (largest !== pos) {
- this.swap(largest, pos);
- this.bubbleDown(largest);
- }
- };
- MinHeap.prototype.bubbleUp = function (pos) {
- if (pos <= 0) {
- return;
- }
- var parent = ((pos - 1) >> 1);
- if (this.arr[pos].key < this.arr[parent].key) {
- this.swap(pos, parent);
- this.bubbleUp(parent);
- }
- };
- return MinHeap;
-}());
-exports.MinHeap = MinHeap;
-/** List that keeps the K elements with the smallest keys. */
-var KMin = (function () {
- /** Constructs a new k-min data structure with the provided k. */
- function KMin(k) {
- this.maxHeap = new MinHeap();
- this.k = k;
- }
- /** Adds an element to the list. */
- KMin.prototype.add = function (key, value) {
- if (this.maxHeap.size() < this.k) {
- this.maxHeap.push(-key, value);
- return;
- }
- var largest = this.maxHeap.peek();
- // If the new element is smaller, replace the largest with the new element.
- if (key < -largest.key) {
- this.maxHeap.popPush(-key, value);
- }
- };
- /** Returns the k items with the smallest keys. */
- KMin.prototype.getMinKItems = function () {
- var items = this.maxHeap.items();
- items.sort(function (a, b) { return b.key - a.key; });
- return items.map(function (a) { return a.value; });
- };
- /** Returns the size of the list. */
- KMin.prototype.getSize = function () { return this.maxHeap.size(); };
- /** Returns the largest key in the list. */
- KMin.prototype.getLargestKey = function () {
- return this.maxHeap.size() === 0 ? null : -this.maxHeap.peek().key;
- };
- return KMin;
-}());
-exports.KMin = KMin;
-
-},{}],10:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var util_1 = require('./util');
-var logging = require('./logging');
-var heap_1 = require('./heap');
-var vector = require('./vector');
-/**
- * Optimal size for the height of the matrix when doing computation on the GPU
- * using WebGL. This was found experimentally.
- *
- * This also guarantees that for computing pair-wise distance for up to 10K
- * vectors, no more than 40MB will be allocated in the GPU. Without the
- * allocation limit, we can freeze the graphics of the whole OS.
- */
-var OPTIMAL_GPU_BLOCK_SIZE = 256;
-/** Id of message box used for knn gpu progress bar. */
-var KNN_GPU_MSG_ID = 'knn-gpu';
-/**
- * Returns the K nearest neighbors for each vector where the distance
- * computation is done on the GPU (WebGL) using cosine distance.
- *
- * @param dataPoints List of data points, where each data point holds an
- * n-dimensional vector.
- * @param k Number of nearest neighbors to find.
- * @param accessor A method that returns the vector, given the data point.
- */
-function findKNNGPUCosine(dataPoints, k, accessor) {
- var N = dataPoints.length;
- var dim = accessor(dataPoints[0]).length;
- // The goal is to compute a large matrix multiplication A*A.T where A is of
- // size NxD and A.T is its transpose. This results in a NxN matrix which
- // could be too big to store on the GPU memory. To avoid memory overflow, we
- // compute multiple A*partial_A.T where partial_A is of size BxD (B is much
- // smaller than N). This results in storing only NxB size matrices on the GPU
- // at a given time.
- // A*A.T will give us NxN matrix holding the cosine distance between every
- // pair of points, which we sort using KMin data structure to obtain the
- // K nearest neighbors for each point.
- var typedArray = vector.toTypedArray(dataPoints, accessor);
- var bigMatrix = new weblas.pipeline.Tensor([N, dim], typedArray);
- var nearest = new Array(N);
- var numPieces = Math.ceil(N / OPTIMAL_GPU_BLOCK_SIZE);
- var M = Math.floor(N / numPieces);
- var modulo = N % numPieces;
- var offset = 0;
- var progress = 0;
- var progressDiff = 1 / (2 * numPieces);
- var piece = 0;
- function step(resolve) {
- var progressMsg = 'Finding nearest neighbors: ' + (progress * 100).toFixed() + '%';
- util_1.runAsyncTask(progressMsg, function () {
- var B = piece < modulo ? M + 1 : M;
- var typedB = new Float32Array(B * dim);
- for (var i = 0; i < B; ++i) {
- var vector_1 = accessor(dataPoints[offset + i]);
- for (var d = 0; d < dim; ++d) {
- typedB[i * dim + d] = vector_1[d];
- }
- }
- var partialMatrix = new weblas.pipeline.Tensor([B, dim], typedB);
- // Result is N x B matrix.
- var result = weblas.pipeline.sgemm(1, bigMatrix, partialMatrix, null, null);
- var partial = result.transfer();
- partialMatrix.delete();
- result.delete();
- progress += progressDiff;
- for (var i = 0; i < B; i++) {
- var kMin = new heap_1.KMin(k);
- var iReal = offset + i;
- for (var j = 0; j < N; j++) {
- if (j === iReal) {
- continue;
- }
- var cosDist = 1 - partial[j * B + i]; // [j, i];
- kMin.add(cosDist, { index: j, dist: cosDist });
- }
- nearest[iReal] = kMin.getMinKItems();
- }
- progress += progressDiff;
- offset += B;
- piece++;
- }, KNN_GPU_MSG_ID).then(function () {
- if (piece < numPieces) {
- step(resolve);
- }
- else {
- logging.setModalMessage(null, KNN_GPU_MSG_ID);
- bigMatrix.delete();
- resolve(nearest);
- }
- }, function (error) {
- // GPU failed. Reverting back to CPU.
- logging.setModalMessage(null, KNN_GPU_MSG_ID);
- var distFunc = function (a, b, limit) { return vector.cosDistNorm(a, b); };
- findKNN(dataPoints, k, accessor, distFunc).then(function (nearest) {
- resolve(nearest);
- });
- });
- }
- return new Promise(function (resolve) { return step(resolve); });
-}
-exports.findKNNGPUCosine = findKNNGPUCosine;
-/**
- * Returns the K nearest neighbors for each vector where the distance
- * computation is done on the CPU using a user-specified distance method.
- *
- * @param dataPoints List of data points, where each data point holds an
- * n-dimensional vector.
- * @param k Number of nearest neighbors to find.
- * @param accessor A method that returns the vector, given the data point.
- * @param dist Method that takes two vectors and a limit, and computes the
- * distance between two vectors, with the ability to stop early if the
- * distance is above the limit.
- */
-function findKNN(dataPoints, k, accessor, dist) {
- return util_1.runAsyncTask('Finding nearest neighbors...', function () {
- var N = dataPoints.length;
- var nearest = new Array(N);
- // Find the distances from node i.
- var kMin = new Array(N);
- for (var i = 0; i < N; i++) {
- kMin[i] = new heap_1.KMin(k);
- }
- for (var i = 0; i < N; i++) {
- var a = accessor(dataPoints[i]);
- var kMinA = kMin[i];
- for (var j = i + 1; j < N; j++) {
- var kMinB = kMin[j];
- var limitI = kMinA.getSize() === k ?
- kMinA.getLargestKey() || Number.MAX_VALUE :
- Number.MAX_VALUE;
- var limitJ = kMinB.getSize() === k ?
- kMinB.getLargestKey() || Number.MAX_VALUE :
- Number.MAX_VALUE;
- var limit = Math.max(limitI, limitJ);
- var dist2ItoJ = dist(a, accessor(dataPoints[j]), limit);
- if (dist2ItoJ >= 0) {
- kMinA.add(dist2ItoJ, { index: j, dist: dist2ItoJ });
- kMinB.add(dist2ItoJ, { index: i, dist: dist2ItoJ });
- }
- }
- }
- for (var i = 0; i < N; i++) {
- nearest[i] = kMin[i].getMinKItems();
- }
- return nearest;
- });
-}
-exports.findKNN = findKNN;
-/** Calculates the minimum distance between a search point and a rectangle. */
-function minDist(point, x1, y1, x2, y2) {
- var x = point[0];
- var y = point[1];
- var dx1 = x - x1;
- var dx2 = x - x2;
- var dy1 = y - y1;
- var dy2 = y - y2;
- if (dx1 * dx2 <= 0) {
- if (dy1 * dy2 <= 0) {
- return 0; // return 0 as point is in rect
- }
- return Math.min(Math.abs(dy1), Math.abs(dy2));
- }
- if (dy1 * dy2 <= 0) {
- // We know it is already inside the rectangle
- return Math.min(Math.abs(dx1), Math.abs(dx2));
- }
- var corner;
- if (x > x2) {
- // Upper-right vs lower-right.
- corner = y > y2 ? [x2, y2] : [x2, y1];
- }
- else {
- // Upper-left vs lower-left.
- corner = y > y2 ? [x1, y2] : [x1, y1];
- }
- return Math.sqrt(vector.dist22D([x, y], corner));
-}
-/**
- * Returns the nearest neighbors of a particular point.
- *
- * @param dataPoints List of data points.
- * @param pointIndex The index of the point we need the nearest neighbors of.
- * @param k Number of nearest neighbors to search for.
- * @param accessor Method that maps a data point => vector (array of numbers).
- * @param distance Method that takes two vectors and returns their distance.
- */
-function findKNNofPoint(dataPoints, pointIndex, k, accessor, distance) {
- var kMin = new heap_1.KMin(k);
- var a = accessor(dataPoints[pointIndex]);
- for (var i = 0; i < dataPoints.length; ++i) {
- if (i === pointIndex) {
- continue;
- }
- var b = accessor(dataPoints[i]);
- var dist = distance(a, b);
- kMin.add(dist, { index: i, dist: dist });
- }
- return kMin.getMinKItems();
-}
-exports.findKNNofPoint = findKNNofPoint;
-
-},{"./heap":9,"./logging":12,"./util":24,"./vector":25}],11:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-/**
- * Accelerates label placement by dividing the view into a uniform grid.
- * Labels only need to be tested for collision with other labels that overlap
- * the same grid cells. This is a fork of {@code amoeba.CollisionGrid}.
- */
-var CollisionGrid = (function () {
- /**
- * Constructs a new Collision grid.
- *
- * @param bound The bound of the grid. Labels out of bounds will be rejected.
- * @param cellWidth Width of a cell in the grid.
- * @param cellHeight Height of a cell in the grid.
- */
- function CollisionGrid(bound, cellWidth, cellHeight) {
- /** The bound of the grid. Labels out of bounds will be rejected. */
- this.bound = bound;
- /** Width of a cell in the grid. */
- this.cellWidth = cellWidth;
- /** Height of a cell in the grid. */
- this.cellHeight = cellHeight;
- /** Number of grid cells along the x axis. */
- this.numHorizCells = Math.ceil(this.boundWidth(bound) / cellWidth);
- /** Number of grid cells along the y axis. */
- this.numVertCells = Math.ceil(this.boundHeight(bound) / cellHeight);
- /**
- * The 2d grid (stored as a 1d array.) Each cell consists of an array of
- * BoundingBoxes for objects that are in the cell.
- */
- this.grid = new Array(this.numHorizCells * this.numVertCells);
- }
- CollisionGrid.prototype.boundWidth = function (bound) { return bound.hiX - bound.loX; };
- CollisionGrid.prototype.boundHeight = function (bound) { return bound.hiY - bound.loY; };
- CollisionGrid.prototype.boundsIntersect = function (a, b) {
- return !(a.loX > b.hiX || a.loY > b.hiY || a.hiX < b.loX || a.hiY < b.loY);
- };
- /**
- * Checks if a given bounding box has any conflicts in the grid and inserts it
- * if none are found.
- *
- * @param bound The bound to insert.
- * @param justTest If true, just test if it conflicts, without inserting.
- * @return True if the bound was successfully inserted; false if it
- * could not be inserted due to a conflict.
- */
- CollisionGrid.prototype.insert = function (bound, justTest) {
- if (justTest === void 0) { justTest = false; }
- // Reject if the label is out of bounds.
- if ((bound.hiX < this.bound.loX) || (bound.loX > this.bound.hiX) ||
- (bound.hiY < this.bound.loY) || (bound.loY > this.bound.hiY)) {
- return false;
- }
- var minCellX = this.getCellX(bound.loX);
- var maxCellX = this.getCellX(bound.hiX);
- var minCellY = this.getCellY(bound.loY);
- var maxCellY = this.getCellY(bound.hiY);
- // Check all overlapped cells to verify that we can insert.
- var baseIdx = minCellY * this.numHorizCells + minCellX;
- var idx = baseIdx;
- for (var j = minCellY; j <= maxCellY; j++) {
- for (var i = minCellX; i <= maxCellX; i++) {
- var cell = this.grid[idx++];
- if (cell) {
- for (var k = 0; k < cell.length; k++) {
- if (this.boundsIntersect(bound, cell[k])) {
- return false;
- }
- }
- }
- }
- idx += this.numHorizCells - (maxCellX - minCellX + 1);
- }
- if (justTest) {
- return true;
- }
- // Insert into the overlapped cells.
- idx = baseIdx;
- for (var j = minCellY; j <= maxCellY; j++) {
- for (var i = minCellX; i <= maxCellX; i++) {
- if (!this.grid[idx]) {
- this.grid[idx] = [bound];
- }
- else {
- this.grid[idx].push(bound);
- }
- idx++;
- }
- idx += this.numHorizCells - (maxCellX - minCellX + 1);
- }
- return true;
- };
- /**
- * Returns the x index of the grid cell where the given x coordinate falls.
- *
- * @param x the coordinate, in world space.
- * @return the x index of the cell.
- */
- CollisionGrid.prototype.getCellX = function (x) {
- return Math.floor((x - this.bound.loX) / this.cellWidth);
- };
- ;
- /**
- * Returns the y index of the grid cell where the given y coordinate falls.
- *
- * @param y the coordinate, in world space.
- * @return the y index of the cell.
- */
- CollisionGrid.prototype.getCellY = function (y) {
- return Math.floor((y - this.bound.loY) / this.cellHeight);
- };
- ;
- return CollisionGrid;
-}());
-exports.CollisionGrid = CollisionGrid;
-
-},{}],12:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-/** Duration in ms for showing warning messages to the user */
-var WARNING_DURATION_MS = 10000;
-var dom = null;
-var msgId = 0;
-var numActiveMessages = 0;
-function setDomContainer(domElement) {
- dom = domElement;
-}
-exports.setDomContainer = setDomContainer;
-/**
- * Updates the user message with the provided id.
- *
- * @param msg The message shown to the user. If null, the message is removed.
- * @param id The id of an existing message. If no id is provided, a unique id
- * is assigned.
- * @param title The title of the notification.
- * @param isErrorMsg If true, the message is error and the dialog will have a
- * close button.
- * @return The id of the message.
- */
-function setModalMessage(msg, id, title, isErrorMsg) {
- if (id === void 0) { id = null; }
- if (title === void 0) { title = null; }
- if (isErrorMsg === void 0) { isErrorMsg = false; }
- if (dom == null) {
- console.warn('Can\'t show modal message before the dom is initialized');
- return;
- }
- if (id == null) {
- id = (msgId++).toString();
- }
- var dialog = dom.querySelector('#notification-dialog');
- dialog.querySelector('.close-button').style.display =
- isErrorMsg ? null : 'none';
- var spinner = dialog.querySelector('.progress');
- spinner.style.display = isErrorMsg ? 'none' : null;
- spinner.active = isErrorMsg ? null : true;
- dialog.querySelector('#notification-title').innerHTML = title;
- var msgsContainer = dialog.querySelector('#notify-msgs');
- if (isErrorMsg) {
- d3.select(msgsContainer).html('');
- }
- else {
- d3.select(msgsContainer).selectAll('.error').remove();
- }
- var divId = "notify-msg-" + id;
- var msgDiv = d3.select(dialog.querySelector('#' + divId));
- var exists = msgDiv.size() > 0;
- if (!exists) {
- msgDiv = d3.select(msgsContainer)
- .insert('div', ':first-child')
- .attr('class', 'notify-msg')
- .classed('error', isErrorMsg)
- .attr('id', divId);
- if (!isErrorMsg) {
- numActiveMessages++;
- }
- else {
- numActiveMessages = 0;
- }
- }
- if (msg == null) {
- numActiveMessages--;
- if (numActiveMessages === 0) {
- dialog.close();
- }
- msgDiv.remove();
- }
- else {
- msgDiv.text(msg);
- dialog.open();
- }
- return id;
-}
-exports.setModalMessage = setModalMessage;
-function setErrorMessage(errMsg, task) {
- setModalMessage(errMsg, null, 'Error ' + (task != null ? task : ''), true);
-}
-exports.setErrorMessage = setErrorMessage;
-/**
- * Shows a warning message to the user for a certain amount of time.
- */
-function setWarningMessage(msg) {
- var toast = dom.querySelector('#toast');
- toast.text = msg;
- toast.duration = WARNING_DURATION_MS;
- toast.open();
-}
-exports.setWarningMessage = setWarningMessage;
-
-},{}],13:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-
-},{}],14:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var renderContext_1 = require('./renderContext');
-var scatterPlot_1 = require('./scatterPlot');
-var scatterPlotVisualizer3DLabels_1 = require('./scatterPlotVisualizer3DLabels');
-var scatterPlotVisualizerCanvasLabels_1 = require('./scatterPlotVisualizerCanvasLabels');
-var scatterPlotVisualizerPolylines_1 = require('./scatterPlotVisualizerPolylines');
-var scatterPlotVisualizerSprites_1 = require('./scatterPlotVisualizerSprites');
-var vector = require('./vector');
-var LABEL_FONT_SIZE = 10;
-var LABEL_SCALE_DEFAULT = 1.0;
-var LABEL_SCALE_LARGE = 2;
-var LABEL_FILL_COLOR_SELECTED = 0x000000;
-var LABEL_FILL_COLOR_HOVER = 0x000000;
-var LABEL_FILL_COLOR_NEIGHBOR = 0x000000;
-var LABEL_STROKE_COLOR_SELECTED = 0xFFFFFF;
-var LABEL_STROKE_COLOR_HOVER = 0xFFFFFF;
-var LABEL_STROKE_COLOR_NEIGHBOR = 0xFFFFFF;
-var POINT_COLOR_UNSELECTED = 0xE3E3E3;
-var POINT_COLOR_NO_SELECTION = 0x7575D9;
-var POINT_COLOR_SELECTED = 0xFA6666;
-var POINT_COLOR_HOVER = 0x760B4F;
-var POINT_SCALE_DEFAULT = 1.0;
-var POINT_SCALE_SELECTED = 1.2;
-var POINT_SCALE_NEIGHBOR = 1.2;
-var POINT_SCALE_HOVER = 1.2;
-var LABELS_3D_COLOR_UNSELECTED = 0xFFFFFF;
-var LABELS_3D_COLOR_NO_SELECTION = 0xFFFFFF;
-var SPRITE_IMAGE_COLOR_UNSELECTED = 0xFFFFFF;
-var SPRITE_IMAGE_COLOR_NO_SELECTION = 0xFFFFFF;
-var POLYLINE_START_HUE = 60;
-var POLYLINE_END_HUE = 360;
-var POLYLINE_SATURATION = 1;
-var POLYLINE_LIGHTNESS = .3;
-var POLYLINE_DEFAULT_OPACITY = .2;
-var POLYLINE_DEFAULT_LINEWIDTH = 2;
-var POLYLINE_SELECTED_OPACITY = .9;
-var POLYLINE_SELECTED_LINEWIDTH = 3;
-var POLYLINE_DESELECTED_OPACITY = .05;
-var SCATTER_PLOT_CUBE_LENGTH = 2;
-/** Color scale for nearest neighbors. */
-var NN_COLOR_SCALE = d3.scale.linear()
- .domain([1, 0.7, 0.4])
- .range(['hsl(285, 80%, 40%)', 'hsl(0, 80%, 65%)', 'hsl(40, 70%, 60%)'])
- .clamp(true);
-/**
- * Interprets projector events and assembes the arrays and commands necessary
- * to use the ScatterPlot to render the current projected data set.
- */
-var ProjectorScatterPlotAdapter = (function () {
- function ProjectorScatterPlotAdapter(scatterPlotContainer, projectorEventContext) {
- var _this = this;
- this.renderLabelsIn3D = false;
- this.scatterPlot =
- new scatterPlot_1.ScatterPlot(scatterPlotContainer, projectorEventContext);
- this.scatterPlotContainer = scatterPlotContainer;
- projectorEventContext.registerProjectionChangedListener(function (projection) {
- _this.projection = projection;
- _this.updateScatterPlotWithNewProjection(projection);
- });
- projectorEventContext.registerSelectionChangedListener(function (selectedPointIndices, neighbors) {
- _this.selectedPointIndices = selectedPointIndices;
- _this.neighborsOfFirstSelectedPoint = neighbors;
- _this.updateScatterPlotPositions();
- _this.updateScatterPlotAttributes();
- _this.scatterPlot.render();
- });
- projectorEventContext.registerHoverListener(function (hoverPointIndex) {
- _this.hoverPointIndex = hoverPointIndex;
- _this.updateScatterPlotAttributes();
- _this.scatterPlot.render();
- });
- projectorEventContext.registerDistanceMetricChangedListener(function (distanceMetric) {
- _this.distanceMetric = distanceMetric;
- _this.updateScatterPlotAttributes();
- _this.scatterPlot.render();
- });
- this.createVisualizers(false);
- }
- ProjectorScatterPlotAdapter.prototype.notifyProjectionPositionsUpdated = function () {
- this.updateScatterPlotPositions();
- this.scatterPlot.render();
- };
- ProjectorScatterPlotAdapter.prototype.setDataSet = function (dataSet) {
- if (this.projection != null) {
- // TODO(nicholsonc): setDataSet needs to go away, the projection is the
- // atomic unit of update.
- this.projection.dataSet = dataSet;
- }
- if (this.polylineVisualizer != null) {
- this.polylineVisualizer.setDataSet(dataSet);
- }
- if (this.labels3DVisualizer != null) {
- this.labels3DVisualizer.setLabelStrings(this.generate3DLabelsArray(dataSet, this.labelPointAccessor));
- }
- if (this.spriteVisualizer == null) {
- return;
- }
- this.spriteVisualizer.clearSpriteAtlas();
- if ((dataSet == null) || (dataSet.spriteAndMetadataInfo == null)) {
- return;
- }
- var metadata = dataSet.spriteAndMetadataInfo;
- if ((metadata.spriteImage == null) || (metadata.spriteMetadata == null)) {
- return;
- }
- var n = dataSet.points.length;
- var spriteIndices = new Float32Array(n);
- for (var i = 0; i < n; ++i) {
- spriteIndices[i] = dataSet.points[i].index;
- }
- this.spriteVisualizer.setSpriteAtlas(metadata.spriteImage, metadata.spriteMetadata.singleImageDim, spriteIndices);
- };
- ProjectorScatterPlotAdapter.prototype.set3DLabelMode = function (renderLabelsIn3D) {
- this.renderLabelsIn3D = renderLabelsIn3D;
- this.createVisualizers(renderLabelsIn3D);
- this.updateScatterPlotAttributes();
- this.scatterPlot.render();
- };
- ProjectorScatterPlotAdapter.prototype.setLegendPointColorer = function (legendPointColorer) {
- this.legendPointColorer = legendPointColorer;
- };
- ProjectorScatterPlotAdapter.prototype.setLabelPointAccessor = function (labelPointAccessor) {
- this.labelPointAccessor = labelPointAccessor;
- if (this.labels3DVisualizer != null) {
- var ds = (this.projection == null) ? null : this.projection.dataSet;
- this.labels3DVisualizer.setLabelStrings(this.generate3DLabelsArray(ds, labelPointAccessor));
- }
- };
- ProjectorScatterPlotAdapter.prototype.resize = function () {
- this.scatterPlot.resize();
- };
- ProjectorScatterPlotAdapter.prototype.populateBookmarkFromUI = function (state) {
- state.cameraDef = this.scatterPlot.getCameraDef();
- };
- ProjectorScatterPlotAdapter.prototype.restoreUIFromBookmark = function (state) {
- this.scatterPlot.setCameraParametersForNextCameraCreation(state.cameraDef, false);
- };
- ProjectorScatterPlotAdapter.prototype.updateScatterPlotPositions = function () {
- var ds = (this.projection == null) ? null : this.projection.dataSet;
- var projectionComponents = (this.projection == null) ? null : this.projection.projectionComponents;
- var newPositions = this.generatePointPositionArray(ds, projectionComponents);
- this.scatterPlot.setPointPositions(newPositions);
- };
- ProjectorScatterPlotAdapter.prototype.updateScatterPlotAttributes = function () {
- if (this.projection == null) {
- return;
- }
- var dataSet = this.projection.dataSet;
- var selectedSet = this.selectedPointIndices;
- var hoverIndex = this.hoverPointIndex;
- var neighbors = this.neighborsOfFirstSelectedPoint;
- var pointColorer = this.legendPointColorer;
- var pointColors = this.generatePointColorArray(dataSet, pointColorer, this.distanceMetric, selectedSet, neighbors, hoverIndex, this.renderLabelsIn3D, this.getSpriteImageMode());
- var pointScaleFactors = this.generatePointScaleFactorArray(dataSet, selectedSet, neighbors, hoverIndex);
- var labels = this.generateVisibleLabelRenderParams(dataSet, selectedSet, neighbors, hoverIndex);
- var polylineColors = this.generateLineSegmentColorMap(dataSet, pointColorer);
- var polylineOpacities = this.generateLineSegmentOpacityArray(dataSet, selectedSet);
- var polylineWidths = this.generateLineSegmentWidthArray(dataSet, selectedSet);
- this.scatterPlot.setPointColors(pointColors);
- this.scatterPlot.setPointScaleFactors(pointScaleFactors);
- this.scatterPlot.setLabels(labels);
- this.scatterPlot.setPolylineColors(polylineColors);
- this.scatterPlot.setPolylineOpacities(polylineOpacities);
- this.scatterPlot.setPolylineWidths(polylineWidths);
- };
- ProjectorScatterPlotAdapter.prototype.render = function () {
- this.scatterPlot.render();
- };
- ProjectorScatterPlotAdapter.prototype.generatePointPositionArray = function (ds, projectionComponents) {
- if (ds == null) {
- return null;
- }
- var xScaler = d3.scale.linear();
- var yScaler = d3.scale.linear();
- var zScaler = null;
- {
- // Determine max and min of each axis of our data.
- var xExtent = d3.extent(ds.points, function (p, i) { return ds.points[i].projections[projectionComponents[0]]; });
- var yExtent = d3.extent(ds.points, function (p, i) { return ds.points[i].projections[projectionComponents[1]]; });
- var range = [-SCATTER_PLOT_CUBE_LENGTH / 2, SCATTER_PLOT_CUBE_LENGTH / 2];
- xScaler.domain(xExtent).range(range);
- yScaler.domain(yExtent).range(range);
- if (projectionComponents[2] != null) {
- var zExtent = d3.extent(ds.points, function (p, i) { return ds.points[i].projections[projectionComponents[2]]; });
- zScaler = d3.scale.linear();
- zScaler.domain(zExtent).range(range);
- }
- }
- var positions = new Float32Array(ds.points.length * 3);
- var dst = 0;
- ds.points.forEach(function (d, i) {
- positions[dst++] =
- xScaler(ds.points[i].projections[projectionComponents[0]]);
- positions[dst++] =
- yScaler(ds.points[i].projections[projectionComponents[1]]);
- positions[dst++] = 0.0;
- });
- if (zScaler) {
- dst = 2;
- ds.points.forEach(function (d, i) {
- positions[dst] =
- zScaler(ds.points[i].projections[projectionComponents[2]]);
- dst += 3;
- });
- }
- return positions;
- };
- ProjectorScatterPlotAdapter.prototype.generateVisibleLabelRenderParams = function (ds, selectedPointIndices, neighborsOfFirstPoint, hoverPointIndex) {
- if (ds == null) {
- return null;
- }
- var selectedPointCount = (selectedPointIndices == null) ? 0 : selectedPointIndices.length;
- var neighborCount = (neighborsOfFirstPoint == null) ? 0 : neighborsOfFirstPoint.length;
- var n = selectedPointCount + neighborCount +
- ((hoverPointIndex != null) ? 1 : 0);
- var visibleLabels = new Uint32Array(n);
- var scale = new Float32Array(n);
- var opacityFlags = new Int8Array(n);
- var fillColors = new Uint8Array(n * 3);
- var strokeColors = new Uint8Array(n * 3);
- var labelStrings = [];
- scale.fill(LABEL_SCALE_DEFAULT);
- opacityFlags.fill(1);
- var dst = 0;
- if (hoverPointIndex != null) {
- labelStrings.push(this.getLabelText(ds, hoverPointIndex, this.labelPointAccessor));
- visibleLabels[dst] = hoverPointIndex;
- scale[dst] = LABEL_SCALE_LARGE;
- opacityFlags[dst] = 0;
- var fillRgb = styleRgbFromHexColor(LABEL_FILL_COLOR_HOVER);
- packRgbIntoUint8Array(fillColors, dst, fillRgb[0], fillRgb[1], fillRgb[2]);
- var strokeRgb = styleRgbFromHexColor(LABEL_STROKE_COLOR_HOVER);
- packRgbIntoUint8Array(strokeColors, dst, strokeRgb[0], strokeRgb[1], strokeRgb[1]);
- ++dst;
- }
- // Selected points
- {
- var n_1 = selectedPointCount;
- var fillRgb = styleRgbFromHexColor(LABEL_FILL_COLOR_SELECTED);
- var strokeRgb = styleRgbFromHexColor(LABEL_STROKE_COLOR_SELECTED);
- for (var i = 0; i < n_1; ++i) {
- var labelIndex = selectedPointIndices[i];
- labelStrings.push(this.getLabelText(ds, labelIndex, this.labelPointAccessor));
- visibleLabels[dst] = labelIndex;
- scale[dst] = LABEL_SCALE_LARGE;
- opacityFlags[dst] = (n_1 === 1) ? 0 : 1;
- packRgbIntoUint8Array(fillColors, dst, fillRgb[0], fillRgb[1], fillRgb[2]);
- packRgbIntoUint8Array(strokeColors, dst, strokeRgb[0], strokeRgb[1], strokeRgb[2]);
- ++dst;
- }
- }
- // Neighbors
- {
- var n_2 = neighborCount;
- var fillRgb = styleRgbFromHexColor(LABEL_FILL_COLOR_NEIGHBOR);
- var strokeRgb = styleRgbFromHexColor(LABEL_STROKE_COLOR_NEIGHBOR);
- for (var i = 0; i < n_2; ++i) {
- var labelIndex = neighborsOfFirstPoint[i].index;
- labelStrings.push(this.getLabelText(ds, labelIndex, this.labelPointAccessor));
- visibleLabels[dst] = labelIndex;
- packRgbIntoUint8Array(fillColors, dst, fillRgb[0], fillRgb[1], fillRgb[2]);
- packRgbIntoUint8Array(strokeColors, dst, strokeRgb[0], strokeRgb[1], strokeRgb[2]);
- ++dst;
- }
- }
- return new renderContext_1.LabelRenderParams(visibleLabels, labelStrings, scale, opacityFlags, LABEL_FONT_SIZE, fillColors, strokeColors);
- };
- ProjectorScatterPlotAdapter.prototype.generatePointScaleFactorArray = function (ds, selectedPointIndices, neighborsOfFirstPoint, hoverPointIndex) {
- if (ds == null) {
- return new Float32Array(0);
- }
- var scale = new Float32Array(ds.points.length);
- scale.fill(POINT_SCALE_DEFAULT);
- var selectedPointCount = (selectedPointIndices == null) ? 0 : selectedPointIndices.length;
- var neighborCount = (neighborsOfFirstPoint == null) ? 0 : neighborsOfFirstPoint.length;
- // Scale up all selected points.
- {
- var n = selectedPointCount;
- for (var i = 0; i < n; ++i) {
- var p = selectedPointIndices[i];
- scale[p] = POINT_SCALE_SELECTED;
- }
- }
- // Scale up the neighbor points.
- {
- var n = neighborCount;
- for (var i = 0; i < n; ++i) {
- var p = neighborsOfFirstPoint[i].index;
- scale[p] = POINT_SCALE_NEIGHBOR;
- }
- }
- // Scale up the hover point.
- if (hoverPointIndex != null) {
- scale[hoverPointIndex] = POINT_SCALE_HOVER;
- }
- return scale;
- };
- ProjectorScatterPlotAdapter.prototype.generateLineSegmentColorMap = function (ds, legendPointColorer) {
- var polylineColorArrayMap = {};
- if (ds == null) {
- return polylineColorArrayMap;
- }
- for (var i = 0; i < ds.sequences.length; i++) {
- var sequence = ds.sequences[i];
- var colors = new Float32Array(2 * (sequence.pointIndices.length - 1) * 3);
- var colorIndex = 0;
- if (legendPointColorer) {
- for (var j = 0; j < sequence.pointIndices.length - 1; j++) {
- var c1 = new THREE.Color(legendPointColorer(ds, sequence.pointIndices[j]));
- var c2 = new THREE.Color(legendPointColorer(ds, sequence.pointIndices[j + 1]));
- colors[colorIndex++] = c1.r;
- colors[colorIndex++] = c1.g;
- colors[colorIndex++] = c1.b;
- colors[colorIndex++] = c2.r;
- colors[colorIndex++] = c2.g;
- colors[colorIndex++] = c2.b;
- }
- }
- else {
- for (var j = 0; j < sequence.pointIndices.length - 1; j++) {
- var c1 = getDefaultPointInPolylineColor(j, sequence.pointIndices.length);
- var c2 = getDefaultPointInPolylineColor(j + 1, sequence.pointIndices.length);
- colors[colorIndex++] = c1.r;
- colors[colorIndex++] = c1.g;
- colors[colorIndex++] = c1.b;
- colors[colorIndex++] = c2.r;
- colors[colorIndex++] = c2.g;
- colors[colorIndex++] = c2.b;
- }
- }
- polylineColorArrayMap[i] = colors;
- }
- return polylineColorArrayMap;
- };
- ProjectorScatterPlotAdapter.prototype.generateLineSegmentOpacityArray = function (ds, selectedPoints) {
- if (ds == null) {
- return new Float32Array(0);
- }
- var opacities = new Float32Array(ds.sequences.length);
- var selectedPointCount = (selectedPoints == null) ? 0 : selectedPoints.length;
- if (selectedPointCount > 0) {
- opacities.fill(POLYLINE_DESELECTED_OPACITY);
- var i = ds.points[selectedPoints[0]].sequenceIndex;
- opacities[i] = POLYLINE_SELECTED_OPACITY;
- }
- else {
- opacities.fill(POLYLINE_DEFAULT_OPACITY);
- }
- return opacities;
- };
- ProjectorScatterPlotAdapter.prototype.generateLineSegmentWidthArray = function (ds, selectedPoints) {
- if (ds == null) {
- return new Float32Array(0);
- }
- var widths = new Float32Array(ds.sequences.length);
- widths.fill(POLYLINE_DEFAULT_LINEWIDTH);
- var selectedPointCount = (selectedPoints == null) ? 0 : selectedPoints.length;
- if (selectedPointCount > 0) {
- var i = ds.points[selectedPoints[0]].sequenceIndex;
- widths[i] = POLYLINE_SELECTED_LINEWIDTH;
- }
- return widths;
- };
- ProjectorScatterPlotAdapter.prototype.generatePointColorArray = function (ds, legendPointColorer, distFunc, selectedPointIndices, neighborsOfFirstPoint, hoverPointIndex, label3dMode, spriteImageMode) {
- if (ds == null) {
- return new Float32Array(0);
- }
- var selectedPointCount = (selectedPointIndices == null) ? 0 : selectedPointIndices.length;
- var neighborCount = (neighborsOfFirstPoint == null) ? 0 : neighborsOfFirstPoint.length;
- var colors = new Float32Array(ds.points.length * 3);
- var unselectedColor = POINT_COLOR_UNSELECTED;
- var noSelectionColor = POINT_COLOR_NO_SELECTION;
- if (label3dMode) {
- unselectedColor = LABELS_3D_COLOR_UNSELECTED;
- noSelectionColor = LABELS_3D_COLOR_NO_SELECTION;
- }
- if (spriteImageMode) {
- unselectedColor = SPRITE_IMAGE_COLOR_UNSELECTED;
- noSelectionColor = SPRITE_IMAGE_COLOR_NO_SELECTION;
- }
- // Give all points the unselected color.
- {
- var n = ds.points.length;
- var dst = 0;
- if (selectedPointCount > 0) {
- var c = new THREE.Color(unselectedColor);
- for (var i = 0; i < n; ++i) {
- colors[dst++] = c.r;
- colors[dst++] = c.g;
- colors[dst++] = c.b;
- }
- }
- else {
- if (legendPointColorer != null) {
- for (var i = 0; i < n; ++i) {
- var c = new THREE.Color(legendPointColorer(ds, i));
- colors[dst++] = c.r;
- colors[dst++] = c.g;
- colors[dst++] = c.b;
- }
- }
- else {
- var c = new THREE.Color(noSelectionColor);
- for (var i = 0; i < n; ++i) {
- colors[dst++] = c.r;
- colors[dst++] = c.g;
- colors[dst++] = c.b;
- }
- }
- }
- }
- // Color the selected points.
- {
- var n = selectedPointCount;
- var c = new THREE.Color(POINT_COLOR_SELECTED);
- for (var i = 0; i < n; ++i) {
- var dst = selectedPointIndices[i] * 3;
- colors[dst++] = c.r;
- colors[dst++] = c.g;
- colors[dst++] = c.b;
- }
- }
- // Color the neighbors.
- {
- var n = neighborCount;
- var minDist = n > 0 ? neighborsOfFirstPoint[0].dist : 0;
- for (var i = 0; i < n; ++i) {
- var c = new THREE.Color(dist2color(distFunc, neighborsOfFirstPoint[i].dist, minDist));
- var dst = neighborsOfFirstPoint[i].index * 3;
- colors[dst++] = c.r;
- colors[dst++] = c.g;
- colors[dst++] = c.b;
- }
- }
- // Color the hover point.
- if (hoverPointIndex != null) {
- var c = new THREE.Color(POINT_COLOR_HOVER);
- var dst = hoverPointIndex * 3;
- colors[dst++] = c.r;
- colors[dst++] = c.g;
- colors[dst++] = c.b;
- }
- return colors;
- };
- ProjectorScatterPlotAdapter.prototype.generate3DLabelsArray = function (ds, accessor) {
- if ((ds == null) || (accessor == null)) {
- return null;
- }
- var labels = [];
- var n = ds.points.length;
- for (var i = 0; i < n; ++i) {
- labels.push(this.getLabelText(ds, i, accessor));
- }
- return labels;
- };
- ProjectorScatterPlotAdapter.prototype.getLabelText = function (ds, i, accessor) {
- return ds.points[i].metadata[accessor].toString();
- };
- ProjectorScatterPlotAdapter.prototype.updateScatterPlotWithNewProjection = function (projection) {
- if (projection == null) {
- this.createVisualizers(this.renderLabelsIn3D);
- this.scatterPlot.render();
- return;
- }
- this.setDataSet(projection.dataSet);
- this.scatterPlot.setDimensions(projection.dimensionality);
- if (projection.dataSet.projectionCanBeRendered(projection.projectionType)) {
- this.updateScatterPlotAttributes();
- this.notifyProjectionPositionsUpdated();
- }
- this.scatterPlot.setCameraParametersForNextCameraCreation(null, false);
- };
- ProjectorScatterPlotAdapter.prototype.createVisualizers = function (inLabels3DMode) {
- var ds = (this.projection == null) ? null : this.projection.dataSet;
- var scatterPlot = this.scatterPlot;
- scatterPlot.removeAllVisualizers();
- this.labels3DVisualizer = null;
- this.canvasLabelsVisualizer = null;
- this.spriteVisualizer = null;
- this.polylineVisualizer = null;
- if (inLabels3DMode) {
- this.labels3DVisualizer = new scatterPlotVisualizer3DLabels_1.ScatterPlotVisualizer3DLabels();
- this.labels3DVisualizer.setLabelStrings(this.generate3DLabelsArray(ds, this.labelPointAccessor));
- }
- else {
- this.spriteVisualizer = new scatterPlotVisualizerSprites_1.ScatterPlotVisualizerSprites();
- scatterPlot.addVisualizer(this.spriteVisualizer);
- this.canvasLabelsVisualizer =
- new scatterPlotVisualizerCanvasLabels_1.ScatterPlotVisualizerCanvasLabels(this.scatterPlotContainer);
- }
- this.polylineVisualizer = new scatterPlotVisualizerPolylines_1.ScatterPlotVisualizerPolylines();
- this.setDataSet(ds);
- if (this.spriteVisualizer) {
- scatterPlot.addVisualizer(this.spriteVisualizer);
- }
- if (this.labels3DVisualizer) {
- scatterPlot.addVisualizer(this.labels3DVisualizer);
- }
- if (this.canvasLabelsVisualizer) {
- scatterPlot.addVisualizer(this.canvasLabelsVisualizer);
- }
- scatterPlot.addVisualizer(this.polylineVisualizer);
- };
- ProjectorScatterPlotAdapter.prototype.getSpriteImageMode = function () {
- if (this.projection == null) {
- return false;
- }
- var ds = this.projection.dataSet;
- if ((ds == null) || (ds.spriteAndMetadataInfo == null)) {
- return false;
- }
- return ds.spriteAndMetadataInfo.spriteImage != null;
- };
- return ProjectorScatterPlotAdapter;
-}());
-exports.ProjectorScatterPlotAdapter = ProjectorScatterPlotAdapter;
-function packRgbIntoUint8Array(rgbArray, labelIndex, r, g, b) {
- rgbArray[labelIndex * 3] = r;
- rgbArray[labelIndex * 3 + 1] = g;
- rgbArray[labelIndex * 3 + 2] = b;
-}
-function styleRgbFromHexColor(hex) {
- var c = new THREE.Color(hex);
- return [(c.r * 255) | 0, (c.g * 255) | 0, (c.b * 255) | 0];
-}
-function getDefaultPointInPolylineColor(index, totalPoints) {
- var hue = POLYLINE_START_HUE +
- (POLYLINE_END_HUE - POLYLINE_START_HUE) * index / totalPoints;
- var rgb = d3.hsl(hue, POLYLINE_SATURATION, POLYLINE_LIGHTNESS).rgb();
- return new THREE.Color(rgb.r / 255, rgb.g / 255, rgb.b / 255);
-}
-/**
- * Normalizes the distance so it can be visually encoded with color.
- * The normalization depends on the distance metric (cosine vs euclidean).
- */
-function normalizeDist(distFunc, d, minDist) {
- return (distFunc === vector.dist) ? (minDist / d) : (1 - d);
-}
-exports.normalizeDist = normalizeDist;
-/** Normalizes and encodes the provided distance with color. */
-function dist2color(distFunc, d, minDist) {
- return NN_COLOR_SCALE(normalizeDist(distFunc, d, minDist));
-}
-exports.dist2color = dist2color;
-
-},{"./renderContext":15,"./scatterPlot":16,"./scatterPlotVisualizer3DLabels":19,"./scatterPlotVisualizerCanvasLabels":20,"./scatterPlotVisualizerPolylines":21,"./scatterPlotVisualizerSprites":22,"./vector":25}],15:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http:www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-/**
- * LabelRenderParams describes the set of points that should have labels
- * rendered next to them.
- */
-var LabelRenderParams = (function () {
- function LabelRenderParams(pointIndices, labelStrings, scaleFactors, useSceneOpacityFlags, defaultFontSize, fillColors, strokeColors) {
- this.pointIndices = pointIndices;
- this.labelStrings = labelStrings;
- this.scaleFactors = scaleFactors;
- this.useSceneOpacityFlags = useSceneOpacityFlags;
- this.defaultFontSize = defaultFontSize;
- this.fillColors = fillColors;
- this.strokeColors = strokeColors;
- }
- return LabelRenderParams;
-}());
-exports.LabelRenderParams = LabelRenderParams;
-/** Details about the camera projection being used to render the scene. */
-(function (CameraType) {
- CameraType[CameraType["Perspective"] = 0] = "Perspective";
- CameraType[CameraType["Orthographic"] = 1] = "Orthographic";
-})(exports.CameraType || (exports.CameraType = {}));
-var CameraType = exports.CameraType;
-/**
- * RenderContext contains all of the state required to color and render the data
- * set. ScatterPlot passes this to every attached visualizer as part of the
- * render callback.
- * TODO(nicholsonc): This should only contain the data that's changed between
- * each frame. Data like colors / scale factors / labels should be reapplied
- * only when they change.
- */
-var RenderContext = (function () {
- function RenderContext(camera, cameraType, cameraTarget, screenWidth, screenHeight, nearestCameraSpacePointZ, farthestCameraSpacePointZ, backgroundColor, pointColors, pointScaleFactors, labels, polylineColors, polylineOpacities, polylineWidths) {
- this.camera = camera;
- this.cameraType = cameraType;
- this.cameraTarget = cameraTarget;
- this.screenWidth = screenWidth;
- this.screenHeight = screenHeight;
- this.nearestCameraSpacePointZ = nearestCameraSpacePointZ;
- this.farthestCameraSpacePointZ = farthestCameraSpacePointZ;
- this.backgroundColor = backgroundColor;
- this.pointColors = pointColors;
- this.pointScaleFactors = pointScaleFactors;
- this.labels = labels;
- this.polylineColors = polylineColors;
- this.polylineOpacities = polylineOpacities;
- this.polylineWidths = polylineWidths;
- }
- return RenderContext;
-}());
-exports.RenderContext = RenderContext;
-
-},{}],16:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var renderContext_1 = require('./renderContext');
-var scatterPlotRectangleSelector_1 = require('./scatterPlotRectangleSelector');
-var util = require('./util');
-var BACKGROUND_COLOR = 0xffffff;
-/**
- * The length of the cube (diameter of the circumscribing sphere) where all the
- * points live.
- */
-var CUBE_LENGTH = 2;
-var MAX_ZOOM = 5 * CUBE_LENGTH;
-var MIN_ZOOM = 0.025 * CUBE_LENGTH;
-// Constants relating to the camera parameters.
-var PERSP_CAMERA_FOV_VERTICAL = 70;
-var PERSP_CAMERA_NEAR_CLIP_PLANE = 0.01;
-var PERSP_CAMERA_FAR_CLIP_PLANE = 100;
-var ORTHO_CAMERA_FRUSTUM_HALF_EXTENT = 1.2;
-// Key presses.
-var SHIFT_KEY = 16;
-var CTRL_KEY = 17;
-var START_CAMERA_POS_3D = new THREE.Vector3(0.45, 0.9, 1.6);
-var START_CAMERA_TARGET_3D = new THREE.Vector3(0, 0, 0);
-var START_CAMERA_POS_2D = new THREE.Vector3(0, 0, 4);
-var START_CAMERA_TARGET_2D = new THREE.Vector3(0, 0, 0);
-var ORBIT_MOUSE_ROTATION_SPEED = 1;
-var ORBIT_ANIMATION_ROTATION_CYCLE_IN_SECONDS = 7;
-/** Supported modes of interaction. */
-(function (MouseMode) {
- MouseMode[MouseMode["AREA_SELECT"] = 0] = "AREA_SELECT";
- MouseMode[MouseMode["CAMERA_AND_CLICK_SELECT"] = 1] = "CAMERA_AND_CLICK_SELECT";
-})(exports.MouseMode || (exports.MouseMode = {}));
-var MouseMode = exports.MouseMode;
-/** Defines a camera, suitable for serialization. */
-var CameraDef = (function () {
- function CameraDef() {
- this.orthographic = false;
- }
- return CameraDef;
-}());
-exports.CameraDef = CameraDef;
-/**
- * Maintains a three.js instantiation and context,
- * animation state, and all other logic that's
- * independent of how a 3D scatter plot is actually rendered. Also holds an
- * array of visualizers and dispatches application events to them.
- */
-var ScatterPlot = (function () {
- function ScatterPlot(container, projectorEventContext) {
- var _this = this;
- this.visualizers = [];
- this.onCameraMoveListeners = [];
- this.backgroundColor = BACKGROUND_COLOR;
- this.dimensionality = 3;
- this.cameraDef = null;
- this.orbitAnimationOnNextCameraCreation = false;
- this.selecting = false;
- this.mouseIsDown = false;
- this.isDragSequence = false;
- this.containerNode = container.node();
- this.projectorEventContext = projectorEventContext;
- this.getLayoutValues();
- this.scene = new THREE.Scene();
- this.renderer = new THREE.WebGLRenderer({ alpha: true, premultipliedAlpha: false, antialias: false });
- this.renderer.setClearColor(BACKGROUND_COLOR, 1);
- this.containerNode.appendChild(this.renderer.domElement);
- this.light = new THREE.PointLight(0xFFECBF, 1, 0);
- this.scene.add(this.light);
- this.setDimensions(3);
- this.recreateCamera(this.makeDefaultCameraDef(this.dimensionality));
- this.renderer.render(this.scene, this.camera);
- this.rectangleSelector = new scatterPlotRectangleSelector_1.ScatterPlotRectangleSelector(this.containerNode, function (boundingBox) { return _this.selectBoundingBox(boundingBox); });
- this.addInteractionListeners();
- }
- ScatterPlot.prototype.addInteractionListeners = function () {
- this.containerNode.addEventListener('mousemove', this.onMouseMove.bind(this));
- this.containerNode.addEventListener('mousedown', this.onMouseDown.bind(this));
- this.containerNode.addEventListener('mouseup', this.onMouseUp.bind(this));
- this.containerNode.addEventListener('click', this.onClick.bind(this));
- window.addEventListener('keydown', this.onKeyDown.bind(this), false);
- window.addEventListener('keyup', this.onKeyUp.bind(this), false);
- };
- ScatterPlot.prototype.addCameraControlsEventListeners = function (cameraControls) {
- var _this = this;
- // Start is called when the user stars interacting with
- // controls.
- cameraControls.addEventListener('start', function () {
- _this.stopOrbitAnimation();
- _this.onCameraMoveListeners.forEach(function (l) { return l(_this.camera.position, cameraControls.target); });
- });
- // Change is called everytime the user interacts with the controls.
- cameraControls.addEventListener('change', function () {
- _this.render();
- });
- // End is called when the user stops interacting with the
- // controls (e.g. on mouse up, after dragging).
- cameraControls.addEventListener('end', function () { });
- };
- ScatterPlot.prototype.makeOrbitControls = function (camera, cameraDef, cameraIs3D) {
- if (this.orbitCameraControls != null) {
- this.orbitCameraControls.dispose();
- }
- var occ = new THREE.OrbitControls(camera, this.renderer.domElement);
- occ.target0 = new THREE.Vector3(cameraDef.target[0], cameraDef.target[1], cameraDef.target[2]);
- occ.position0 = new THREE.Vector3().copy(camera.position);
- occ.zoom0 = cameraDef.zoom;
- occ.enableRotate = cameraIs3D;
- occ.autoRotate = false;
- occ.rotateSpeed = ORBIT_MOUSE_ROTATION_SPEED;
- if (cameraIs3D) {
- occ.mouseButtons.ORBIT = THREE.MOUSE.LEFT;
- occ.mouseButtons.PAN = THREE.MOUSE.RIGHT;
- }
- else {
- occ.mouseButtons.ORBIT = null;
- occ.mouseButtons.PAN = THREE.MOUSE.LEFT;
- }
- occ.reset();
- this.camera = camera;
- this.orbitCameraControls = occ;
- this.addCameraControlsEventListeners(this.orbitCameraControls);
- };
- ScatterPlot.prototype.makeCamera3D = function (cameraDef, w, h) {
- var camera;
- {
- var aspectRatio = w / h;
- camera = new THREE.PerspectiveCamera(PERSP_CAMERA_FOV_VERTICAL, aspectRatio, PERSP_CAMERA_NEAR_CLIP_PLANE, PERSP_CAMERA_FAR_CLIP_PLANE);
- camera.position.set(cameraDef.position[0], cameraDef.position[1], cameraDef.position[2]);
- var at = new THREE.Vector3(cameraDef.target[0], cameraDef.target[1], cameraDef.target[2]);
- camera.lookAt(at);
- camera.zoom = cameraDef.zoom;
- camera.updateProjectionMatrix();
- }
- this.camera = camera;
- this.makeOrbitControls(camera, cameraDef, true);
- };
- ScatterPlot.prototype.makeCamera2D = function (cameraDef, w, h) {
- var camera;
- var target = new THREE.Vector3(cameraDef.target[0], cameraDef.target[1], cameraDef.target[2]);
- {
- var aspectRatio = w / h;
- var left = -ORTHO_CAMERA_FRUSTUM_HALF_EXTENT;
- var right = ORTHO_CAMERA_FRUSTUM_HALF_EXTENT;
- var bottom = -ORTHO_CAMERA_FRUSTUM_HALF_EXTENT;
- var top_1 = ORTHO_CAMERA_FRUSTUM_HALF_EXTENT;
- // Scale up the larger of (w, h) to match the aspect ratio.
- if (aspectRatio > 1) {
- left *= aspectRatio;
- right *= aspectRatio;
- }
- else {
- top_1 /= aspectRatio;
- bottom /= aspectRatio;
- }
- camera =
- new THREE.OrthographicCamera(left, right, top_1, bottom, -1000, 1000);
- camera.position.set(cameraDef.position[0], cameraDef.position[1], cameraDef.position[2]);
- camera.up = new THREE.Vector3(0, 1, 0);
- camera.lookAt(target);
- camera.zoom = cameraDef.zoom;
- camera.updateProjectionMatrix();
- }
- this.camera = camera;
- this.makeOrbitControls(camera, cameraDef, false);
- };
- ScatterPlot.prototype.makeDefaultCameraDef = function (dimensionality) {
- var def = new CameraDef();
- def.orthographic = (dimensionality === 2);
- def.zoom = 1.0;
- if (def.orthographic) {
- def.position =
- [START_CAMERA_POS_2D.x, START_CAMERA_POS_2D.y, START_CAMERA_POS_2D.z];
- def.target = [
- START_CAMERA_TARGET_2D.x, START_CAMERA_TARGET_2D.y,
- START_CAMERA_TARGET_2D.z
- ];
- }
- else {
- def.position =
- [START_CAMERA_POS_3D.x, START_CAMERA_POS_3D.y, START_CAMERA_POS_3D.z];
- def.target = [
- START_CAMERA_TARGET_3D.x, START_CAMERA_TARGET_3D.y,
- START_CAMERA_TARGET_3D.z
- ];
- }
- return def;
- };
- /** Recreate the scatter plot camera from a definition structure. */
- ScatterPlot.prototype.recreateCamera = function (cameraDef) {
- if (cameraDef.orthographic) {
- this.makeCamera2D(cameraDef, this.width, this.height);
- }
- else {
- this.makeCamera3D(cameraDef, this.width, this.height);
- }
- this.orbitCameraControls.minDistance = MIN_ZOOM;
- this.orbitCameraControls.maxDistance = MAX_ZOOM;
- this.orbitCameraControls.update();
- if (this.orbitAnimationOnNextCameraCreation) {
- this.startOrbitAnimation();
- }
- };
- ScatterPlot.prototype.onClick = function (e, notify) {
- if (notify === void 0) { notify = true; }
- if (e && this.selecting) {
- return;
- }
- // Only call event handlers if the click originated from the scatter plot.
- if (!this.isDragSequence && notify) {
- var selection = (this.nearestPoint != null) ? [this.nearestPoint] : [];
- this.projectorEventContext.notifySelectionChanged(selection);
- }
- this.isDragSequence = false;
- this.render();
- };
- ScatterPlot.prototype.onMouseDown = function (e) {
- this.isDragSequence = false;
- this.mouseIsDown = true;
- if (this.selecting) {
- this.orbitCameraControls.enabled = false;
- this.rectangleSelector.onMouseDown(e.offsetX, e.offsetY);
- this.setNearestPointToMouse(e);
- }
- else if (!e.ctrlKey && this.sceneIs3D() &&
- this.orbitCameraControls.mouseButtons.ORBIT === THREE.MOUSE.RIGHT) {
- // The user happened to press the ctrl key when the tab was active,
- // unpressed the ctrl when the tab was inactive, and now he/she
- // is back to the projector tab.
- this.orbitCameraControls.mouseButtons.ORBIT = THREE.MOUSE.LEFT;
- this.orbitCameraControls.mouseButtons.PAN = THREE.MOUSE.RIGHT;
- }
- else if (e.ctrlKey && this.sceneIs3D() &&
- this.orbitCameraControls.mouseButtons.ORBIT === THREE.MOUSE.LEFT) {
- // Similarly to the situation above.
- this.orbitCameraControls.mouseButtons.ORBIT = THREE.MOUSE.RIGHT;
- this.orbitCameraControls.mouseButtons.PAN = THREE.MOUSE.LEFT;
- }
- };
- /** When we stop dragging/zooming, return to normal behavior. */
- ScatterPlot.prototype.onMouseUp = function (e) {
- if (this.selecting) {
- this.orbitCameraControls.enabled = true;
- this.rectangleSelector.onMouseUp();
- this.render();
- }
- this.mouseIsDown = false;
- };
- /**
- * When the mouse moves, find the nearest point (if any) and send it to the
- * hoverlisteners (usually called from embedding.ts)
- */
- ScatterPlot.prototype.onMouseMove = function (e) {
- this.isDragSequence = this.mouseIsDown;
- // Depending if we're selecting or just navigating, handle accordingly.
- if (this.selecting && this.mouseIsDown) {
- this.rectangleSelector.onMouseMove(e.offsetX, e.offsetY);
- this.render();
- }
- else if (!this.mouseIsDown) {
- this.setNearestPointToMouse(e);
- this.projectorEventContext.notifyHoverOverPoint(this.nearestPoint);
- }
- };
- /** For using ctrl + left click as right click, and for circle select */
- ScatterPlot.prototype.onKeyDown = function (e) {
- // If ctrl is pressed, use left click to orbit
- if (e.keyCode === CTRL_KEY && this.sceneIs3D()) {
- this.orbitCameraControls.mouseButtons.ORBIT = THREE.MOUSE.RIGHT;
- this.orbitCameraControls.mouseButtons.PAN = THREE.MOUSE.LEFT;
- }
- // If shift is pressed, start selecting
- if (e.keyCode === SHIFT_KEY) {
- this.selecting = true;
- this.containerNode.style.cursor = 'crosshair';
- }
- };
- /** For using ctrl + left click as right click, and for circle select */
- ScatterPlot.prototype.onKeyUp = function (e) {
- if (e.keyCode === CTRL_KEY && this.sceneIs3D()) {
- this.orbitCameraControls.mouseButtons.ORBIT = THREE.MOUSE.LEFT;
- this.orbitCameraControls.mouseButtons.PAN = THREE.MOUSE.RIGHT;
- }
- // If shift is released, stop selecting
- if (e.keyCode === SHIFT_KEY) {
- this.selecting = (this.getMouseMode() === MouseMode.AREA_SELECT);
- if (!this.selecting) {
- this.containerNode.style.cursor = 'default';
- }
- this.render();
- }
- };
- /**
- * Returns a list of indices of points in a bounding box from the picking
- * texture.
- * @param boundingBox The bounding box to select from.
- */
- ScatterPlot.prototype.getPointIndicesFromPickingTexture = function (boundingBox) {
- if (this.worldSpacePointPositions == null) {
- return null;
- }
- var pointCount = this.worldSpacePointPositions.length / 3;
- var dpr = window.devicePixelRatio || 1;
- var x = Math.floor(boundingBox.x * dpr);
- var y = Math.floor(boundingBox.y * dpr);
- var width = Math.floor(boundingBox.width * dpr);
- var height = Math.floor(boundingBox.height * dpr);
- // Create buffer for reading all of the pixels from the texture.
- var pixelBuffer = new Uint8Array(width * height * 4);
- // Read the pixels from the bounding box.
- this.renderer.readRenderTargetPixels(this.pickingTexture, x, this.pickingTexture.height - y, width, height, pixelBuffer);
- // Keep a flat list of each point and whether they are selected or not. This
- // approach is more efficient than using an object keyed by the index.
- var pointIndicesSelection = new Uint8Array(this.worldSpacePointPositions.length);
- for (var i = 0; i < width * height; i++) {
- var id = (pixelBuffer[i * 4] << 16) | (pixelBuffer[i * 4 + 1] << 8) |
- pixelBuffer[i * 4 + 2];
- if (id !== 0xffffff && (id < pointCount)) {
- pointIndicesSelection[id] = 1;
- }
- }
- var pointIndices = [];
- for (var i = 0; i < pointIndicesSelection.length; i++) {
- if (pointIndicesSelection[i] === 1) {
- pointIndices.push(i);
- }
- }
- return pointIndices;
- };
- ScatterPlot.prototype.selectBoundingBox = function (boundingBox) {
- var pointIndices = this.getPointIndicesFromPickingTexture(boundingBox);
- this.projectorEventContext.notifySelectionChanged(pointIndices);
- };
- ScatterPlot.prototype.setNearestPointToMouse = function (e) {
- if (this.pickingTexture == null) {
- this.nearestPoint = null;
- return;
- }
- var boundingBox = { x: e.offsetX, y: e.offsetY, width: 1, height: 1 };
- var pointIndices = this.getPointIndicesFromPickingTexture(boundingBox);
- this.nearestPoint = (pointIndices != null) ? pointIndices[0] : null;
- };
- ScatterPlot.prototype.getLayoutValues = function () {
- this.width = this.containerNode.offsetWidth;
- this.height = Math.max(1, this.containerNode.offsetHeight);
- return [this.width, this.height];
- };
- ScatterPlot.prototype.sceneIs3D = function () {
- return this.dimensionality === 3;
- };
- ScatterPlot.prototype.remove3dAxisFromScene = function () {
- var axes = this.scene.getObjectByName('axes');
- if (axes != null) {
- this.scene.remove(axes);
- }
- return axes;
- };
- ScatterPlot.prototype.add3dAxis = function () {
- var axes = new THREE.AxisHelper();
- axes.name = 'axes';
- this.scene.add(axes);
- };
- /** Set 2d vs 3d mode. */
- ScatterPlot.prototype.setDimensions = function (dimensionality) {
- if ((dimensionality !== 2) && (dimensionality !== 3)) {
- throw new RangeError('dimensionality must be 2 or 3');
- }
- this.dimensionality = dimensionality;
- var def = this.cameraDef || this.makeDefaultCameraDef(dimensionality);
- this.recreateCamera(def);
- this.remove3dAxisFromScene();
- if (dimensionality === 3) {
- this.add3dAxis();
- }
- };
- /** Gets the current camera information, suitable for serialization. */
- ScatterPlot.prototype.getCameraDef = function () {
- var def = new CameraDef();
- var pos = this.camera.position;
- var tgt = this.orbitCameraControls.target;
- def.orthographic = !this.sceneIs3D();
- def.position = [pos.x, pos.y, pos.z];
- def.target = [tgt.x, tgt.y, tgt.z];
- def.zoom = this.camera.zoom;
- return def;
- };
- /** Sets parameters for the next camera recreation. */
- ScatterPlot.prototype.setCameraParametersForNextCameraCreation = function (def, orbitAnimation) {
- this.cameraDef = def;
- this.orbitAnimationOnNextCameraCreation = orbitAnimation;
- };
- /** Gets the current camera position. */
- ScatterPlot.prototype.getCameraPosition = function () {
- var currPos = this.camera.position;
- return [currPos.x, currPos.y, currPos.z];
- };
- /** Gets the current camera target. */
- ScatterPlot.prototype.getCameraTarget = function () {
- var currTarget = this.orbitCameraControls.target;
- return [currTarget.x, currTarget.y, currTarget.z];
- };
- /** Sets up the camera from given position and target coordinates. */
- ScatterPlot.prototype.setCameraPositionAndTarget = function (position, target) {
- this.stopOrbitAnimation();
- this.camera.position.set(position[0], position[1], position[2]);
- this.orbitCameraControls.target.set(target[0], target[1], target[2]);
- this.orbitCameraControls.update();
- this.render();
- };
- /** Starts orbiting the camera around its current lookat target. */
- ScatterPlot.prototype.startOrbitAnimation = function () {
- if (!this.sceneIs3D()) {
- return;
- }
- if (this.orbitAnimationId != null) {
- this.stopOrbitAnimation();
- }
- this.orbitCameraControls.autoRotate = true;
- this.orbitCameraControls.rotateSpeed =
- ORBIT_ANIMATION_ROTATION_CYCLE_IN_SECONDS;
- this.updateOrbitAnimation();
- };
- ScatterPlot.prototype.updateOrbitAnimation = function () {
- var _this = this;
- this.orbitCameraControls.update();
- this.orbitAnimationId =
- requestAnimationFrame(function () { return _this.updateOrbitAnimation(); });
- };
- /** Stops the orbiting animation on the camera. */
- ScatterPlot.prototype.stopOrbitAnimation = function () {
- this.orbitCameraControls.autoRotate = false;
- this.orbitCameraControls.rotateSpeed = ORBIT_MOUSE_ROTATION_SPEED;
- if (this.orbitAnimationId != null) {
- cancelAnimationFrame(this.orbitAnimationId);
- this.orbitAnimationId = null;
- }
- };
- /** Adds a visualizer to the set, will start dispatching events to it */
- ScatterPlot.prototype.addVisualizer = function (visualizer) {
- if (this.scene) {
- visualizer.setScene(this.scene);
- }
- visualizer.onResize(this.width, this.height);
- visualizer.onPointPositionsChanged(this.worldSpacePointPositions);
- this.visualizers.push(visualizer);
- };
- /** Removes all visualizers attached to this scatter plot. */
- ScatterPlot.prototype.removeAllVisualizers = function () {
- this.visualizers.forEach(function (v) { return v.dispose(); });
- this.visualizers = [];
- };
- /** Update scatter plot with a new array of packed xyz point positions. */
- ScatterPlot.prototype.setPointPositions = function (worldSpacePointPositions) {
- this.worldSpacePointPositions = worldSpacePointPositions;
- this.visualizers.forEach(function (v) { return v.onPointPositionsChanged(worldSpacePointPositions); });
- };
- ScatterPlot.prototype.render = function () {
- {
- var lightPos = this.camera.position.clone();
- lightPos.x += 1;
- lightPos.y += 1;
- this.light.position.set(lightPos.x, lightPos.y, lightPos.z);
- }
- var cameraType = (this.camera instanceof THREE.PerspectiveCamera) ?
- renderContext_1.CameraType.Perspective :
- renderContext_1.CameraType.Orthographic;
- var cameraSpacePointExtents = [0, 0];
- if (this.worldSpacePointPositions != null) {
- cameraSpacePointExtents = util.getNearFarPoints(this.worldSpacePointPositions, this.camera.position, this.orbitCameraControls.target);
- }
- var rc = new renderContext_1.RenderContext(this.camera, cameraType, this.orbitCameraControls.target, this.width, this.height, cameraSpacePointExtents[0], cameraSpacePointExtents[1], this.backgroundColor, this.pointColors, this.pointScaleFactors, this.labels, this.polylineColors, this.polylineOpacities, this.polylineWidths);
- // Render first pass to picking target. This render fills pickingTexture
- // with colors that are actually point ids, so that sampling the texture at
- // the mouse's current x,y coordinates will reveal the data point that the
- // mouse is over.
- this.visualizers.forEach(function (v) { return v.onPickingRender(rc); });
- {
- var axes = this.remove3dAxisFromScene();
- this.renderer.render(this.scene, this.camera, this.pickingTexture);
- if (axes != null) {
- this.scene.add(axes);
- }
- }
- // Render second pass to color buffer, to be displayed on the canvas.
- this.visualizers.forEach(function (v) { return v.onRender(rc); });
- this.renderer.render(this.scene, this.camera);
- };
- ScatterPlot.prototype.setMouseMode = function (mouseMode) {
- this.mouseMode = mouseMode;
- if (mouseMode === MouseMode.AREA_SELECT) {
- this.selecting = true;
- this.containerNode.style.cursor = 'crosshair';
- }
- else {
- this.selecting = false;
- this.containerNode.style.cursor = 'default';
- }
- };
- /** Set the colors for every data point. (RGB triplets) */
- ScatterPlot.prototype.setPointColors = function (colors) {
- this.pointColors = colors;
- };
- /** Set the scale factors for every data point. (scalars) */
- ScatterPlot.prototype.setPointScaleFactors = function (scaleFactors) {
- this.pointScaleFactors = scaleFactors;
- };
- /** Set the labels to rendered */
- ScatterPlot.prototype.setLabels = function (labels) {
- this.labels = labels;
- };
- /** Set the colors for every data polyline. (RGB triplets) */
- ScatterPlot.prototype.setPolylineColors = function (colors) {
- this.polylineColors = colors;
- };
- ScatterPlot.prototype.setPolylineOpacities = function (opacities) {
- this.polylineOpacities = opacities;
- };
- ScatterPlot.prototype.setPolylineWidths = function (widths) {
- this.polylineWidths = widths;
- };
- ScatterPlot.prototype.getMouseMode = function () {
- return this.mouseMode;
- };
- ScatterPlot.prototype.resetZoom = function () {
- this.recreateCamera(this.makeDefaultCameraDef(this.dimensionality));
- this.render();
- };
- ScatterPlot.prototype.setDayNightMode = function (isNight) {
- d3.select(this.containerNode)
- .selectAll('canvas')
- .style('filter', isNight ? 'invert(100%)' : null);
- };
- ScatterPlot.prototype.resize = function (render) {
- if (render === void 0) { render = true; }
- var _a = [this.width, this.height], oldW = _a[0], oldH = _a[1];
- var _b = this.getLayoutValues(), newW = _b[0], newH = _b[1];
- if (this.dimensionality === 3) {
- var camera = this.camera;
- camera.aspect = newW / newH;
- camera.updateProjectionMatrix();
- }
- else {
- var camera = this.camera;
- // Scale the ortho frustum by however much the window changed.
- var scaleW = newW / oldW;
- var scaleH = newH / oldH;
- var newCamHalfWidth = ((camera.right - camera.left) * scaleW) / 2;
- var newCamHalfHeight = ((camera.top - camera.bottom) * scaleH) / 2;
- camera.top = newCamHalfHeight;
- camera.bottom = -newCamHalfHeight;
- camera.left = -newCamHalfWidth;
- camera.right = newCamHalfWidth;
- camera.updateProjectionMatrix();
- }
- // Accouting for retina displays.
- var dpr = window.devicePixelRatio || 1;
- this.renderer.setPixelRatio(dpr);
- this.renderer.setSize(newW, newH);
- // the picking texture needs to be exactly the same as the render texture.
- {
- var renderCanvasSize = this.renderer.getSize();
- var pixelRatio = this.renderer.getPixelRatio();
- this.pickingTexture = new THREE.WebGLRenderTarget(renderCanvasSize.width * pixelRatio, renderCanvasSize.height * pixelRatio);
- this.pickingTexture.texture.minFilter = THREE.LinearFilter;
- }
- this.visualizers.forEach(function (v) { return v.onResize(newW, newH); });
- if (render) {
- this.render();
- }
- ;
- };
- ScatterPlot.prototype.onCameraMove = function (listener) {
- this.onCameraMoveListeners.push(listener);
- };
- ScatterPlot.prototype.clickOnPoint = function (pointIndex) {
- this.nearestPoint = pointIndex;
- this.onClick(null, false);
- };
- return ScatterPlot;
-}());
-exports.ScatterPlot = ScatterPlot;
-
-},{"./renderContext":15,"./scatterPlotRectangleSelector":17,"./util":24}],17:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var FILL = '#dddddd';
-var FILL_OPACITY = .2;
-var STROKE = '#aaaaaa';
-var STROKE_WIDTH = 2;
-var STROKE_DASHARRAY = '10 5';
-/**
- * A class that manages and renders a data selection rectangle.
- */
-var ScatterPlotRectangleSelector = (function () {
- /**
- * @param container The container HTML element that the selection SVG rect
- * will be a child of.
- * @param selectionCallback The callback that accepts a bounding box to be
- * called when selection changes. Currently, we only call the callback on
- * mouseUp.
- */
- function ScatterPlotRectangleSelector(container, selectionCallback) {
- this.svgElement = d3.select(container).select('#selector');
- this.rectElement = this.svgElement.append('rect')
- .style('stroke', STROKE)
- .style('stroke-dasharray', STROKE_DASHARRAY)
- .style('stroke-width', STROKE_WIDTH)
- .style('fill', FILL)
- .style('fill-opacity', FILL_OPACITY);
- this.selectionCallback = selectionCallback;
- this.isMouseDown = false;
- }
- ScatterPlotRectangleSelector.prototype.onMouseDown = function (offsetX, offsetY) {
- this.isMouseDown = true;
- this.svgElement.style('display', 'block');
- this.startCoordinates = [offsetX, offsetY];
- this.lastBoundingBox = {
- x: this.startCoordinates[0],
- y: this.startCoordinates[1],
- width: 1,
- height: 1
- };
- };
- ScatterPlotRectangleSelector.prototype.onMouseMove = function (offsetX, offsetY) {
- if (!this.isMouseDown) {
- return;
- }
- this.lastBoundingBox.x = Math.min(offsetX, this.startCoordinates[0]);
- this.lastBoundingBox.y = Math.max(offsetY, this.startCoordinates[1]);
- this.lastBoundingBox.width =
- Math.max(offsetX, this.startCoordinates[0]) - this.lastBoundingBox.x;
- this.lastBoundingBox.height =
- this.lastBoundingBox.y - Math.min(offsetY, this.startCoordinates[1]);
- this.rectElement.attr({
- x: this.lastBoundingBox.x,
- y: this.lastBoundingBox.y - this.lastBoundingBox.height,
- width: this.lastBoundingBox.width,
- height: this.lastBoundingBox.height
- });
- };
- ScatterPlotRectangleSelector.prototype.onMouseUp = function () {
- this.isMouseDown = false;
- this.svgElement.style('display', 'none');
- this.rectElement.attr('width', 0);
- this.rectElement.attr('height', 0);
- this.selectionCallback(this.lastBoundingBox);
- };
- return ScatterPlotRectangleSelector;
-}());
-exports.ScatterPlotRectangleSelector = ScatterPlotRectangleSelector;
-
-},{}],18:[function(require,module,exports){
-arguments[4][13][0].apply(exports,arguments)
-},{"dup":13}],19:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var util = require('./util');
-var FONT_SIZE = 80;
-var ONE_OVER_FONT_SIZE = 1 / FONT_SIZE;
-var LABEL_SCALE = 2.2; // at 1:1 texel/pixel ratio
-var LABEL_COLOR = 'black';
-var LABEL_BACKGROUND = 'white';
-var MAX_CANVAS_DIMENSION = 8192;
-var NUM_GLYPHS = 256;
-var RGB_ELEMENTS_PER_ENTRY = 3;
-var XYZ_ELEMENTS_PER_ENTRY = 3;
-var UV_ELEMENTS_PER_ENTRY = 2;
-var VERTICES_PER_GLYPH = 2 * 3; // 2 triangles, 3 verts per triangle
-/**
- * Each label is made up of triangles (two per letter.) Each vertex, then, is
- * the corner of one of these triangles (and thus the corner of a letter
- * rectangle.)
- * Each has the following attributes:
- * posObj: The (x, y) position of the vertex within the label, where the
- * bottom center of the word is positioned at (0, 0);
- * position: The position of the label in worldspace.
- * vUv: The (u, v) coordinates that index into the glyphs sheet (range 0, 1.)
- * color: The color of the label (matches the cooresponding point's color.)
- * wordShown: Boolean. Whether or not the label is visible.
- */
-var VERTEX_SHADER = "\n attribute vec2 posObj;\n attribute vec3 color;\n varying vec2 vUv;\n varying vec3 vColor;\n\n void main() {\n vUv = uv;\n vColor = color;\n\n // Rotate label to face camera.\n\n vec4 vRight = vec4(\n modelViewMatrix[0][0], modelViewMatrix[1][0], modelViewMatrix[2][0], 0);\n\n vec4 vUp = vec4(\n modelViewMatrix[0][1], modelViewMatrix[1][1], modelViewMatrix[2][1], 0);\n\n vec4 vAt = -vec4(\n modelViewMatrix[0][2], modelViewMatrix[1][2], modelViewMatrix[2][2], 0);\n\n mat4 pointToCamera = mat4(vRight, vUp, vAt, vec4(0, 0, 0, 1));\n\n vec2 scaledPos = posObj * " + ONE_OVER_FONT_SIZE + " * " + LABEL_SCALE + ";\n\n vec4 posRotated = pointToCamera * vec4(scaledPos, 0, 1);\n vec4 mvPosition = modelViewMatrix * (vec4(position, 0) + posRotated);\n gl_Position = projectionMatrix * mvPosition;\n }";
-var FRAGMENT_SHADER = "\n uniform sampler2D texture;\n uniform bool picking;\n varying vec2 vUv;\n varying vec3 vColor;\n\n void main() {\n if (picking) {\n gl_FragColor = vec4(vColor, 1.0);\n } else {\n vec4 fromTexture = texture2D(texture, vUv);\n gl_FragColor = vec4(vColor, 1.0) * fromTexture;\n }\n }";
-/**
- * Renders the text labels as 3d geometry in the world.
- */
-var ScatterPlotVisualizer3DLabels = (function () {
- function ScatterPlotVisualizer3DLabels() {
- }
- ScatterPlotVisualizer3DLabels.prototype.createGlyphTexture = function () {
- var canvas = document.createElement('canvas');
- canvas.width = MAX_CANVAS_DIMENSION;
- canvas.height = FONT_SIZE;
- var ctx = canvas.getContext('2d');
- ctx.font = 'bold ' + FONT_SIZE * 0.75 + 'px roboto';
- ctx.textBaseline = 'top';
- ctx.fillStyle = LABEL_BACKGROUND;
- ctx.rect(0, 0, canvas.width, canvas.height);
- ctx.fill();
- ctx.fillStyle = LABEL_COLOR;
- var spaceOffset = ctx.measureText(' ').width;
- // For each letter, store length, position at the encoded index.
- var glyphLengths = new Float32Array(NUM_GLYPHS);
- var glyphOffset = new Float32Array(NUM_GLYPHS);
- var leftCoord = 0;
- for (var i = 0; i < NUM_GLYPHS; i++) {
- var text = ' ' + String.fromCharCode(i);
- var textLength = ctx.measureText(text).width;
- glyphLengths[i] = textLength - spaceOffset;
- glyphOffset[i] = leftCoord;
- ctx.fillText(text, leftCoord - spaceOffset, 0);
- leftCoord += textLength;
- }
- var tex = util.createTexture(canvas);
- return { texture: tex, lengths: glyphLengths, offsets: glyphOffset };
- };
- ScatterPlotVisualizer3DLabels.prototype.processLabelVerts = function (pointCount) {
- var numTotalLetters = 0;
- this.labelVertexMap = [];
- for (var i = 0; i < pointCount; i++) {
- var label = this.labelStrings[i];
- var vertsArray = [];
- for (var j = 0; j < label.length; j++) {
- for (var k = 0; k < VERTICES_PER_GLYPH; k++) {
- vertsArray.push(numTotalLetters * VERTICES_PER_GLYPH + k);
- }
- numTotalLetters++;
- }
- this.labelVertexMap.push(vertsArray);
- }
- this.totalVertexCount = numTotalLetters * VERTICES_PER_GLYPH;
- };
- ScatterPlotVisualizer3DLabels.prototype.createColorBuffers = function (pointCount) {
- var _this = this;
- this.pickingColors =
- new Float32Array(this.totalVertexCount * RGB_ELEMENTS_PER_ENTRY);
- this.renderColors =
- new Float32Array(this.totalVertexCount * RGB_ELEMENTS_PER_ENTRY);
- var _loop_1 = function(i) {
- var color = new THREE.Color(i);
- this_1.labelVertexMap[i].forEach(function (j) {
- _this.pickingColors[RGB_ELEMENTS_PER_ENTRY * j] = color.r;
- _this.pickingColors[RGB_ELEMENTS_PER_ENTRY * j + 1] = color.g;
- _this.pickingColors[RGB_ELEMENTS_PER_ENTRY * j + 2] = color.b;
- _this.renderColors[RGB_ELEMENTS_PER_ENTRY * j] = 1.0;
- _this.renderColors[RGB_ELEMENTS_PER_ENTRY * j + 1] = 1.0;
- _this.renderColors[RGB_ELEMENTS_PER_ENTRY * j + 2] = 1.0;
- });
- };
- var this_1 = this;
- for (var i = 0; i < pointCount; i++) {
- _loop_1(i);
- }
- };
- ScatterPlotVisualizer3DLabels.prototype.createLabels = function () {
- var _this = this;
- if ((this.labelStrings == null) ||
- (this.worldSpacePointPositions == null)) {
- return;
- }
- var pointCount = this.worldSpacePointPositions.length / XYZ_ELEMENTS_PER_ENTRY;
- if (pointCount !== this.labelStrings.length) {
- return;
- }
- this.glyphTexture = this.createGlyphTexture();
- this.uniforms = {
- texture: { type: 't' },
- picking: { type: 'bool' },
- };
- this.material = new THREE.ShaderMaterial({
- uniforms: this.uniforms,
- transparent: true,
- vertexShader: VERTEX_SHADER,
- fragmentShader: FRAGMENT_SHADER,
- });
- this.processLabelVerts(pointCount);
- this.createColorBuffers(pointCount);
- var positionArray = new Float32Array(this.totalVertexCount * XYZ_ELEMENTS_PER_ENTRY);
- this.positions =
- new THREE.BufferAttribute(positionArray, XYZ_ELEMENTS_PER_ENTRY);
- var posArray = new Float32Array(this.totalVertexCount * XYZ_ELEMENTS_PER_ENTRY);
- var uvArray = new Float32Array(this.totalVertexCount * UV_ELEMENTS_PER_ENTRY);
- var colorsArray = new Float32Array(this.totalVertexCount * RGB_ELEMENTS_PER_ENTRY);
- var positionObject = new THREE.BufferAttribute(posArray, 2);
- var uv = new THREE.BufferAttribute(uvArray, UV_ELEMENTS_PER_ENTRY);
- var colors = new THREE.BufferAttribute(colorsArray, RGB_ELEMENTS_PER_ENTRY);
- this.geometry = new THREE.BufferGeometry();
- this.geometry.addAttribute('posObj', positionObject);
- this.geometry.addAttribute('position', this.positions);
- this.geometry.addAttribute('uv', uv);
- this.geometry.addAttribute('color', colors);
- var lettersSoFar = 0;
- for (var i = 0; i < pointCount; i++) {
- var label = this.labelStrings[i];
- var leftOffset = 0;
- // Determine length of word in pixels.
- for (var j = 0; j < label.length; j++) {
- var letterCode = label.charCodeAt(j);
- leftOffset += this.glyphTexture.lengths[letterCode];
- }
- leftOffset /= -2; // centers text horizontally around the origin
- for (var j = 0; j < label.length; j++) {
- var letterCode = label.charCodeAt(j);
- var letterWidth = this.glyphTexture.lengths[letterCode];
- var scale = FONT_SIZE;
- var right = (leftOffset + letterWidth) / scale;
- var left = (leftOffset) / scale;
- var top_1 = FONT_SIZE / scale;
- // First triangle
- positionObject.setXY(lettersSoFar * VERTICES_PER_GLYPH + 0, left, 0);
- positionObject.setXY(lettersSoFar * VERTICES_PER_GLYPH + 1, right, 0);
- positionObject.setXY(lettersSoFar * VERTICES_PER_GLYPH + 2, left, top_1);
- // Second triangle
- positionObject.setXY(lettersSoFar * VERTICES_PER_GLYPH + 3, left, top_1);
- positionObject.setXY(lettersSoFar * VERTICES_PER_GLYPH + 4, right, 0);
- positionObject.setXY(lettersSoFar * VERTICES_PER_GLYPH + 5, right, top_1);
- // Set UVs based on letter.
- var uLeft = (this.glyphTexture.offsets[letterCode]);
- var uRight = (this.glyphTexture.offsets[letterCode] + letterWidth);
- // Scale so that uvs lie between 0 and 1 on the texture.
- uLeft /= MAX_CANVAS_DIMENSION;
- uRight /= MAX_CANVAS_DIMENSION;
- var vTop = 1;
- var vBottom = 0;
- uv.setXY(lettersSoFar * VERTICES_PER_GLYPH + 0, uLeft, vTop);
- uv.setXY(lettersSoFar * VERTICES_PER_GLYPH + 1, uRight, vTop);
- uv.setXY(lettersSoFar * VERTICES_PER_GLYPH + 2, uLeft, vBottom);
- uv.setXY(lettersSoFar * VERTICES_PER_GLYPH + 3, uLeft, vBottom);
- uv.setXY(lettersSoFar * VERTICES_PER_GLYPH + 4, uRight, vTop);
- uv.setXY(lettersSoFar * VERTICES_PER_GLYPH + 5, uRight, vBottom);
- lettersSoFar++;
- leftOffset += letterWidth;
- }
- }
- var _loop_2 = function(i) {
- var p = util.vector3FromPackedArray(this_2.worldSpacePointPositions, i);
- this_2.labelVertexMap[i].forEach(function (j) {
- _this.positions.setXYZ(j, p.x, p.y, p.z);
- });
- };
- var this_2 = this;
- for (var i = 0; i < pointCount; i++) {
- _loop_2(i);
- }
- ;
- this.labelsMesh = new THREE.Mesh(this.geometry, this.material);
- this.labelsMesh.frustumCulled = false;
- this.scene.add(this.labelsMesh);
- };
- ScatterPlotVisualizer3DLabels.prototype.colorLabels = function (pointColors) {
- if (this.labelStrings == null || this.geometry == null ||
- pointColors == null) {
- return;
- }
- var colors = this.geometry.getAttribute('color');
- colors.array = this.renderColors;
- var n = pointColors.length / XYZ_ELEMENTS_PER_ENTRY;
- var src = 0;
- for (var i = 0; i < n; ++i) {
- var c = new THREE.Color(pointColors[src], pointColors[src + 1], pointColors[src + 2]);
- var m = this.labelVertexMap[i].length;
- for (var j = 0; j < m; ++j) {
- colors.setXYZ(this.labelVertexMap[i][j], c.r, c.g, c.b);
- }
- src += RGB_ELEMENTS_PER_ENTRY;
- }
- colors.needsUpdate = true;
- };
- ScatterPlotVisualizer3DLabels.prototype.setScene = function (scene) {
- this.scene = scene;
- };
- ScatterPlotVisualizer3DLabels.prototype.dispose = function () {
- if (this.labelsMesh) {
- if (this.scene) {
- this.scene.remove(this.labelsMesh);
- }
- this.labelsMesh = null;
- }
- if (this.geometry) {
- this.geometry.dispose();
- this.geometry = null;
- }
- if ((this.glyphTexture != null) && (this.glyphTexture.texture != null)) {
- this.glyphTexture.texture.dispose();
- this.glyphTexture.texture = null;
- }
- };
- ScatterPlotVisualizer3DLabels.prototype.onPickingRender = function (rc) {
- if (this.geometry == null) {
- this.createLabels();
- }
- if (this.geometry == null) {
- return;
- }
- this.material.uniforms.texture.value = this.glyphTexture.texture;
- this.material.uniforms.picking.value = true;
- var colors = this.geometry.getAttribute('color');
- colors.array = this.pickingColors;
- colors.needsUpdate = true;
- };
- ScatterPlotVisualizer3DLabels.prototype.onRender = function (rc) {
- if (this.geometry == null) {
- this.createLabels();
- }
- if (this.geometry == null) {
- return;
- }
- this.colorLabels(rc.pointColors);
- this.material.uniforms.texture.value = this.glyphTexture.texture;
- this.material.uniforms.picking.value = false;
- var colors = this.geometry.getAttribute('color');
- colors.array = this.renderColors;
- colors.needsUpdate = true;
- };
- ScatterPlotVisualizer3DLabels.prototype.onPointPositionsChanged = function (newPositions) {
- this.worldSpacePointPositions = newPositions;
- this.dispose();
- };
- ScatterPlotVisualizer3DLabels.prototype.setLabelStrings = function (labelStrings) {
- this.labelStrings = labelStrings;
- this.dispose();
- };
- ScatterPlotVisualizer3DLabels.prototype.onResize = function (newWidth, newHeight) { };
- return ScatterPlotVisualizer3DLabels;
-}());
-exports.ScatterPlotVisualizer3DLabels = ScatterPlotVisualizer3DLabels;
-
-},{"./util":24}],20:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var label_1 = require('./label');
-var renderContext_1 = require('./renderContext');
-var util = require('./util');
-var MAX_LABELS_ON_SCREEN = 10000;
-var LABEL_STROKE_WIDTH = 3;
-var LABEL_FILL_WIDTH = 6;
-/**
- * Creates and maintains a 2d canvas on top of the GL canvas. All labels, when
- * active, are rendered to the 2d canvas as part of the visible render pass.
- */
-var ScatterPlotVisualizerCanvasLabels = (function () {
- function ScatterPlotVisualizerCanvasLabels(container) {
- this.labelsActive = true;
- this.canvas = container.append('canvas').node();
- this.gc = this.canvas.getContext('2d');
- d3.select(this.canvas).style({ position: 'absolute', left: 0, top: 0 });
- this.canvas.style.pointerEvents = 'none';
- }
- ScatterPlotVisualizerCanvasLabels.prototype.removeAllLabels = function () {
- var pixelWidth = this.canvas.width * window.devicePixelRatio;
- var pixelHeight = this.canvas.height * window.devicePixelRatio;
- this.gc.clearRect(0, 0, pixelWidth, pixelHeight);
- };
- /** Render all of the non-overlapping visible labels to the canvas. */
- ScatterPlotVisualizerCanvasLabels.prototype.makeLabels = function (rc) {
- if ((rc.labels == null) || (rc.labels.pointIndices.length === 0)) {
- return;
- }
- if (this.worldSpacePointPositions == null) {
- return;
- }
- var lrc = rc.labels;
- var sceneIs3D = (rc.cameraType === renderContext_1.CameraType.Perspective);
- var labelHeight = parseInt(this.gc.font, 10);
- var dpr = window.devicePixelRatio;
- var grid;
- {
- var pixw = this.canvas.width * dpr;
- var pixh = this.canvas.height * dpr;
- var bb = { loX: 0, hiX: pixw, loY: 0, hiY: pixh };
- grid = new label_1.CollisionGrid(bb, pixw / 25, pixh / 50);
- }
- var opacityMap = d3.scale.pow()
- .exponent(Math.E)
- .domain([rc.farthestCameraSpacePointZ, rc.nearestCameraSpacePointZ])
- .range([0.1, 1]);
- var camPos = rc.camera.position;
- var camToTarget = camPos.clone().sub(rc.cameraTarget);
- var camToPoint = new THREE.Vector3();
- this.gc.textBaseline = 'middle';
- this.gc.miterLimit = 2;
- // Have extra space between neighboring labels. Don't pack too tightly.
- var labelMargin = 2;
- // Shift the label to the right of the point circle.
- var xShift = 4;
- var n = Math.min(MAX_LABELS_ON_SCREEN, lrc.pointIndices.length);
- for (var i = 0; i < n; ++i) {
- var point = void 0;
- {
- var pi = lrc.pointIndices[i];
- point = util.vector3FromPackedArray(this.worldSpacePointPositions, pi);
- }
- // discard points that are behind the camera
- camToPoint.copy(camPos).sub(point);
- if (camToTarget.dot(camToPoint) < 0) {
- continue;
- }
- var _a = util.vector3DToScreenCoords(rc.camera, rc.screenWidth, rc.screenHeight, point), x = _a[0], y = _a[1];
- x += xShift;
- // Computing the width of the font is expensive,
- // so we assume width of 1 at first. Then, if the label doesn't
- // conflict with other labels, we measure the actual width.
- var textBoundingBox = {
- loX: x - labelMargin,
- hiX: x + 1 + labelMargin,
- loY: y - labelHeight / 2 - labelMargin,
- hiY: y + labelHeight / 2 + labelMargin
- };
- if (grid.insert(textBoundingBox, true)) {
- var text = lrc.labelStrings[i];
- var fontSize = lrc.defaultFontSize * lrc.scaleFactors[i] * dpr;
- this.gc.font = fontSize + 'px roboto';
- // Now, check with properly computed width.
- textBoundingBox.hiX += this.gc.measureText(text).width - 1;
- if (grid.insert(textBoundingBox)) {
- var opacity = 1;
- if (sceneIs3D && (lrc.useSceneOpacityFlags[i] === 1)) {
- opacity = opacityMap(camToPoint.length());
- }
- this.gc.fillStyle =
- this.styleStringFromPackedRgba(lrc.fillColors, i, opacity);
- this.gc.strokeStyle =
- this.styleStringFromPackedRgba(lrc.strokeColors, i, opacity);
- this.gc.lineWidth = LABEL_STROKE_WIDTH;
- this.gc.strokeText(text, x, y);
- this.gc.lineWidth = LABEL_FILL_WIDTH;
- this.gc.fillText(text, x, y);
- }
- }
- }
- };
- ScatterPlotVisualizerCanvasLabels.prototype.styleStringFromPackedRgba = function (packedRgbaArray, colorIndex, opacity) {
- var offset = colorIndex * 3;
- var r = packedRgbaArray[offset];
- var g = packedRgbaArray[offset + 1];
- var b = packedRgbaArray[offset + 2];
- return 'rgba(' + r + ',' + g + ',' + b + ',' + opacity + ')';
- };
- ScatterPlotVisualizerCanvasLabels.prototype.onResize = function (newWidth, newHeight) {
- var dpr = window.devicePixelRatio;
- d3.select(this.canvas)
- .attr('width', newWidth * dpr)
- .attr('height', newHeight * dpr)
- .style({ width: newWidth + 'px', height: newHeight + 'px' });
- };
- ScatterPlotVisualizerCanvasLabels.prototype.dispose = function () {
- this.removeAllLabels();
- this.canvas = null;
- this.gc = null;
- };
- ScatterPlotVisualizerCanvasLabels.prototype.onPointPositionsChanged = function (newPositions) {
- this.worldSpacePointPositions = newPositions;
- this.removeAllLabels();
- };
- ScatterPlotVisualizerCanvasLabels.prototype.onRender = function (rc) {
- if (!this.labelsActive) {
- return;
- }
- this.removeAllLabels();
- this.makeLabels(rc);
- };
- ScatterPlotVisualizerCanvasLabels.prototype.setScene = function (scene) { };
- ScatterPlotVisualizerCanvasLabels.prototype.onPickingRender = function (renderContext) { };
- return ScatterPlotVisualizerCanvasLabels;
-}());
-exports.ScatterPlotVisualizerCanvasLabels = ScatterPlotVisualizerCanvasLabels;
-
-},{"./label":11,"./renderContext":15,"./util":24}],21:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var util = require('./util');
-var RGB_NUM_ELEMENTS = 3;
-var XYZ_NUM_ELEMENTS = 3;
-/**
- * Renders polylines that connect multiple points in the dataset.
- */
-var ScatterPlotVisualizerPolylines = (function () {
- function ScatterPlotVisualizerPolylines() {
- this.polylinePositionBuffer = {};
- this.polylineColorBuffer = {};
- }
- ScatterPlotVisualizerPolylines.prototype.updateSequenceIndicesInDataSet = function (ds) {
- for (var i = 0; i < ds.sequences.length; i++) {
- var sequence = ds.sequences[i];
- for (var j = 0; j < sequence.pointIndices.length - 1; j++) {
- ds.points[sequence.pointIndices[j]].sequenceIndex = i;
- ds.points[sequence.pointIndices[j + 1]].sequenceIndex = i;
- }
- }
- };
- ScatterPlotVisualizerPolylines.prototype.createPolylines = function (scene) {
- if (!this.dataSet || !this.dataSet.sequences) {
- return;
- }
- this.updateSequenceIndicesInDataSet(this.dataSet);
- this.polylines = [];
- for (var i = 0; i < this.dataSet.sequences.length; i++) {
- var geometry = new THREE.BufferGeometry();
- geometry.addAttribute('position', this.polylinePositionBuffer[i]);
- geometry.addAttribute('color', this.polylineColorBuffer[i]);
- var material = new THREE.LineBasicMaterial({
- linewidth: 1,
- opacity: 1.0,
- transparent: true,
- vertexColors: THREE.VertexColors
- });
- var polyline = new THREE.LineSegments(geometry, material);
- polyline.frustumCulled = false;
- this.polylines.push(polyline);
- scene.add(polyline);
- }
- };
- ScatterPlotVisualizerPolylines.prototype.dispose = function () {
- if (this.polylines == null) {
- return;
- }
- for (var i = 0; i < this.polylines.length; i++) {
- this.scene.remove(this.polylines[i]);
- this.polylines[i].geometry.dispose();
- }
- this.polylines = null;
- this.polylinePositionBuffer = {};
- this.polylineColorBuffer = {};
- };
- ScatterPlotVisualizerPolylines.prototype.setScene = function (scene) {
- this.scene = scene;
- };
- ScatterPlotVisualizerPolylines.prototype.setDataSet = function (dataSet) {
- this.dataSet = dataSet;
- };
- ScatterPlotVisualizerPolylines.prototype.onPointPositionsChanged = function (newPositions) {
- if ((newPositions == null) || (this.polylines != null)) {
- this.dispose();
- }
- if ((newPositions == null) || (this.dataSet == null)) {
- return;
- }
- // Set up the position buffer arrays for each polyline.
- for (var i = 0; i < this.dataSet.sequences.length; i++) {
- var sequence = this.dataSet.sequences[i];
- var vertexCount = 2 * (sequence.pointIndices.length - 1);
- var polylines = new Float32Array(vertexCount * XYZ_NUM_ELEMENTS);
- this.polylinePositionBuffer[i] =
- new THREE.BufferAttribute(polylines, XYZ_NUM_ELEMENTS);
- var colors = new Float32Array(vertexCount * RGB_NUM_ELEMENTS);
- this.polylineColorBuffer[i] =
- new THREE.BufferAttribute(colors, RGB_NUM_ELEMENTS);
- }
- for (var i = 0; i < this.dataSet.sequences.length; i++) {
- var sequence = this.dataSet.sequences[i];
- var src = 0;
- for (var j = 0; j < sequence.pointIndices.length - 1; j++) {
- var p1Index = sequence.pointIndices[j];
- var p2Index = sequence.pointIndices[j + 1];
- var p1 = util.vector3FromPackedArray(newPositions, p1Index);
- var p2 = util.vector3FromPackedArray(newPositions, p2Index);
- this.polylinePositionBuffer[i].setXYZ(src, p1.x, p1.y, p1.z);
- this.polylinePositionBuffer[i].setXYZ(src + 1, p2.x, p2.y, p2.z);
- src += 2;
- }
- this.polylinePositionBuffer[i].needsUpdate = true;
- }
- if (this.polylines == null) {
- this.createPolylines(this.scene);
- }
- };
- ScatterPlotVisualizerPolylines.prototype.onRender = function (renderContext) {
- if (this.polylines == null) {
- return;
- }
- for (var i = 0; i < this.polylines.length; i++) {
- this.polylines[i].material.opacity = renderContext.polylineOpacities[i];
- this.polylines[i].material.linewidth =
- renderContext.polylineWidths[i];
- this.polylineColorBuffer[i].array = renderContext.polylineColors[i];
- this.polylineColorBuffer[i].needsUpdate = true;
- }
- };
- ScatterPlotVisualizerPolylines.prototype.onPickingRender = function (renderContext) { };
- ScatterPlotVisualizerPolylines.prototype.onResize = function (newWidth, newHeight) { };
- return ScatterPlotVisualizerPolylines;
-}());
-exports.ScatterPlotVisualizerPolylines = ScatterPlotVisualizerPolylines;
-
-},{"./util":24}],22:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var renderContext_1 = require('./renderContext');
-var util = require('./util');
-var NUM_POINTS_FOG_THRESHOLD = 5000;
-var MIN_POINT_SIZE = 5.0;
-var IMAGE_SIZE = 30;
-// Constants relating to the indices of buffer arrays.
-var RGB_NUM_ELEMENTS = 3;
-var INDEX_NUM_ELEMENTS = 1;
-var XYZ_NUM_ELEMENTS = 3;
-var VERTEX_SHADER = "\n // Index of the specific vertex (passed in as bufferAttribute), and the\n // variable that will be used to pass it to the fragment shader.\n attribute float spriteIndex;\n attribute vec3 color;\n attribute float scaleFactor;\n\n varying vec2 xyIndex;\n varying vec3 vColor;\n\n uniform bool sizeAttenuation;\n uniform float pointSize;\n uniform float spritesPerRow;\n uniform float spritesPerColumn;\n\n void main() {\n // Pass index and color values to fragment shader.\n vColor = color;\n xyIndex = vec2(mod(spriteIndex, spritesPerRow),\n floor(spriteIndex / spritesPerColumn));\n\n // Transform current vertex by modelViewMatrix (model world position and\n // camera world position matrix).\n vec4 cameraSpacePos = modelViewMatrix * vec4(position, 1.0);\n\n // Project vertex in camera-space to screen coordinates using the camera's\n // projection matrix.\n gl_Position = projectionMatrix * cameraSpacePos;\n\n // Create size attenuation (if we're in 3D mode) by making the size of\n // each point inversly proportional to its distance to the camera.\n float outputPointSize = pointSize;\n if (sizeAttenuation) {\n outputPointSize = -pointSize / cameraSpacePos.z;\n }\n\n gl_PointSize =\n max(outputPointSize * scaleFactor, " + MIN_POINT_SIZE.toFixed(1) + ");\n }";
-var FRAGMENT_SHADER_POINT_TEST_CHUNK = "\n bool point_in_unit_circle(vec2 spriteCoord) {\n vec2 centerToP = spriteCoord - vec2(0.5, 0.5);\n return dot(centerToP, centerToP) < (0.5 * 0.5);\n }\n\n bool point_in_unit_equilateral_triangle(vec2 spriteCoord) {\n vec3 v0 = vec3(0, 1, 0);\n vec3 v1 = vec3(0.5, 0, 0);\n vec3 v2 = vec3(1, 1, 0);\n vec3 p = vec3(spriteCoord, 0);\n float p_in_v0_v1 = cross(v1 - v0, p - v0).z;\n float p_in_v1_v2 = cross(v2 - v1, p - v1).z;\n return (p_in_v0_v1 > 0.0) && (p_in_v1_v2 > 0.0);\n }\n\n bool point_in_unit_square(vec2 spriteCoord) {\n return true;\n }\n";
-var FRAGMENT_SHADER = "\n varying vec2 xyIndex;\n varying vec3 vColor;\n\n uniform sampler2D texture;\n uniform float spritesPerRow;\n uniform float spritesPerColumn;\n uniform bool isImage;\n\n " + THREE.ShaderChunk['common'] + "\n " + THREE.ShaderChunk['fog_pars_fragment'] + "\n " + FRAGMENT_SHADER_POINT_TEST_CHUNK + "\n\n void main() {\n if (isImage) {\n // Coordinates of the vertex within the entire sprite image.\n vec2 coords =\n (gl_PointCoord + xyIndex) / vec2(spritesPerRow, spritesPerColumn);\n gl_FragColor = vec4(vColor, 1.0) * texture2D(texture, coords);\n } else {\n bool inside = point_in_unit_circle(gl_PointCoord);\n if (!inside) {\n discard;\n }\n gl_FragColor = vec4(vColor, 1);\n }\n " + THREE.ShaderChunk['fog_fragment'] + "\n }";
-var FRAGMENT_SHADER_PICKING = "\n varying vec2 xyIndex;\n varying vec3 vColor;\n uniform bool isImage;\n\n " + FRAGMENT_SHADER_POINT_TEST_CHUNK + "\n\n void main() {\n xyIndex; // Silence 'unused variable' warning.\n if (isImage) {\n gl_FragColor = vec4(vColor, 1);\n } else {\n bool inside = point_in_unit_circle(gl_PointCoord);\n if (!inside) {\n discard;\n }\n gl_FragColor = vec4(vColor, 1);\n }\n }";
-/**
- * Uses GL point sprites to render the dataset.
- */
-var ScatterPlotVisualizerSprites = (function () {
- function ScatterPlotVisualizerSprites() {
- this.texture = null;
- this.standinTextureForPoints =
- util.createTexture(document.createElement('canvas'));
- this.renderMaterial = this.createRenderMaterial(false);
- this.pickingMaterial = this.createPickingMaterial(false);
- }
- ScatterPlotVisualizerSprites.prototype.createTextureFromSpriteAtlas = function (spriteAtlas, spriteDimensions, spriteIndices) {
- this.texture = util.createTexture(spriteAtlas);
- this.spritesPerRow = spriteAtlas.width / spriteDimensions[0];
- this.spritesPerColumn = spriteAtlas.height / spriteDimensions[1];
- this.spriteDimensions = spriteDimensions;
- this.spriteIndexBufferAttribute =
- new THREE.BufferAttribute(spriteIndices, INDEX_NUM_ELEMENTS);
- if (this.points != null) {
- this.points.geometry
- .addAttribute('spriteIndex', this.spriteIndexBufferAttribute);
- }
- };
- ScatterPlotVisualizerSprites.prototype.createUniforms = function () {
- return {
- texture: { type: 't' },
- spritesPerRow: { type: 'f' },
- spritesPerColumn: { type: 'f' },
- fogColor: { type: 'c' },
- fogNear: { type: 'f' },
- fogFar: { type: 'f' },
- isImage: { type: 'bool' },
- sizeAttenuation: { type: 'bool' },
- pointSize: { type: 'f' }
- };
- };
- ScatterPlotVisualizerSprites.prototype.createRenderMaterial = function (haveImage) {
- var uniforms = this.createUniforms();
- return new THREE.ShaderMaterial({
- uniforms: uniforms,
- vertexShader: VERTEX_SHADER,
- fragmentShader: FRAGMENT_SHADER,
- transparent: !haveImage,
- depthTest: haveImage,
- depthWrite: haveImage,
- fog: true,
- blending: THREE.MultiplyBlending,
- });
- };
- ScatterPlotVisualizerSprites.prototype.createPickingMaterial = function (haveImage) {
- var uniforms = this.createUniforms();
- return new THREE.ShaderMaterial({
- uniforms: uniforms,
- vertexShader: VERTEX_SHADER,
- fragmentShader: FRAGMENT_SHADER_PICKING,
- transparent: true,
- depthTest: true,
- depthWrite: true,
- fog: false,
- blending: THREE.NormalBlending,
- });
- };
- /**
- * Create points, set their locations and actually instantiate the
- * geometry.
- */
- ScatterPlotVisualizerSprites.prototype.createPointSprites = function (scene, positions) {
- var pointCount = (positions != null) ? (positions.length / XYZ_NUM_ELEMENTS) : 0;
- var geometry = this.createGeometry(pointCount);
- this.fog = new THREE.Fog(0xFFFFFF); // unused value, gets overwritten.
- this.points = new THREE.Points(geometry, this.renderMaterial);
- this.points.frustumCulled = false;
- if (this.spriteIndexBufferAttribute != null) {
- this.points.geometry
- .addAttribute('spriteIndex', this.spriteIndexBufferAttribute);
- }
- scene.add(this.points);
- };
- ScatterPlotVisualizerSprites.prototype.calculatePointSize = function (sceneIs3D) {
- if (this.texture != null) {
- return sceneIs3D ? IMAGE_SIZE : this.spriteDimensions[0];
- }
- var n = (this.worldSpacePointPositions != null) ?
- (this.worldSpacePointPositions.length / XYZ_NUM_ELEMENTS) :
- 1;
- var SCALE = 200;
- var LOG_BASE = 8;
- var DIVISOR = 1.5;
- // Scale point size inverse-logarithmically to the number of points.
- var pointSize = SCALE / Math.log(n) / Math.log(LOG_BASE);
- return sceneIs3D ? pointSize : (pointSize / DIVISOR);
- };
- /**
- * Set up buffer attributes to be used for the points/images.
- */
- ScatterPlotVisualizerSprites.prototype.createGeometry = function (pointCount) {
- var n = pointCount;
- // Fill pickingColors with each point's unique id as its color.
- this.pickingColors = new Float32Array(n * RGB_NUM_ELEMENTS);
- {
- var dst = 0;
- for (var i = 0; i < n; i++) {
- var c = new THREE.Color(i);
- this.pickingColors[dst++] = c.r;
- this.pickingColors[dst++] = c.g;
- this.pickingColors[dst++] = c.b;
- }
- }
- var geometry = new THREE.BufferGeometry();
- geometry.addAttribute('position', new THREE.BufferAttribute(null, XYZ_NUM_ELEMENTS));
- geometry.addAttribute('color', new THREE.BufferAttribute(null, RGB_NUM_ELEMENTS));
- geometry.addAttribute('scaleFactor', new THREE.BufferAttribute(null, INDEX_NUM_ELEMENTS));
- return geometry;
- };
- ScatterPlotVisualizerSprites.prototype.setFogDistances = function (sceneIs3D, nearestPointZ, farthestPointZ) {
- if (sceneIs3D) {
- var n = this.worldSpacePointPositions.length / XYZ_NUM_ELEMENTS;
- this.fog.near = nearestPointZ;
- // If there are fewer points we want less fog. We do this
- // by making the "far" value (that is, the distance from the camera to the
- // far edge of the fog) proportional to the number of points.
- var multiplier = 2 - Math.min(n, NUM_POINTS_FOG_THRESHOLD) / NUM_POINTS_FOG_THRESHOLD;
- this.fog.far = farthestPointZ * multiplier;
- }
- else {
- this.fog.near = Infinity;
- this.fog.far = Infinity;
- }
- };
- ScatterPlotVisualizerSprites.prototype.dispose = function () {
- this.disposeGeometry();
- this.disposeTextureAtlas();
- };
- ScatterPlotVisualizerSprites.prototype.disposeGeometry = function () {
- if (this.points != null) {
- this.scene.remove(this.points);
- this.points.geometry.dispose();
- this.points = null;
- this.worldSpacePointPositions = null;
- }
- };
- ScatterPlotVisualizerSprites.prototype.disposeTextureAtlas = function () {
- if (this.texture != null) {
- this.texture.dispose();
- }
- this.texture = null;
- this.renderMaterial = null;
- this.pickingMaterial = null;
- };
- ScatterPlotVisualizerSprites.prototype.setScene = function (scene) {
- this.scene = scene;
- };
- ScatterPlotVisualizerSprites.prototype.setSpriteAtlas = function (spriteImage, spriteDimensions, spriteIndices) {
- this.disposeTextureAtlas();
- this.createTextureFromSpriteAtlas(spriteImage, spriteDimensions, spriteIndices);
- this.renderMaterial = this.createRenderMaterial(true);
- this.pickingMaterial = this.createPickingMaterial(true);
- };
- ScatterPlotVisualizerSprites.prototype.clearSpriteAtlas = function () {
- this.disposeTextureAtlas();
- this.renderMaterial = this.createRenderMaterial(false);
- this.pickingMaterial = this.createPickingMaterial(false);
- };
- ScatterPlotVisualizerSprites.prototype.onPointPositionsChanged = function (newPositions) {
- if ((newPositions == null) || (newPositions.length === 0)) {
- this.dispose();
- return;
- }
- if (this.points != null) {
- if (this.worldSpacePointPositions.length !== newPositions.length) {
- this.disposeGeometry();
- }
- }
- this.worldSpacePointPositions = newPositions;
- if (this.points == null) {
- this.createPointSprites(this.scene, newPositions);
- }
- var positions = this.points.geometry
- .getAttribute('position');
- positions.array = newPositions;
- positions.needsUpdate = true;
- };
- ScatterPlotVisualizerSprites.prototype.onPickingRender = function (rc) {
- if (this.points == null) {
- return;
- }
- var sceneIs3D = (rc.cameraType === renderContext_1.CameraType.Perspective);
- this.pickingMaterial.uniforms.spritesPerRow.value = this.spritesPerRow;
- this.pickingMaterial.uniforms.spritesPerRow.value = this.spritesPerColumn;
- this.pickingMaterial.uniforms.sizeAttenuation.value = sceneIs3D;
- this.pickingMaterial.uniforms.pointSize.value =
- this.calculatePointSize(sceneIs3D);
- this.points.material = this.pickingMaterial;
- var colors = this.points.geometry
- .getAttribute('color');
- colors.array = this.pickingColors;
- colors.needsUpdate = true;
- var scaleFactors = this.points.geometry
- .getAttribute('scaleFactor');
- scaleFactors.array = rc.pointScaleFactors;
- scaleFactors.needsUpdate = true;
- };
- ScatterPlotVisualizerSprites.prototype.onRender = function (rc) {
- if (!this.points) {
- return;
- }
- var sceneIs3D = (rc.camera instanceof THREE.PerspectiveCamera);
- this.setFogDistances(sceneIs3D, rc.nearestCameraSpacePointZ, rc.farthestCameraSpacePointZ);
- this.scene.fog = this.fog;
- this.scene.fog.color = new THREE.Color(rc.backgroundColor);
- this.renderMaterial.uniforms.fogColor.value = this.scene.fog.color;
- this.renderMaterial.uniforms.fogNear.value = this.fog.near;
- this.renderMaterial.uniforms.fogFar.value = this.fog.far;
- this.renderMaterial.uniforms.spritesPerRow.value = this.spritesPerRow;
- this.renderMaterial.uniforms.spritesPerColumn.value = this.spritesPerColumn;
- this.renderMaterial.uniforms.isImage.value = (this.texture != null);
- this.renderMaterial.uniforms.texture.value =
- (this.texture != null) ? this.texture : this.standinTextureForPoints;
- this.renderMaterial.uniforms.sizeAttenuation.value = sceneIs3D;
- this.renderMaterial.uniforms.pointSize.value =
- this.calculatePointSize(sceneIs3D);
- this.points.material = this.renderMaterial;
- var colors = this.points.geometry
- .getAttribute('color');
- this.renderColors = rc.pointColors;
- colors.array = this.renderColors;
- colors.needsUpdate = true;
- var scaleFactors = this.points.geometry
- .getAttribute('scaleFactor');
- scaleFactors.array = rc.pointScaleFactors;
- scaleFactors.needsUpdate = true;
- };
- ScatterPlotVisualizerSprites.prototype.onResize = function (newWidth, newHeight) { };
- return ScatterPlotVisualizerSprites;
-}());
-exports.ScatterPlotVisualizerSprites = ScatterPlotVisualizerSprites;
-
-},{"./renderContext":15,"./util":24}],23:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-/**
- * A Space-partitioning tree (https://en.wikipedia.org/wiki/Space_partitioning)
- * that recursively divides the space into regions of equal sizes. This data
- * structure can act both as a Quad tree and an Octree when the data is 2 or
- * 3 dimensional respectively. One usage is in t-SNE in order to do Barnes-Hut
- * approximation.
- */
-var SPTree = (function () {
- /**
- * Constructs a new tree with the provided data.
- *
- * @param data List of n-dimensional data points.
- * @param capacity Number of data points to store in a single node.
- */
- function SPTree(data) {
- if (data.length < 1) {
- throw new Error('There should be at least 1 data point');
- }
- // Make a bounding box based on the extent of the data.
- this.dim = data[0].length;
- // Each node has 2^d children, where d is the dimension of the space.
- // Binary masks (e.g. 000, 001, ... 111 in 3D) are used to determine in
- // which child (e.g. quadron in 2D) the new point is going to be assigned.
- // For more details, see the insert() method and its comments.
- this.masks = new Array(Math.pow(2, this.dim));
- for (var d = 0; d < this.masks.length; ++d) {
- this.masks[d] = (1 << d);
- }
- var min = new Array(this.dim);
- fillArray(min, Number.POSITIVE_INFINITY);
- var max = new Array(this.dim);
- fillArray(max, Number.NEGATIVE_INFINITY);
- for (var i = 0; i < data.length; ++i) {
- // For each dim get the min and max.
- // E.g. For 2-D, get the x_min, x_max, y_min, y_max.
- for (var d = 0; d < this.dim; ++d) {
- min[d] = Math.min(min[d], data[i][d]);
- max[d] = Math.max(max[d], data[i][d]);
- }
- }
- // Create a bounding box with the center of the largest span.
- var center = new Array(this.dim);
- var halfDim = 0;
- for (var d = 0; d < this.dim; ++d) {
- var span = max[d] - min[d];
- center[d] = min[d] + span / 2;
- halfDim = Math.max(halfDim, span / 2);
- }
- this.root = { box: { center: center, halfDim: halfDim }, point: data[0] };
- for (var i = 1; i < data.length; ++i) {
- this.insert(this.root, data[i]);
- }
- }
- /**
- * Visits every node in the tree. Each node can store 1 or more points,
- * depending on the node capacity provided in the constructor.
- *
- * @param accessor Method that takes the currently visited node, and the
- * low and high point of the region that this node occupies. E.g. in 2D,
- * the low and high points will be the lower-left corner and the upper-right
- * corner.
- */
- SPTree.prototype.visit = function (accessor, noBox) {
- if (noBox === void 0) { noBox = false; }
- this.visitNode(this.root, accessor, noBox);
- };
- SPTree.prototype.visitNode = function (node, accessor, noBox) {
- var skipChildren;
- if (noBox) {
- skipChildren = accessor(node);
- }
- else {
- var lowPoint = new Array(this.dim);
- var highPoint = new Array(this.dim);
- for (var d = 0; d < this.dim; ++d) {
- lowPoint[d] = node.box.center[d] - node.box.halfDim;
- highPoint[d] = node.box.center[d] + node.box.halfDim;
- }
- skipChildren = accessor(node, lowPoint, highPoint);
- }
- if (!node.children || skipChildren) {
- return;
- }
- for (var i = 0; i < node.children.length; ++i) {
- var child = node.children[i];
- if (child) {
- this.visitNode(child, accessor, noBox);
- }
- }
- };
- SPTree.prototype.insert = function (node, p) {
- // Subdivide and then add the point to whichever node will accept it.
- if (node.children == null) {
- node.children = new Array(this.masks.length);
- }
- // Decide which child will get the new point by constructing a D-bits binary
- // signature (D=3 for 3D) where the k-th bit is 1 if the point's k-th
- // coordinate is greater than the node's k-th coordinate, 0 otherwise.
- // Then the binary signature in decimal system gives us the index of the
- // child where the new point should be.
- var index = 0;
- for (var d = 0; d < this.dim; ++d) {
- if (p[d] > node.box.center[d]) {
- index |= this.masks[d];
- }
- }
- if (node.children[index] == null) {
- this.makeChild(node, index, p);
- }
- else {
- this.insert(node.children[index], p);
- }
- };
- SPTree.prototype.makeChild = function (node, index, p) {
- var oldC = node.box.center;
- var h = node.box.halfDim / 2;
- var newC = new Array(this.dim);
- for (var d = 0; d < this.dim; ++d) {
- newC[d] = (index & (1 << d)) ? oldC[d] + h : oldC[d] - h;
- }
- node.children[index] = { box: { center: newC, halfDim: h }, point: p };
- };
- return SPTree;
-}());
-exports.SPTree = SPTree;
-function fillArray(arr, value) {
- for (var i = 0; i < arr.length; ++i) {
- arr[i] = value;
- }
-}
-
-},{}],24:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var logging = require('./logging');
-/**
- * Delay for running expensive tasks, in milliseconds.
- * The duration was empirically found so that it leaves enough time for the
- * browser to update its UI state before starting an expensive UI-blocking task.
- */
-var TASK_DELAY_MS = 200;
-/** Shuffles the array in-place in O(n) time using Fisher-Yates algorithm. */
-function shuffle(array) {
- var m = array.length;
- var t;
- var i;
- // While there remain elements to shuffle.
- while (m) {
- // Pick a remaining element
- i = Math.floor(Math.random() * m--);
- // And swap it with the current element.
- t = array[m];
- array[m] = array[i];
- array[i] = t;
- }
- return array;
-}
-exports.shuffle = shuffle;
-/** Projects a 3d point into screen space */
-function vector3DToScreenCoords(cam, w, h, v) {
- var dpr = window.devicePixelRatio;
- var pv = new THREE.Vector3().copy(v).project(cam);
- // The screen-space origin is at the middle of the screen, with +y up.
- var coords = [((pv.x + 1) / 2 * w) * dpr, -((pv.y - 1) / 2 * h) * dpr];
- return coords;
-}
-exports.vector3DToScreenCoords = vector3DToScreenCoords;
-/** Loads 3 contiguous elements from a packed xyz array into a Vector3. */
-function vector3FromPackedArray(a, pointIndex) {
- var offset = pointIndex * 3;
- return new THREE.Vector3(a[offset], a[offset + 1], a[offset + 2]);
-}
-exports.vector3FromPackedArray = vector3FromPackedArray;
-/**
- * Gets the camera-space z coordinates of the nearest and farthest points.
- * Ignores points that are behind the camera.
- */
-function getNearFarPoints(worldSpacePoints, cameraPos, cameraTarget) {
- var shortestDist = Infinity;
- var furthestDist = 0;
- var camToTarget = new THREE.Vector3().copy(cameraTarget).sub(cameraPos);
- var camPlaneNormal = new THREE.Vector3().copy(camToTarget).normalize();
- var n = worldSpacePoints.length / 3;
- var src = 0;
- var p = new THREE.Vector3();
- var camToPoint = new THREE.Vector3();
- for (var i = 0; i < n; i++) {
- p.x = worldSpacePoints[src];
- p.y = worldSpacePoints[src + 1];
- p.z = worldSpacePoints[src + 2];
- src += 3;
- camToPoint.copy(p).sub(cameraPos);
- var dist = camPlaneNormal.dot(camToPoint);
- if (dist < 0) {
- continue;
- }
- furthestDist = (dist > furthestDist) ? dist : furthestDist;
- shortestDist = (dist < shortestDist) ? dist : shortestDist;
- }
- return [shortestDist, furthestDist];
-}
-exports.getNearFarPoints = getNearFarPoints;
-/**
- * Generate a texture for the points/images and sets some initial params
- */
-function createTexture(image) {
- var tex = new THREE.Texture(image);
- tex.needsUpdate = true;
- // Used if the texture isn't a power of 2.
- tex.minFilter = THREE.LinearFilter;
- tex.generateMipmaps = false;
- tex.flipY = false;
- return tex;
-}
-exports.createTexture = createTexture;
-/**
- * Assert that the condition is satisfied; if not, log user-specified message
- * to the console.
- */
-function assert(condition, message) {
- if (!condition) {
- message = message || 'Assertion failed';
- throw new Error(message);
- }
-}
-exports.assert = assert;
-function getSearchPredicate(query, inRegexMode, fieldName) {
- var predicate;
- if (inRegexMode) {
- var regExp_1 = new RegExp(query, 'i');
- predicate = function (p) { return regExp_1.test(p.metadata[fieldName].toString()); };
- }
- else {
- // Doing a case insensitive substring match.
- query = query.toLowerCase();
- predicate = function (p) {
- var label = p.metadata[fieldName].toString().toLowerCase();
- return label.indexOf(query) >= 0;
- };
- }
- return predicate;
-}
-exports.getSearchPredicate = getSearchPredicate;
-/**
- * Runs an expensive task asynchronously with some delay
- * so that it doesn't block the UI thread immediately.
- *
- * @param message The message to display to the user.
- * @param task The expensive task to run.
- * @param msgId Optional. ID of an existing message. If provided, will overwrite
- * an existing message and won't automatically clear the message when the
- * task is done.
- * @return The value returned by the task.
- */
-function runAsyncTask(message, task, msgId) {
- if (msgId === void 0) { msgId = null; }
- var autoClear = (msgId == null);
- msgId = logging.setModalMessage(message, msgId);
- return new Promise(function (resolve, reject) {
- d3.timer(function () {
- try {
- var result = task();
- // Clearing the old message.
- if (autoClear) {
- logging.setModalMessage(null, msgId);
- }
- resolve(result);
- }
- catch (ex) {
- reject(ex);
- }
- return true;
- }, TASK_DELAY_MS);
- });
-}
-exports.runAsyncTask = runAsyncTask;
-/**
- * Parses the URL for query parameters, e.g. ?foo=1&bar=2 will return
- * {'foo': '1', 'bar': '2'}.
- * @param url The URL to parse.
- * @return A map of queryParam key to its value.
- */
-function getURLParams(url) {
- if (!url) {
- return {};
- }
- var queryString = url.indexOf('?') !== -1 ? url.split('?')[1] : url;
- if (queryString.indexOf('#')) {
- queryString = queryString.split('#')[0];
- }
- var queryEntries = queryString.split('&');
- var queryParams = {};
- for (var i = 0; i < queryEntries.length; i++) {
- var queryEntryComponents = queryEntries[i].split('=');
- queryParams[queryEntryComponents[0].toLowerCase()] =
- decodeURIComponent(queryEntryComponents[1]);
- }
- return queryParams;
-}
-exports.getURLParams = getURLParams;
-/** List of substrings that auto generated tensors have in their name. */
-var SUBSTR_GEN_TENSORS = ['/Adagrad'];
-/** Returns true if the tensor was automatically generated by TF API calls. */
-function tensorIsGenerated(tensorName) {
- for (var i = 0; i < SUBSTR_GEN_TENSORS.length; i++) {
- if (tensorName.indexOf(SUBSTR_GEN_TENSORS[i]) >= 0) {
- return true;
- }
- }
- return false;
-}
-exports.tensorIsGenerated = tensorIsGenerated;
-function xor(cond1, cond2) {
- return (cond1 || cond2) && !(cond1 && cond2);
-}
-exports.xor = xor;
-/** Checks to see if the browser supports webgl. */
-function hasWebGLSupport() {
- try {
- var c = document.createElement('canvas');
- var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
- return gl != null && typeof weblas !== 'undefined';
- }
- catch (e) {
- return false;
- }
-}
-exports.hasWebGLSupport = hasWebGLSupport;
-
-},{"./logging":12}],25:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-var util_1 = require('./util');
-/** Returns the dot product of two vectors. */
-function dot(a, b) {
- util_1.assert(a.length === b.length, 'Vectors a and b must be of same length');
- var result = 0;
- for (var i = 0; i < a.length; ++i) {
- result += a[i] * b[i];
- }
- return result;
-}
-exports.dot = dot;
-/** Sums all the elements in the vector */
-function sum(a) {
- var result = 0;
- for (var i = 0; i < a.length; ++i) {
- result += a[i];
- }
- return result;
-}
-exports.sum = sum;
-/** Returns the sum of two vectors, i.e. a + b */
-function add(a, b) {
- util_1.assert(a.length === b.length, 'Vectors a and b must be of same length');
- var result = new Float32Array(a.length);
- for (var i = 0; i < a.length; ++i) {
- result[i] = a[i] + b[i];
- }
- return result;
-}
-exports.add = add;
-/** Subtracts vector b from vector a, i.e. returns a - b */
-function sub(a, b) {
- util_1.assert(a.length === b.length, 'Vectors a and b must be of same length');
- var result = new Float32Array(a.length);
- for (var i = 0; i < a.length; ++i) {
- result[i] = a[i] - b[i];
- }
- return result;
-}
-exports.sub = sub;
-/** Returns the square norm of the vector */
-function norm2(a) {
- var result = 0;
- for (var i = 0; i < a.length; ++i) {
- result += a[i] * a[i];
- }
- return result;
-}
-exports.norm2 = norm2;
-/** Returns the euclidean distance between two vectors. */
-function dist(a, b) {
- return Math.sqrt(dist2(a, b));
-}
-exports.dist = dist;
-/** Returns the square euclidean distance between two vectors. */
-function dist2(a, b) {
- util_1.assert(a.length === b.length, 'Vectors a and b must be of same length');
- var result = 0;
- for (var i = 0; i < a.length; ++i) {
- var diff = a[i] - b[i];
- result += diff * diff;
- }
- return result;
-}
-exports.dist2 = dist2;
-/** Returns the square euclidean distance between two 2D points. */
-function dist2_2D(a, b) {
- var dX = a[0] - b[0];
- var dY = a[1] - b[1];
- return dX * dX + dY * dY;
-}
-exports.dist2_2D = dist2_2D;
-/** Returns the square euclidean distance between two 3D points. */
-function dist2_3D(a, b) {
- var dX = a[0] - b[0];
- var dY = a[1] - b[1];
- var dZ = a[2] - b[2];
- return dX * dX + dY * dY + dZ * dZ;
-}
-exports.dist2_3D = dist2_3D;
-/** Returns the euclidean distance between 2 3D points. */
-function dist_3D(a, b) {
- return Math.sqrt(dist2_3D(a, b));
-}
-exports.dist_3D = dist_3D;
-/**
- * Returns the square euclidean distance between two vectors, with an early
- * exit (returns -1) if the distance is >= to the provided limit.
- */
-function dist2WithLimit(a, b, limit) {
- util_1.assert(a.length === b.length, 'Vectors a and b must be of same length');
- var result = 0;
- for (var i = 0; i < a.length; ++i) {
- var diff = a[i] - b[i];
- result += diff * diff;
- if (result >= limit) {
- return -1;
- }
- }
- return result;
-}
-exports.dist2WithLimit = dist2WithLimit;
-/** Returns the square euclidean distance between two 2D points. */
-function dist22D(a, b) {
- var dX = a[0] - b[0];
- var dY = a[1] - b[1];
- return dX * dX + dY * dY;
-}
-exports.dist22D = dist22D;
-/** Modifies the vector in-place to have unit norm. */
-function unit(a) {
- var norm = Math.sqrt(norm2(a));
- util_1.assert(norm >= 0, 'Norm of the vector must be > 0');
- for (var i = 0; i < a.length; ++i) {
- a[i] /= norm;
- }
-}
-exports.unit = unit;
-/**
- * Projects the vectors to a lower dimension
- *
- * @param vectors Array of vectors to be projected.
- * @param newDim The resulting dimension of the vectors.
- */
-function projectRandom(vectors, newDim) {
- var dim = vectors[0].length;
- var N = vectors.length;
- var newVectors = new Array(N);
- for (var i = 0; i < N; ++i) {
- newVectors[i] = new Float32Array(newDim);
- }
- // Make nDim projections.
- for (var k = 0; k < newDim; ++k) {
- var randomVector = rn(dim);
- for (var i = 0; i < N; ++i) {
- newVectors[i][k] = dot(vectors[i], randomVector);
- }
- }
- return newVectors;
-}
-exports.projectRandom = projectRandom;
-/**
- * Projects a vector onto a 2D plane specified by the two direction vectors.
- */
-function project2d(a, dir1, dir2) {
- return [dot(a, dir1), dot(a, dir2)];
-}
-exports.project2d = project2d;
-/**
- * Computes the centroid of the data points. If the provided data points are not
- * vectors, an accessor function needs to be provided.
- */
-function centroid(dataPoints, accessor) {
- if (dataPoints.length === 0) {
- return null;
- }
- if (accessor == null) {
- accessor = function (a) { return a; };
- }
- util_1.assert(dataPoints.length >= 0, '`vectors` must be of length >= 1');
- var centroid = new Float32Array(accessor(dataPoints[0]).length);
- for (var i = 0; i < dataPoints.length; ++i) {
- var dataPoint = dataPoints[i];
- var vector = accessor(dataPoint);
- for (var j = 0; j < centroid.length; ++j) {
- centroid[j] += vector[j];
- }
- }
- for (var j = 0; j < centroid.length; ++j) {
- centroid[j] /= dataPoints.length;
- }
- return centroid;
-}
-exports.centroid = centroid;
-/**
- * Generates a vector of the specified size where each component is drawn from
- * a random (0, 1) gaussian distribution.
- */
-function rn(size) {
- var normal = d3.random.normal();
- var result = new Float32Array(size);
- for (var i = 0; i < size; ++i) {
- result[i] = normal();
- }
- return result;
-}
-exports.rn = rn;
-/**
- * Returns the cosine distance ([0, 2]) between two vectors
- * that have been normalized to unit norm.
- */
-function cosDistNorm(a, b) {
- return 1 - dot(a, b);
-}
-exports.cosDistNorm = cosDistNorm;
-/**
- * Returns the cosine distance ([0, 2]) between two vectors.
- */
-function cosDist(a, b) {
- return 1 - cosSim(a, b);
-}
-exports.cosDist = cosDist;
-/** Returns the cosine similarity ([-1, 1]) between two vectors. */
-function cosSim(a, b) {
- return dot(a, b) / Math.sqrt(norm2(a) * norm2(b));
-}
-exports.cosSim = cosSim;
-/**
- * Converts list of vectors (matrix) into a 1-dimensional
- * typed array with row-first order.
- */
-function toTypedArray(dataPoints, accessor) {
- var N = dataPoints.length;
- var dim = accessor(dataPoints[0]).length;
- var result = new Float32Array(N * dim);
- for (var i = 0; i < N; ++i) {
- var vector = accessor(dataPoints[i]);
- for (var d = 0; d < dim; ++d) {
- result[i * dim + d] = vector[d];
- }
- }
- return result;
-}
-exports.toTypedArray = toTypedArray;
-/**
- * Transposes an RxC matrix represented as a flat typed array
- * into a CxR matrix, again represented as a flat typed array.
- */
-function transposeTypedArray(r, c, typedArray) {
- var result = new Float32Array(r * c);
- for (var i = 0; i < r; ++i) {
- for (var j = 0; j < c; ++j) {
- result[j * r + i] = typedArray[i * c + j];
- }
- }
- return result;
-}
-exports.transposeTypedArray = transposeTypedArray;
-
-},{"./util":24}],26:[function(require,module,exports){
-"use strict";
-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 __());
-};
-var logging = require('./logging');
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-// tslint:disable-next-line
-exports.BookmarkPanelPolymer = vz_projector_util_1.PolymerElement({
- is: 'vz-projector-bookmark-panel',
- properties: {
- savedStates: Object,
- // Keep a separate polymer property because the savedStates doesn't change
- // when adding and removing states.
- hasStates: { type: Boolean, value: false },
- selectedState: Number
- }
-});
-var BookmarkPanel = (function (_super) {
- __extends(BookmarkPanel, _super);
- function BookmarkPanel() {
- _super.apply(this, arguments);
- this.hasStates = false;
- }
- BookmarkPanel.prototype.ready = function () {
- this.dom = d3.select(this);
- this.savedStates = [];
- this.setupUploadButton();
- this.ignoreNextProjectionEvent = false;
- };
- BookmarkPanel.prototype.initialize = function (projector, projectorEventContext) {
- var _this = this;
- this.projector = projector;
- projectorEventContext.registerProjectionChangedListener(function () {
- if (_this.ignoreNextProjectionEvent) {
- _this.ignoreNextProjectionEvent = false;
- }
- else {
- _this.clearStateSelection();
- }
- });
- };
- BookmarkPanel.prototype.setSelectedTensor = function (run, tensorInfo, dataProvider) {
- var _this = this;
- // Clear any existing bookmarks.
- this.addStates(null);
- if (tensorInfo && tensorInfo.bookmarksPath) {
- // Get any bookmarks that may come when the projector starts up.
- dataProvider.getBookmarks(run, tensorInfo.tensorName, function (bookmarks) {
- _this.addStates(bookmarks);
- _this._expandMore();
- });
- }
- else {
- this._expandLess();
- }
- };
- /** Handles a click on show bookmarks tray button. */
- BookmarkPanel.prototype._expandMore = function () {
- this.$.panel.show();
- this.dom.select('#expand-more').style('display', 'none');
- this.dom.select('#expand-less').style('display', '');
- };
- /** Handles a click on hide bookmarks tray button. */
- BookmarkPanel.prototype._expandLess = function () {
- this.$.panel.hide();
- this.dom.select('#expand-more').style('display', '');
- this.dom.select('#expand-less').style('display', 'none');
- };
- /** Handles a click on the add bookmark button. */
- BookmarkPanel.prototype._addBookmark = function () {
- var currentState = this.projector.getCurrentState();
- currentState.label = 'State ' + this.savedStates.length;
- currentState.isSelected = true;
- this.selectedState = this.savedStates.length;
- for (var i = 0; i < this.savedStates.length; i++) {
- this.savedStates[i].isSelected = false;
- // We have to call notifyPath so that polymer knows this element was
- // updated.
- this.notifyPath('savedStates.' + i + '.isSelected', false, false);
- }
- this.push('savedStates', currentState);
- this.updateHasStates();
- };
- /** Handles a click on the download bookmarks button. */
- BookmarkPanel.prototype._downloadFile = function () {
- var serializedState = this.serializeAllSavedStates();
- var blob = new Blob([serializedState], { type: 'text/plain' });
- var textFile = window.URL.createObjectURL(blob);
- // Force a download.
- var a = document.createElement('a');
- document.body.appendChild(a);
- a.style.display = 'none';
- a.href = textFile;
- a.download = 'state';
- a.click();
- document.body.removeChild(a);
- window.URL.revokeObjectURL(textFile);
- };
- /** Handles a click on the upload bookmarks button. */
- BookmarkPanel.prototype._uploadFile = function () {
- var fileInput = this.dom.select('#state-file');
- fileInput.node().click();
- };
- BookmarkPanel.prototype.setupUploadButton = function () {
- var _this = this;
- // Show and setup the load view button.
- var fileInput = this.dom.select('#state-file');
- fileInput.on('change', function () {
- var file = d3.event.target.files[0];
- // Clear out the value of the file chooser. This ensures that if the user
- // selects the same file, we'll re-read it.
- d3.event.target.value = '';
- var fileReader = new FileReader();
- fileReader.onload = function (evt) {
- var str = evt.target.result;
- var savedStates = JSON.parse(str);
- // Verify the bookmarks match.
- if (_this.savedStatesValid(savedStates)) {
- _this.addStates(savedStates);
- _this.loadSavedState(0);
- }
- else {
- logging.setWarningMessage("Unable to load bookmarks: wrong dataset, expected dataset " +
- ("with shape (" + savedStates[0].dataSetDimensions + ")."));
- }
- };
- fileReader.readAsText(file);
- });
- };
- BookmarkPanel.prototype.addStates = function (savedStates) {
- if (savedStates == null) {
- this.savedStates = [];
- }
- else {
- for (var i = 0; i < savedStates.length; i++) {
- savedStates[i].isSelected = false;
- this.push('savedStates', savedStates[i]);
- }
- }
- this.updateHasStates();
- };
- /** Deselects any selected state selection. */
- BookmarkPanel.prototype.clearStateSelection = function () {
- for (var i = 0; i < this.savedStates.length; i++) {
- this.setSelectionState(i, false);
- }
- };
- /** Handles a radio button click on a saved state. */
- BookmarkPanel.prototype._radioButtonHandler = function (evt) {
- var index = this.getParentDataIndex(evt);
- this.loadSavedState(index);
- this.setSelectionState(index, true);
- };
- BookmarkPanel.prototype.loadSavedState = function (index) {
- for (var i = 0; i < this.savedStates.length; i++) {
- if (this.savedStates[i].isSelected) {
- this.setSelectionState(i, false);
- }
- else if (index === i) {
- this.setSelectionState(i, true);
- this.ignoreNextProjectionEvent = true;
- this.projector.loadState(this.savedStates[i]);
- }
- }
- };
- BookmarkPanel.prototype.setSelectionState = function (stateIndex, selected) {
- this.savedStates[stateIndex].isSelected = selected;
- var path = 'savedStates.' + stateIndex + '.isSelected';
- this.notifyPath(path, selected, false);
- };
- /**
- * Crawls up the DOM to find an ancestor with a data-index attribute. This is
- * used to match events to their bookmark index.
- */
- BookmarkPanel.prototype.getParentDataIndex = function (evt) {
- for (var i = 0; i < evt.path.length; i++) {
- var dataIndex = evt.path[i].getAttribute('data-index');
- if (dataIndex != null) {
- return +dataIndex;
- }
- }
- return -1;
- };
- /** Handles a clear button click on a bookmark. */
- BookmarkPanel.prototype._clearButtonHandler = function (evt) {
- var index = this.getParentDataIndex(evt);
- this.splice('savedStates', index, 1);
- this.updateHasStates();
- };
- /** Handles a label change event on a bookmark. */
- BookmarkPanel.prototype._labelChange = function (evt) {
- var index = this.getParentDataIndex(evt);
- this.savedStates[index].label = evt.target.value;
- };
- /**
- * Used to determine whether to select the radio button for a given bookmark.
- */
- BookmarkPanel.prototype._isSelectedState = function (index) {
- return index === this.selectedState;
- };
- BookmarkPanel.prototype._isNotSelectedState = function (index) {
- return index !== this.selectedState;
- };
- /**
- * Gets all of the saved states as a serialized string.
- */
- BookmarkPanel.prototype.serializeAllSavedStates = function () {
- return JSON.stringify(this.savedStates);
- };
- /**
- * Loads all of the serialized states and shows them in the list of
- * viewable states.
- */
- BookmarkPanel.prototype.loadSavedStates = function (serializedStates) {
- this.savedStates = JSON.parse(serializedStates);
- this.updateHasStates();
- };
- /**
- * Updates the hasState polymer property.
- */
- BookmarkPanel.prototype.updateHasStates = function () {
- this.hasStates = (this.savedStates.length !== 0);
- };
- /** Sanity checks a State array to ensure it matches the current dataset. */
- BookmarkPanel.prototype.savedStatesValid = function (states) {
- for (var i = 0; i < states.length; i++) {
- if (states[i].dataSetDimensions[0] !== this.projector.dataSet.dim[0] ||
- states[i].dataSetDimensions[1] !== this.projector.dataSet.dim[1]) {
- return false;
- }
- }
- return true;
- };
- return BookmarkPanel;
-}(exports.BookmarkPanelPolymer));
-exports.BookmarkPanel = BookmarkPanel;
-document.registerElement(BookmarkPanel.prototype.is, BookmarkPanel);
-
-},{"./logging":12,"./vz-projector-util":33}],27:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-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 __());
-};
-var data_provider_1 = require('./data-provider');
-var util = require('./util');
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-exports.DataPanelPolymer = vz_projector_util_1.PolymerElement({
- is: 'vz-projector-data-panel',
- properties: {
- selectedTensor: { type: String, observer: '_selectedTensorChanged' },
- selectedRun: { type: String, observer: '_selectedRunChanged' },
- selectedColorOptionName: {
- type: String,
- notify: true,
- observer: '_selectedColorOptionNameChanged'
- },
- selectedLabelOption: { type: String, notify: true, observer: '_selectedLabelOptionChanged' },
- normalizeData: Boolean,
- showForceCategoricalColorsCheckbox: Boolean
- }
-});
-var DataPanel = (function (_super) {
- __extends(DataPanel, _super);
- function DataPanel() {
- _super.apply(this, arguments);
- this.forceCategoricalColoring = false;
- }
- DataPanel.prototype.ready = function () {
- this.dom = d3.select(this);
- this.normalizeData = true;
- };
- DataPanel.prototype.initialize = function (projector, dp) {
- var _this = this;
- this.projector = projector;
- this.dataProvider = dp;
- this.setupUploadButtons();
- // Tell the projector whenever the data normalization changes.
- // Unknown why, but the polymer checkbox button stops working as soon as
- // you do d3.select() on it.
- this.querySelector('#normalize-data-checkbox')
- .addEventListener('change', function () {
- _this.projector.setNormalizeData(_this.normalizeData);
- });
- var forceCategoricalColoringCheckbox = this.querySelector('#force-categorical-checkbox');
- forceCategoricalColoringCheckbox.addEventListener('change', function () {
- _this.setForceCategoricalColoring(forceCategoricalColoringCheckbox.checked);
- });
- // Get all the runs.
- this.dataProvider.retrieveRuns(function (runs) {
- _this.runNames = runs;
- // Choose the first run by default.
- if (_this.runNames.length > 0) {
- _this.selectedRun = runs[0];
- }
- });
- };
- DataPanel.prototype.setForceCategoricalColoring = function (forceCategoricalColoring) {
- this.forceCategoricalColoring = forceCategoricalColoring;
- this.querySelector('#force-categorical-checkbox')
- .checked = this.forceCategoricalColoring;
- this.updateMetadataUI(this.spriteAndMetadata.stats, this.metadataFile);
- // The selected color option name doesn't change when we switch to using
- // categorical coloring for stats with too many unique values, so we
- // manually call this polymer observer so that we update the UI.
- this._selectedColorOptionNameChanged();
- };
- DataPanel.prototype.getSeparatorClass = function (isSeparator) {
- return isSeparator ? 'separator' : null;
- };
- DataPanel.prototype.metadataChanged = function (spriteAndMetadata, metadataFile) {
- this.spriteAndMetadata = spriteAndMetadata;
- this.metadataFile = metadataFile;
- this.updateMetadataUI(this.spriteAndMetadata.stats, this.metadataFile);
- this.selectedColorOptionName = this.colorOptions[0].name;
- };
- DataPanel.prototype.addWordBreaks = function (longString) {
- if (longString == null) {
- return '';
- }
- return longString.replace(/([\/=-_,])/g, '$1<wbr>');
- };
- DataPanel.prototype.updateMetadataUI = function (columnStats, metadataFile) {
- var _this = this;
- this.dom.select('#metadata-file')
- .html(this.addWordBreaks(metadataFile))
- .attr('title', metadataFile);
- // Label by options.
- var labelIndex = -1;
- this.labelOptions = columnStats.map(function (stats, i) {
- // Make the default label by the first non-numeric column.
- if (!stats.isNumeric && labelIndex === -1) {
- labelIndex = i;
- }
- return stats.name;
- });
- this.selectedLabelOption = this.labelOptions[Math.max(0, labelIndex)];
- // Color by options.
- var standardColorOption = [
- { name: 'No color map' },
- ];
- var metadataColorOption = columnStats
- .filter(function (stats) {
- return !stats.tooManyUniqueValues || stats.isNumeric;
- })
- .map(function (stats) {
- var map;
- var items;
- var thresholds;
- var isCategorical = _this.forceCategoricalColoring || !stats.tooManyUniqueValues;
- if (isCategorical) {
- var scale = d3.scale.category20();
- var range_1 = scale.range();
- // Re-order the range.
- var newRange = range_1.map(function (color, i) {
- var index = (i * 3) % range_1.length;
- return range_1[index];
- });
- items = stats.uniqueEntries;
- scale.range(newRange).domain(items.map(function (x) { return x.label; }));
- map = scale;
- }
- else {
- thresholds = [
- { color: '#ffffdd', value: stats.min },
- { color: '#1f2d86', value: stats.max }
- ];
- map = d3.scale.linear()
- .domain(thresholds.map(function (t) { return t.value; }))
- .range(thresholds.map(function (t) { return t.color; }));
- }
- var desc = !isCategorical ? 'gradient' :
- stats.uniqueEntries.length +
- ((stats.uniqueEntries.length > 20) ? ' non-unique' : '') +
- ' colors';
- return {
- name: stats.name,
- desc: desc,
- map: map,
- items: items,
- thresholds: thresholds,
- tooManyUniqueValues: stats.tooManyUniqueValues
- };
- });
- if (metadataColorOption.length > 0) {
- // Add a separator line between built-in color maps
- // and those based on metadata columns.
- standardColorOption.push({ name: 'Metadata', isSeparator: true });
- }
- this.colorOptions = standardColorOption.concat(metadataColorOption);
- };
- DataPanel.prototype.setNormalizeData = function (normalizeData) {
- this.normalizeData = normalizeData;
- };
- DataPanel.prototype._selectedTensorChanged = function () {
- var _this = this;
- this.projector.updateDataSet(null, null, null);
- if (this.selectedTensor == null) {
- return;
- }
- this.dataProvider.retrieveTensor(this.selectedRun, this.selectedTensor, function (ds) {
- var metadataFile = _this.getEmbeddingInfoByName(_this.selectedTensor).metadataPath;
- _this.dataProvider.retrieveSpriteAndMetadata(_this.selectedRun, _this.selectedTensor, function (metadata) {
- _this.projector.updateDataSet(ds, metadata, metadataFile);
- });
- });
- this.projector.setSelectedTensor(this.selectedRun, this.getEmbeddingInfoByName(this.selectedTensor));
- };
- DataPanel.prototype._selectedRunChanged = function () {
- var _this = this;
- this.dataProvider.retrieveProjectorConfig(this.selectedRun, function (info) {
- _this.projectorConfig = info;
- var names = _this.projectorConfig.embeddings.map(function (e) { return e.tensorName; })
- .filter(function (name) {
- var shape = _this.getEmbeddingInfoByName(name).tensorShape;
- return shape.length === 2 && shape[0] > 1 && shape[1] > 1;
- })
- .sort(function (a, b) {
- var embA = _this.getEmbeddingInfoByName(a);
- var embB = _this.getEmbeddingInfoByName(b);
- // Prefer tensors with metadata.
- if (util.xor(!!embA.metadataPath, !!embB.metadataPath)) {
- return embA.metadataPath ? -1 : 1;
- }
- // Prefer non-generated tensors.
- var isGenA = util.tensorIsGenerated(a);
- var isGenB = util.tensorIsGenerated(b);
- if (util.xor(isGenA, isGenB)) {
- return isGenB ? -1 : 1;
- }
- // Prefer bigger tensors.
- var sizeA = embA.tensorShape[0];
- var sizeB = embB.tensorShape[0];
- if (sizeA !== sizeB) {
- return sizeB - sizeA;
- }
- // Sort alphabetically by tensor name.
- return a <= b ? -1 : 1;
- });
- _this.tensorNames = names.map(function (name) {
- return { name: name, shape: _this.getEmbeddingInfoByName(name).tensorShape };
- });
- var wordBreakablePath = _this.addWordBreaks(_this.projectorConfig.modelCheckpointPath);
- _this.dom.select('#checkpoint-file')
- .html(wordBreakablePath)
- .attr('title', _this.projectorConfig.modelCheckpointPath);
- // If in demo mode, let the order decide which tensor to load by default.
- var defaultTensor = _this.projector.servingMode === 'demo' ?
- _this.projectorConfig.embeddings[0].tensorName :
- names[0];
- if (_this.selectedTensor === defaultTensor) {
- // Explicitly call the observer. Polymer won't call it if the previous
- // string matches the current string.
- _this._selectedTensorChanged();
- }
- else {
- _this.selectedTensor = defaultTensor;
- }
- });
- };
- DataPanel.prototype._selectedLabelOptionChanged = function () {
- this.projector.setSelectedLabelOption(this.selectedLabelOption);
- };
- DataPanel.prototype._selectedColorOptionNameChanged = function () {
- var colorOption;
- for (var i = 0; i < this.colorOptions.length; i++) {
- if (this.colorOptions[i].name === this.selectedColorOptionName) {
- colorOption = this.colorOptions[i];
- break;
- }
- }
- if (!colorOption) {
- return;
- }
- this.showForceCategoricalColorsCheckbox = !!colorOption.tooManyUniqueValues;
- if (colorOption.map == null) {
- this.colorLegendRenderInfo = null;
- }
- else if (colorOption.items) {
- var items = colorOption.items.map(function (item) {
- return {
- color: colorOption.map(item.label),
- label: item.label,
- count: item.count
- };
- });
- this.colorLegendRenderInfo = { items: items, thresholds: null };
- }
- else {
- this.colorLegendRenderInfo = {
- items: null,
- thresholds: colorOption.thresholds
- };
- }
- this.projector.setSelectedColorOption(colorOption);
- };
- DataPanel.prototype.tensorWasReadFromFile = function (rawContents, fileName) {
- var _this = this;
- data_provider_1.parseRawTensors(rawContents, function (ds) {
- _this.dom.select('#checkpoint-file')
- .text(fileName)
- .attr('title', fileName);
- _this.projector.updateDataSet(ds);
- });
- };
- DataPanel.prototype.metadataWasReadFromFile = function (rawContents, fileName) {
- var _this = this;
- data_provider_1.parseRawMetadata(rawContents, function (metadata) {
- _this.projector.updateDataSet(_this.projector.dataSet, metadata, fileName);
- });
- };
- DataPanel.prototype.getEmbeddingInfoByName = function (tensorName) {
- for (var i = 0; i < this.projectorConfig.embeddings.length; i++) {
- var e = this.projectorConfig.embeddings[i];
- if (e.tensorName === tensorName) {
- return e;
- }
- }
- };
- DataPanel.prototype.setupUploadButtons = function () {
- var _this = this;
- // Show and setup the upload button.
- var fileInput = this.dom.select('#file');
- fileInput.on('change', function () {
- var file = d3.event.target.files[0];
- // Clear out the value of the file chooser. This ensures that if the user
- // selects the same file, we'll re-read it.
- d3.event.target.value = '';
- var fileReader = new FileReader();
- fileReader.onload = function (evt) {
- var content = evt.target.result;
- _this.tensorWasReadFromFile(content, file.name);
- };
- fileReader.readAsArrayBuffer(file);
- });
- var uploadButton = this.dom.select('#upload-tensors');
- uploadButton.on('click', function () {
- fileInput.node().click();
- });
- // Show and setup the upload metadata button.
- var fileMetadataInput = this.dom.select('#file-metadata');
- fileMetadataInput.on('change', function () {
- var file = d3.event.target.files[0];
- // Clear out the value of the file chooser. This ensures that if the user
- // selects the same file, we'll re-read it.
- d3.event.target.value = '';
- var fileReader = new FileReader();
- fileReader.onload = function (evt) {
- var contents = evt.target.result;
- _this.metadataWasReadFromFile(contents, file.name);
- };
- fileReader.readAsArrayBuffer(file);
- });
- var uploadMetadataButton = this.dom.select('#upload-metadata');
- uploadMetadataButton.on('click', function () {
- fileMetadataInput.node().click();
- });
- if (this.projector.servingMode !== 'demo') {
- this.$$('#publish-container').style.display = 'none';
- this.$$('#upload-tensors-step-container').style.display =
- 'none';
- this.$$('#upload-metadata-label').style.display = 'none';
- }
- this.$$('#demo-data-buttons-container').style.display =
- 'block';
- // Fill out the projector config.
- var projectorConfigTemplate = this.$$('#projector-config-template');
- var projectorConfigTemplateJson = {
- embeddings: [{
- tensorName: 'My tensor',
- tensorShape: [1000, 50],
- tensorPath: 'https://raw.githubusercontent.com/.../tensors.tsv',
- metadataPath: 'https://raw.githubusercontent.com/.../optional.metadata.tsv',
- }],
- };
- this.setProjectorConfigTemplateJson(projectorConfigTemplate, projectorConfigTemplateJson);
- // Set up optional field checkboxes.
- var spriteFieldCheckbox = this.$$('#config-sprite-checkbox');
- spriteFieldCheckbox.addEventListener('change', function () {
- if (spriteFieldCheckbox.checked) {
- projectorConfigTemplateJson.embeddings[0].sprite = {
- imagePath: 'https://github.com/.../optional.sprite.png',
- singleImageDim: [32, 32]
- };
- }
- else {
- delete projectorConfigTemplateJson.embeddings[0].sprite;
- }
- _this.setProjectorConfigTemplateJson(projectorConfigTemplate, projectorConfigTemplateJson);
- });
- var bookmarksFieldCheckbox = this.$$('#config-bookmarks-checkbox');
- bookmarksFieldCheckbox.addEventListener('change', function () {
- if (bookmarksFieldCheckbox.checked) {
- projectorConfigTemplateJson.embeddings[0].bookmarksPath =
- 'https://raw.githubusercontent.com/.../bookmarks.txt';
- }
- else {
- delete projectorConfigTemplateJson.embeddings[0].bookmarksPath;
- }
- _this.setProjectorConfigTemplateJson(projectorConfigTemplate, projectorConfigTemplateJson);
- });
- var metadataFieldCheckbox = this.$$('#config-metadata-checkbox');
- metadataFieldCheckbox.addEventListener('change', function () {
- if (metadataFieldCheckbox.checked) {
- projectorConfigTemplateJson.embeddings[0].metadataPath =
- 'https://raw.githubusercontent.com/.../optional.metadata.tsv';
- }
- else {
- delete projectorConfigTemplateJson.embeddings[0].metadataPath;
- }
- _this.setProjectorConfigTemplateJson(projectorConfigTemplate, projectorConfigTemplateJson);
- });
- // Update the link and the readonly shareable URL.
- var projectorConfigUrlInput = this.$$('#projector-config-url');
- var projectorConfigDemoUrlInput = this.$$('#projector-share-url');
- var projectorConfigDemoUrlLink = this.$$('#projector-share-url-link');
- projectorConfigUrlInput.addEventListener('input', function () {
- var projectorDemoUrl = location.protocol + '//' + location.host +
- location.pathname +
- '?config=' + projectorConfigUrlInput.value;
- projectorConfigDemoUrlInput.value =
- projectorDemoUrl;
- projectorConfigDemoUrlLink.href = projectorDemoUrl;
- });
- };
- DataPanel.prototype.setProjectorConfigTemplateJson = function (projectorConfigTemplate, config) {
- projectorConfigTemplate.value =
- JSON.stringify(config, null, /** replacer */ 2 /** white space */);
- };
- DataPanel.prototype._getNumTensorsLabel = function () {
- return this.tensorNames.length === 1 ? '1 tensor' :
- this.tensorNames.length + ' tensors';
- };
- DataPanel.prototype._getNumRunsLabel = function () {
- return this.runNames.length === 1 ? '1 run' :
- this.runNames.length + ' runs';
- };
- DataPanel.prototype._hasChoices = function (choices) {
- return choices.length > 1;
- };
- return DataPanel;
-}(exports.DataPanelPolymer));
-exports.DataPanel = DataPanel;
-document.registerElement(DataPanel.prototype.is, DataPanel);
-
-},{"./data-provider":6,"./util":24,"./vz-projector-util":33}],28:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-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 __());
-};
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-// tslint:disable-next-line
-exports.PolymerClass = vz_projector_util_1.PolymerElement({ is: 'vz-projector-input', properties: { label: String, message: String } });
-/** Input control with custom capabilities (e.g. regex). */
-var ProjectorInput = (function (_super) {
- __extends(ProjectorInput, _super);
- function ProjectorInput() {
- _super.apply(this, arguments);
- }
- /** Subscribe to be called everytime the input changes. */
- ProjectorInput.prototype.registerInputChangedListener = function (listener) {
- this.textChangedListeners.push(listener);
- };
- ProjectorInput.prototype.ready = function () {
- var _this = this;
- this.inRegexMode = false;
- this.textChangedListeners = [];
- this.dom = d3.select(this);
- this.paperInput = this.querySelector('paper-input');
- this.inRegexModeButton =
- this.querySelector('paper-button');
- this.paperInput.setAttribute('error-message', 'Invalid regex');
- this.paperInput.addEventListener('input', function () {
- _this.onTextChanged();
- });
- this.paperInput.addEventListener('keydown', function (event) {
- event.stopPropagation();
- });
- this.inRegexModeButton.addEventListener('click', function () { return _this.onClickRegexModeButton(); });
- this.updateRegexModeDisplaySlashes();
- this.onTextChanged();
- };
- ProjectorInput.prototype.onClickRegexModeButton = function () {
- this.inRegexMode = this.inRegexModeButton.active;
- this.updateRegexModeDisplaySlashes();
- this.onTextChanged();
- };
- ProjectorInput.prototype.notifyInputChanged = function (value, inRegexMode) {
- this.textChangedListeners.forEach(function (l) { return l(value, inRegexMode); });
- };
- ProjectorInput.prototype.onTextChanged = function () {
- try {
- if (this.inRegexMode) {
- new RegExp(this.paperInput.value);
- }
- }
- catch (invalidRegexException) {
- this.paperInput.setAttribute('invalid', 'true');
- this.message = '';
- this.notifyInputChanged(null, true);
- return;
- }
- this.paperInput.removeAttribute('invalid');
- this.notifyInputChanged(this.paperInput.value, this.inRegexMode);
- };
- ProjectorInput.prototype.updateRegexModeDisplaySlashes = function () {
- d3.select(this.paperInput)
- .selectAll('.slash')
- .style('display', this.inRegexMode ? null : 'none');
- };
- ProjectorInput.prototype.getValue = function () {
- return this.paperInput.value;
- };
- ProjectorInput.prototype.getInRegexMode = function () {
- return this.inRegexMode;
- };
- ProjectorInput.prototype.set = function (value, inRegexMode) {
- this.inRegexModeButton.active = inRegexMode;
- this.paperInput.value = value;
- this.onClickRegexModeButton();
- };
- return ProjectorInput;
-}(exports.PolymerClass));
-exports.ProjectorInput = ProjectorInput;
-document.registerElement(ProjectorInput.prototype.is, ProjectorInput);
-
-},{"./vz-projector-util":33}],29:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-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 __());
-};
-var adapter = require('./projectorScatterPlotAdapter');
-var vector = require('./vector');
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-/** Limit the number of search results we show to the user. */
-var LIMIT_RESULTS = 100;
-// tslint:disable-next-line
-exports.PolymerClass = vz_projector_util_1.PolymerElement({
- is: 'vz-projector-inspector-panel',
- properties: { selectedMetadataField: String, metadataFields: Array }
-});
-var InspectorPanel = (function (_super) {
- __extends(InspectorPanel, _super);
- function InspectorPanel() {
- _super.apply(this, arguments);
- }
- InspectorPanel.prototype.ready = function () {
- this.dom = d3.select(this);
- this.resetFilterButton = this.dom.select('.reset-filter');
- this.setFilterButton = this.dom.select('.set-filter');
- this.clearSelectionButton = this.dom.select('.clear-selection');
- this.limitMessage = this.dom.select('.limit-msg');
- this.searchBox = this.querySelector('#search-box');
- // https://www.polymer-project.org/1.0/docs/devguide/styling#scope-subtree
- this.scopeSubtree(this, true);
- };
- InspectorPanel.prototype.initialize = function (projector, projectorEventContext) {
- var _this = this;
- this.projector = projector;
- this.projectorEventContext = projectorEventContext;
- this.setupUI(projector);
- projectorEventContext.registerSelectionChangedListener(function (selection, neighbors) {
- return _this.updateInspectorPane(selection, neighbors);
- });
- };
- /** Updates the nearest neighbors list in the inspector. */
- InspectorPanel.prototype.updateInspectorPane = function (indices, neighbors) {
- this.neighborsOfFirstPoint = neighbors;
- this.selectedPointIndices = indices;
- this.updateFilterButtons(indices.length + neighbors.length);
- this.updateNeighborsList(neighbors);
- if (neighbors.length === 0) {
- this.updateSearchResults(indices);
- }
- else {
- this.updateSearchResults([]);
- }
- };
- InspectorPanel.prototype.enableResetFilterButton = function (enabled) {
- this.resetFilterButton.attr('disabled', enabled ? null : true);
- };
- InspectorPanel.prototype.restoreUIFromBookmark = function (bookmark) {
- this.enableResetFilterButton(bookmark.filteredPoints != null);
- };
- InspectorPanel.prototype.metadataChanged = function (spriteAndMetadata) {
- var labelIndex = -1;
- this.metadataFields = spriteAndMetadata.stats.map(function (stats, i) {
- if (!stats.isNumeric && labelIndex === -1) {
- labelIndex = i;
- }
- return stats.name;
- });
- labelIndex = Math.max(0, labelIndex);
- // Make the default label the first non-numeric column.
- this.selectedMetadataField = spriteAndMetadata.stats[labelIndex].name;
- };
- InspectorPanel.prototype.datasetChanged = function () {
- this.enableResetFilterButton(false);
- };
- InspectorPanel.prototype.updateSearchResults = function (indices) {
- var _this = this;
- var container = this.dom.select('.matches-list');
- container.style('display', indices.length ? null : 'none');
- var list = container.select('.list');
- list.html('');
- if (indices.length === 0) {
- return;
- }
- this.limitMessage.style('display', indices.length <= LIMIT_RESULTS ? 'none' : null);
- indices = indices.slice(0, LIMIT_RESULTS);
- var rows = list.selectAll('.row').data(indices).enter().append('div').attr('class', 'row');
- rows.append('a')
- .attr('class', 'label')
- .attr('title', function (index) { return _this.getLabelFromIndex(index); })
- .text(function (index) { return _this.getLabelFromIndex(index); });
- rows.on('mouseenter', function (index) {
- _this.projectorEventContext.notifyHoverOverPoint(index);
- });
- rows.on('mouseleave', function () {
- _this.projectorEventContext.notifyHoverOverPoint(null);
- });
- rows.on('click', function (index) {
- _this.projectorEventContext.notifySelectionChanged([index]);
- });
- };
- InspectorPanel.prototype.getLabelFromIndex = function (pointIndex) {
- var point = this.projector.dataSet.points[pointIndex];
- return point.metadata[this.selectedMetadataField].toString();
- };
- InspectorPanel.prototype.updateNeighborsList = function (neighbors) {
- var _this = this;
- var nnlist = this.dom.select('.nn-list');
- nnlist.html('');
- this.dom.select('.nn').style('display', neighbors.length ? null : 'none');
- if (neighbors.length === 0) {
- return;
- }
- this.searchBox.message = '';
- var minDist = neighbors.length > 0 ? neighbors[0].dist : 0;
- var n = nnlist.selectAll('.neighbor')
- .data(neighbors)
- .enter()
- .append('div')
- .attr('class', 'neighbor')
- .append('a')
- .attr('class', 'neighbor-link')
- .attr('title', function (d) { return _this.getLabelFromIndex(d.index); });
- var labelValue = n.append('div').attr('class', 'label-and-value');
- labelValue.append('div')
- .attr('class', 'label')
- .style('color', function (d) { return adapter.dist2color(_this.distFunc, d.dist, minDist); })
- .text(function (d) { return _this.getLabelFromIndex(d.index); });
- labelValue.append('div')
- .attr('class', 'value')
- .text(function (d) { return d.dist.toFixed(3); });
- var bar = n.append('div').attr('class', 'bar');
- bar.append('div')
- .attr('class', 'fill')
- .style('border-top-color', function (d) {
- return adapter.dist2color(_this.distFunc, d.dist, minDist);
- })
- .style('width', function (d) { return adapter.normalizeDist(_this.distFunc, d.dist, minDist) * 100 +
- '%'; });
- bar.selectAll('.tick')
- .data(d3.range(1, 4))
- .enter()
- .append('div')
- .attr('class', 'tick')
- .style('left', function (d) { return d * 100 / 4 + '%'; });
- n.on('mouseenter', function (d) {
- _this.projectorEventContext.notifyHoverOverPoint(d.index);
- });
- n.on('mouseleave', function () {
- _this.projectorEventContext.notifyHoverOverPoint(null);
- });
- n.on('click', function (d) {
- _this.projectorEventContext.notifySelectionChanged([d.index]);
- });
- };
- InspectorPanel.prototype.updateFilterButtons = function (numPoints) {
- if (numPoints > 1) {
- this.setFilterButton.text("Isolate " + numPoints + " points")
- .attr('disabled', null);
- this.clearSelectionButton.attr('disabled', null);
- }
- else {
- this.setFilterButton.attr('disabled', true);
- this.clearSelectionButton.attr('disabled', true);
- }
- };
- InspectorPanel.prototype.setupUI = function (projector) {
- var _this = this;
- this.distFunc = vector.cosDist;
- var eucDist = this.dom.select('.distance a.euclidean');
- eucDist.on('click', function () {
- _this.dom.selectAll('.distance a').classed('selected', false);
- eucDist.classed('selected', true);
- _this.distFunc = vector.dist;
- _this.projectorEventContext.notifyDistanceMetricChanged(_this.distFunc);
- var neighbors = projector.dataSet.findNeighbors(_this.selectedPointIndices[0], _this.distFunc, _this.numNN);
- _this.updateNeighborsList(neighbors);
- });
- var cosDist = this.dom.select('.distance a.cosine');
- cosDist.on('click', function () {
- _this.dom.selectAll('.distance a').classed('selected', false);
- cosDist.classed('selected', true);
- _this.distFunc = vector.cosDist;
- _this.projectorEventContext.notifyDistanceMetricChanged(_this.distFunc);
- var neighbors = projector.dataSet.findNeighbors(_this.selectedPointIndices[0], _this.distFunc, _this.numNN);
- _this.updateNeighborsList(neighbors);
- });
- // Called whenever the search text input changes.
- var updateInput = function (value, inRegexMode) {
- if (value == null || value.trim() === '') {
- _this.searchBox.message = '';
- _this.projectorEventContext.notifySelectionChanged([]);
- return;
- }
- var indices = projector.dataSet.query(value, inRegexMode, _this.selectedMetadataField);
- if (indices.length === 0) {
- _this.searchBox.message = '0 matches.';
- }
- else {
- _this.searchBox.message = indices.length + " matches.";
- }
- _this.projectorEventContext.notifySelectionChanged(indices);
- };
- this.searchBox.registerInputChangedListener(function (value, inRegexMode) {
- updateInput(value, inRegexMode);
- });
- // Nearest neighbors controls.
- var numNNInput = this.$$('#nn-slider');
- var updateNumNN = function () {
- _this.numNN = +numNNInput.value;
- _this.dom.select('.num-nn .nn-count').text(_this.numNN);
- if (_this.selectedPointIndices != null) {
- _this.projectorEventContext.notifySelectionChanged([_this.selectedPointIndices[0]]);
- }
- };
- numNNInput.addEventListener('change', updateNumNN);
- updateNumNN();
- // Filtering dataset.
- this.setFilterButton.on('click', function () {
- var indices = _this.selectedPointIndices.concat(_this.neighborsOfFirstPoint.map(function (n) { return n.index; }));
- projector.filterDataset(indices);
- _this.enableResetFilterButton(true);
- _this.updateFilterButtons(0);
- });
- this.resetFilterButton.on('click', function () {
- projector.resetFilterDataset();
- _this.enableResetFilterButton(false);
- });
- this.clearSelectionButton.on('click', function () {
- projector.adjustSelectionAndHover([]);
- });
- this.enableResetFilterButton(false);
- };
- return InspectorPanel;
-}(exports.PolymerClass));
-exports.InspectorPanel = InspectorPanel;
-document.registerElement(InspectorPanel.prototype.is, InspectorPanel);
-
-},{"./projectorScatterPlotAdapter":14,"./vector":25,"./vz-projector-util":33}],30:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-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 __());
-};
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-// tslint:disable-next-line
-exports.LegendPolymer = vz_projector_util_1.PolymerElement({
- is: 'vz-projector-legend',
- properties: { renderInfo: { type: Object, observer: '_renderInfoChanged' } }
-});
-var Legend = (function (_super) {
- __extends(Legend, _super);
- function Legend() {
- _super.apply(this, arguments);
- }
- Legend.prototype.ready = function () {
- this.dom = d3.select(this);
- };
- Legend.prototype._renderInfoChanged = function () {
- var _this = this;
- if (this.renderInfo == null) {
- return;
- }
- if (this.renderInfo.thresholds) {
- // <linearGradient> is under dom-if so we should wait for it to be
- // inserted in the dom tree using async().
- this.async(function () { return _this.setupLinearGradient(); });
- }
- };
- Legend.prototype._getLastThreshold = function () {
- if (this.renderInfo == null || this.renderInfo.thresholds == null) {
- return;
- }
- return this.renderInfo.thresholds[this.renderInfo.thresholds.length - 1]
- .value;
- };
- Legend.prototype.getOffset = function (value) {
- var min = this.renderInfo.thresholds[0].value;
- var max = this.renderInfo.thresholds[this.renderInfo.thresholds.length - 1].value;
- return (100 * (value - min) / (max - min)).toFixed(2) + '%';
- };
- Legend.prototype.setupLinearGradient = function () {
- var _this = this;
- var linearGradient = this.dom.select('#gradient');
- var width = this.dom.select('svg.gradient').node().clientWidth;
- // Set the svg <rect> to be the width of its <svg> parent.
- this.dom.select('svg.gradient rect').attr('width', width);
- // Remove all <stop> children from before.
- linearGradient.selectAll('*').remove();
- // Add a <stop> child in <linearGradient> for each gradient threshold.
- this.renderInfo.thresholds.forEach(function (t) {
- linearGradient.append('stop')
- .attr('offset', _this.getOffset(t.value))
- .attr('stop-color', t.color);
- });
- };
- return Legend;
-}(exports.LegendPolymer));
-exports.Legend = Legend;
-document.registerElement(Legend.prototype.is, Legend);
-
-},{"./vz-projector-util":33}],31:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-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 __());
-};
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-// tslint:disable-next-line
-exports.MetadataCardPolymer = vz_projector_util_1.PolymerElement({
- is: 'vz-projector-metadata-card',
- properties: {
- hasMetadata: { type: Boolean, value: false },
- metadata: { type: Array },
- label: String
- }
-});
-var MetadataCard = (function (_super) {
- __extends(MetadataCard, _super);
- function MetadataCard() {
- _super.apply(this, arguments);
- }
- MetadataCard.prototype.ready = function () {
- this.dom = d3.select(this);
- };
- /** Handles a click on the expand more icon. */
- MetadataCard.prototype._expandMore = function () {
- this.$$('#metadata-container').toggle();
- this.dom.select('#expand-more').style('display', 'none');
- this.dom.select('#expand-less').style('display', '');
- };
- /** Handles a click on the expand less icon. */
- MetadataCard.prototype._expandLess = function () {
- this.$$('#metadata-container').toggle();
- this.dom.select('#expand-more').style('display', '');
- this.dom.select('#expand-less').style('display', 'none');
- };
- MetadataCard.prototype.updateMetadata = function (pointMetadata) {
- this.pointMetadata = pointMetadata;
- this.hasMetadata = (pointMetadata != null);
- if (pointMetadata) {
- var metadata = [];
- for (var metadataKey in pointMetadata) {
- if (!pointMetadata.hasOwnProperty(metadataKey)) {
- continue;
- }
- metadata.push({ key: metadataKey, value: pointMetadata[metadataKey] });
- }
- this.metadata = metadata;
- this.label = '' + this.pointMetadata[this.labelOption];
- }
- };
- MetadataCard.prototype.setLabelOption = function (labelOption) {
- this.labelOption = labelOption;
- if (this.pointMetadata) {
- this.label = '' + this.pointMetadata[this.labelOption];
- }
- };
- return MetadataCard;
-}(exports.MetadataCardPolymer));
-exports.MetadataCard = MetadataCard;
-document.registerElement(MetadataCard.prototype.is, MetadataCard);
-
-},{"./vz-projector-util":33}],32:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-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 __());
-};
-var data = require('./data');
-var data_1 = require('./data');
-var vector = require('./vector');
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-var NUM_PCA_COMPONENTS = 10;
-// tslint:disable-next-line
-exports.ProjectionsPanelPolymer = vz_projector_util_1.PolymerElement({
- is: 'vz-projector-projections-panel',
- properties: {
- pcaIs3d: { type: Boolean, value: true, observer: '_pcaDimensionToggleObserver' },
- tSNEis3d: { type: Boolean, value: true, observer: '_tsneDimensionToggleObserver' },
- // PCA projection.
- pcaComponents: Array,
- pcaX: { type: Number, value: 0, observer: 'showPCAIfEnabled' },
- pcaY: { type: Number, value: 1, observer: 'showPCAIfEnabled' },
- pcaZ: { type: Number, value: 2, observer: 'showPCAIfEnabled' },
- // Custom projection.
- customSelectedSearchByMetadataOption: {
- type: String,
- observer: '_customSelectedSearchByMetadataOptionChanged'
- },
- }
-});
-/**
- * A polymer component which handles the projection tabs in the projector.
- */
-var ProjectionsPanel = (function (_super) {
- __extends(ProjectionsPanel, _super);
- function ProjectionsPanel() {
- _super.apply(this, arguments);
- }
- ProjectionsPanel.prototype.initialize = function (projector) {
- this.polymerChangesTriggerReprojection = true;
- this.projector = projector;
- // Set up TSNE projections.
- this.perplexity = 30;
- this.learningRate = 10;
- // Setup Custom projections.
- this.centroidValues = { xLeft: null, xRight: null, yUp: null, yDown: null };
- this.clearCentroids();
- this.setupUIControls();
- };
- ProjectionsPanel.prototype.ready = function () {
- this.dom = d3.select(this);
- this.zDropdown = this.dom.select('#z-dropdown');
- this.runTsneButton = this.dom.select('.run-tsne');
- this.stopTsneButton = this.dom.select('.stop-tsne');
- this.perplexitySlider = this.$$('#perplexity-slider');
- this.learningRateInput =
- this.$$('#learning-rate-slider');
- this.iterationLabel = this.dom.select('.run-tsne-iter');
- };
- ProjectionsPanel.prototype.disablePolymerChangesTriggerReprojection = function () {
- this.polymerChangesTriggerReprojection = false;
- };
- ProjectionsPanel.prototype.enablePolymerChangesTriggerReprojection = function () {
- this.polymerChangesTriggerReprojection = true;
- };
- ProjectionsPanel.prototype.updateTSNEPerplexityFromSliderChange = function () {
- if (this.perplexitySlider) {
- this.perplexity = +this.perplexitySlider.value;
- }
- this.dom.select('.tsne-perplexity span').text(this.perplexity);
- };
- ProjectionsPanel.prototype.updateTSNELearningRateFromUIChange = function () {
- if (this.learningRateInput) {
- this.learningRate = Math.pow(10, +this.learningRateInput.value);
- }
- this.dom.select('.tsne-learning-rate span').text(this.learningRate);
- };
- ProjectionsPanel.prototype.setupUIControls = function () {
- var _this = this;
- {
- var self_1 = this;
- this.dom.selectAll('.ink-tab').on('click', function () {
- var id = this.getAttribute('data-tab');
- self_1.showTab(id);
- });
- }
- this.runTsneButton.on('click', function () { return _this.runTSNE(); });
- this.stopTsneButton.on('click', function () { return _this.dataSet.stopTSNE(); });
- this.perplexitySlider.value = this.perplexity.toString();
- this.perplexitySlider.addEventListener('change', function () { return _this.updateTSNEPerplexityFromSliderChange(); });
- this.updateTSNEPerplexityFromSliderChange();
- this.learningRateInput.addEventListener('change', function () { return _this.updateTSNELearningRateFromUIChange(); });
- this.updateTSNELearningRateFromUIChange();
- this.setupCustomProjectionInputFields();
- // TODO: figure out why `--paper-input-container-input` css mixin didn't
- // work.
- this.dom.selectAll('paper-dropdown-menu paper-input input')
- .style('font-size', '14px');
- };
- ProjectionsPanel.prototype.restoreUIFromBookmark = function (bookmark) {
- this.disablePolymerChangesTriggerReprojection();
- // PCA
- this.pcaX = bookmark.pcaComponentDimensions[0];
- this.pcaY = bookmark.pcaComponentDimensions[1];
- if (bookmark.pcaComponentDimensions.length === 3) {
- this.pcaZ = bookmark.pcaComponentDimensions[2];
- }
- this.pcaIs3d = (bookmark.pcaComponentDimensions.length === 3);
- // t-SNE
- if (this.perplexitySlider) {
- this.perplexitySlider.value = bookmark.tSNEPerplexity.toString();
- }
- if (this.learningRateInput) {
- this.learningRateInput.value = bookmark.tSNELearningRate.toString();
- }
- this.tSNEis3d = bookmark.tSNEis3d;
- // custom
- this.customSelectedSearchByMetadataOption =
- bookmark.customSelectedSearchByMetadataOption;
- if (this.customProjectionXLeftInput) {
- this.customProjectionXLeftInput.set(bookmark.customXLeftText, bookmark.customXLeftRegex);
- }
- if (this.customProjectionXRightInput) {
- this.customProjectionXRightInput.set(bookmark.customXRightText, bookmark.customXRightRegex);
- }
- if (this.customProjectionYUpInput) {
- this.customProjectionYUpInput.set(bookmark.customYUpText, bookmark.customYUpRegex);
- }
- if (this.customProjectionYDownInput) {
- this.customProjectionYDownInput.set(bookmark.customYDownText, bookmark.customYDownRegex);
- }
- this.computeAllCentroids();
- this.setZDropdownEnabled(this.pcaIs3d);
- this.updateTSNEPerplexityFromSliderChange();
- this.updateTSNELearningRateFromUIChange();
- if (this.iterationLabel) {
- this.iterationLabel.text(bookmark.tSNEIteration.toString());
- }
- this.showTab(bookmark.selectedProjection);
- this.enablePolymerChangesTriggerReprojection();
- };
- ProjectionsPanel.prototype.populateBookmarkFromUI = function (bookmark) {
- this.disablePolymerChangesTriggerReprojection();
- // PCA
- bookmark.pcaComponentDimensions = [this.pcaX, this.pcaY];
- if (this.pcaIs3d) {
- bookmark.pcaComponentDimensions.push(this.pcaZ);
- }
- // t-SNE
- if (this.perplexitySlider != null) {
- bookmark.tSNEPerplexity = +this.perplexitySlider.value;
- }
- if (this.learningRateInput != null) {
- bookmark.tSNELearningRate = +this.learningRateInput.value;
- }
- bookmark.tSNEis3d = this.tSNEis3d;
- // custom
- bookmark.customSelectedSearchByMetadataOption =
- this.customSelectedSearchByMetadataOption;
- if (this.customProjectionXLeftInput != null) {
- bookmark.customXLeftText = this.customProjectionXLeftInput.getValue();
- bookmark.customXLeftRegex =
- this.customProjectionXLeftInput.getInRegexMode();
- }
- if (this.customProjectionXRightInput != null) {
- bookmark.customXRightText = this.customProjectionXRightInput.getValue();
- bookmark.customXRightRegex =
- this.customProjectionXRightInput.getInRegexMode();
- }
- if (this.customProjectionYUpInput != null) {
- bookmark.customYUpText = this.customProjectionYUpInput.getValue();
- bookmark.customYUpRegex = this.customProjectionYUpInput.getInRegexMode();
- }
- if (this.customProjectionYDownInput != null) {
- bookmark.customYDownText = this.customProjectionYDownInput.getValue();
- bookmark.customYDownRegex =
- this.customProjectionYDownInput.getInRegexMode();
- }
- this.enablePolymerChangesTriggerReprojection();
- };
- // This method is marked as public as it is used as the view method that
- // abstracts DOM manipulation so we can stub it in a test.
- // TODO(nsthorat): Move this to its own class as the glue between this class
- // and the DOM.
- ProjectionsPanel.prototype.setZDropdownEnabled = function (enabled) {
- if (this.zDropdown) {
- this.zDropdown.attr('disabled', enabled ? null : true);
- }
- };
- ProjectionsPanel.prototype.dataSetUpdated = function (dataSet, originalDataSet, dim) {
- this.dataSet = dataSet;
- this.originalDataSet = originalDataSet;
- this.dim = dim;
- var pointCount = (dataSet == null) ? 0 : dataSet.points.length;
- var perplexity = Math.max(5, Math.ceil(Math.sqrt(pointCount) / 4));
- this.perplexitySlider.value = perplexity.toString();
- this.updateTSNEPerplexityFromSliderChange();
- this.clearCentroids();
- this.dom.select('#tsne-sampling')
- .style('display', pointCount > data.TSNE_SAMPLE_SIZE ? null : 'none');
- var wasSampled = (dataSet == null) ? false : (dataSet.dim[0] > data.PCA_SAMPLE_DIM ||
- dataSet.dim[1] > data.PCA_SAMPLE_DIM);
- this.dom.select('#pca-sampling')
- .style('display', wasSampled ? null : 'none');
- this.showTab('pca');
- };
- ProjectionsPanel.prototype._pcaDimensionToggleObserver = function () {
- this.setZDropdownEnabled(this.pcaIs3d);
- this.beginProjection(this.currentProjection);
- };
- ProjectionsPanel.prototype._tsneDimensionToggleObserver = function () {
- this.beginProjection(this.currentProjection);
- };
- ProjectionsPanel.prototype.metadataChanged = function (spriteAndMetadata) {
- // Project by options for custom projections.
- var searchByMetadataIndex = -1;
- this.searchByMetadataOptions = spriteAndMetadata.stats.map(function (stats, i) {
- // Make the default label by the first non-numeric column.
- if (!stats.isNumeric && searchByMetadataIndex === -1) {
- searchByMetadataIndex = i;
- }
- return stats.name;
- });
- this.customSelectedSearchByMetadataOption =
- this.searchByMetadataOptions[Math.max(0, searchByMetadataIndex)];
- };
- ProjectionsPanel.prototype.showTab = function (id) {
- var _this = this;
- this.currentProjection = id;
- var tab = this.dom.select('.ink-tab[data-tab="' + id + '"]');
- this.dom.selectAll('.ink-tab').classed('active', false);
- tab.classed('active', true);
- this.dom.selectAll('.ink-panel-content').classed('active', false);
- this.dom.select('.ink-panel-content[data-panel="' + id + '"]')
- .classed('active', true);
- // guard for unit tests, where polymer isn't attached and $ doesn't exist.
- if (this.$ != null) {
- var main_1 = this.$['main'];
- // In order for the projections panel to animate its height, we need to
- // set it explicitly.
- requestAnimationFrame(function () {
- _this.style.height = main_1.clientHeight + 'px';
- });
- }
- this.beginProjection(id);
- };
- ProjectionsPanel.prototype.beginProjection = function (projection) {
- if (this.polymerChangesTriggerReprojection === false) {
- return;
- }
- if (projection === 'pca') {
- if (this.dataSet != null) {
- this.dataSet.stopTSNE();
- }
- this.showPCA();
- }
- else if (projection === 'tsne') {
- this.showTSNE();
- }
- else if (projection === 'custom') {
- if (this.dataSet != null) {
- this.dataSet.stopTSNE();
- }
- this.computeAllCentroids();
- this.reprojectCustom();
- }
- };
- ProjectionsPanel.prototype.showTSNE = function () {
- var dataSet = this.dataSet;
- if (dataSet == null) {
- return;
- }
- var accessors = data.getProjectionComponents('tsne', [0, 1, this.tSNEis3d ? 2 : null]);
- var dimensionality = this.tSNEis3d ? 3 : 2;
- var projection = new data_1.Projection('tsne', accessors, dimensionality, dataSet);
- this.projector.setProjection(projection);
- if (!this.dataSet.hasTSNERun) {
- this.runTSNE();
- }
- else {
- this.projector.notifyProjectionPositionsUpdated();
- }
- };
- ProjectionsPanel.prototype.runTSNE = function () {
- var _this = this;
- this.runTsneButton.attr('disabled', true);
- this.stopTsneButton.attr('disabled', null);
- this.dataSet.projectTSNE(this.perplexity, this.learningRate, this.tSNEis3d ? 3 : 2, function (iteration) {
- if (iteration != null) {
- _this.iterationLabel.text(iteration);
- _this.projector.notifyProjectionPositionsUpdated();
- }
- else {
- _this.runTsneButton.attr('disabled', null);
- _this.stopTsneButton.attr('disabled', true);
- }
- });
- };
- // tslint:disable-next-line:no-unused-variable
- ProjectionsPanel.prototype.showPCAIfEnabled = function () {
- if (this.polymerChangesTriggerReprojection) {
- this.showPCA();
- }
- };
- ProjectionsPanel.prototype.updateTotalVarianceMessage = function () {
- var variances = this.dataSet.fracVariancesExplained;
- var totalVariance = variances[this.pcaX] + variances[this.pcaY];
- var msg = 'Total variance described: ';
- if (this.pcaIs3d) {
- totalVariance += variances[this.pcaZ];
- }
- msg += (totalVariance * 100).toFixed(1) + '%.';
- this.dom.select('#total-variance').html(msg);
- };
- ProjectionsPanel.prototype.showPCA = function () {
- var _this = this;
- if (this.dataSet == null) {
- return;
- }
- this.dataSet.projectPCA().then(function () {
- // Polymer properties are 1-based.
- var accessors = data.getProjectionComponents('pca', [_this.pcaX, _this.pcaY, _this.pcaZ]);
- var dimensionality = _this.pcaIs3d ? 3 : 2;
- var projection = new data_1.Projection('pca', accessors, dimensionality, _this.dataSet);
- _this.projector.setProjection(projection);
- var numComponents = Math.min(NUM_PCA_COMPONENTS, _this.dataSet.dim[1]);
- _this.updateTotalVarianceMessage();
- _this.pcaComponents = d3.range(0, numComponents).map(function (i) {
- var fracVariance = _this.dataSet.fracVariancesExplained[i];
- return {
- id: i,
- componentNumber: i + 1,
- percVariance: (fracVariance * 100).toFixed(1)
- };
- });
- });
- };
- ProjectionsPanel.prototype.reprojectCustom = function () {
- if (this.centroids == null || this.centroids.xLeft == null ||
- this.centroids.xRight == null || this.centroids.yUp == null ||
- this.centroids.yDown == null) {
- return;
- }
- var xDir = vector.sub(this.centroids.xRight, this.centroids.xLeft);
- this.dataSet.projectLinear(xDir, 'linear-x');
- var yDir = vector.sub(this.centroids.yUp, this.centroids.yDown);
- this.dataSet.projectLinear(yDir, 'linear-y');
- var accessors = data.getProjectionComponents('custom', ['x', 'y']);
- var projection = new data_1.Projection('custom', accessors, 2, this.dataSet);
- this.projector.setProjection(projection);
- };
- ProjectionsPanel.prototype.clearCentroids = function () {
- this.centroids = { xLeft: null, xRight: null, yUp: null, yDown: null };
- this.allCentroid = null;
- };
- ProjectionsPanel.prototype._customSelectedSearchByMetadataOptionChanged = function (newVal, oldVal) {
- if (this.polymerChangesTriggerReprojection === false) {
- return;
- }
- if (this.currentProjection === 'custom') {
- this.computeAllCentroids();
- this.reprojectCustom();
- }
- };
- ProjectionsPanel.prototype.setupCustomProjectionInputFields = function () {
- this.customProjectionXLeftInput =
- this.setupCustomProjectionInputField('xLeft');
- this.customProjectionXRightInput =
- this.setupCustomProjectionInputField('xRight');
- this.customProjectionYUpInput = this.setupCustomProjectionInputField('yUp');
- this.customProjectionYDownInput =
- this.setupCustomProjectionInputField('yDown');
- };
- ProjectionsPanel.prototype.computeAllCentroids = function () {
- this.computeCentroid('xLeft');
- this.computeCentroid('xRight');
- this.computeCentroid('yUp');
- this.computeCentroid('yDown');
- };
- ProjectionsPanel.prototype.computeCentroid = function (name) {
- var input = this.querySelector('#' + name);
- if (input == null) {
- return;
- }
- var value = input.getValue();
- if (value == null) {
- return;
- }
- var inRegexMode = input.getInRegexMode();
- var result = this.getCentroid(value, inRegexMode);
- if (result.numMatches === 0) {
- input.message = '0 matches. Using a random vector.';
- result.centroid = vector.rn(this.dim);
- }
- else {
- input.message = result.numMatches + " matches.";
- }
- this.centroids[name] = result.centroid;
- this.centroidValues[name] = value;
- };
- ProjectionsPanel.prototype.setupCustomProjectionInputField = function (name) {
- var _this = this;
- var input = this.querySelector('#' + name);
- input.registerInputChangedListener(function (input, inRegexMode) {
- if (_this.polymerChangesTriggerReprojection) {
- _this.computeCentroid(name);
- _this.reprojectCustom();
- }
- });
- return input;
- };
- ProjectionsPanel.prototype.getCentroid = function (pattern, inRegexMode) {
- var _this = this;
- if (pattern == null || pattern === '') {
- return { numMatches: 0 };
- }
- // Search by the original dataset since we often want to filter and project
- // only the nearest neighbors of A onto B-C where B and C are not nearest
- // neighbors of A.
- var accessor = function (i) { return _this.originalDataSet.points[i].vector; };
- var r = this.originalDataSet.query(pattern, inRegexMode, this.customSelectedSearchByMetadataOption);
- return { centroid: vector.centroid(r, accessor), numMatches: r.length };
- };
- ProjectionsPanel.prototype.getPcaSampledDimText = function () {
- return data.PCA_SAMPLE_DIM.toLocaleString();
- };
- ProjectionsPanel.prototype.getPcaSampleSizeText = function () {
- return data.PCA_SAMPLE_SIZE.toLocaleString();
- };
- ProjectionsPanel.prototype.getTsneSampleSizeText = function () {
- return data.TSNE_SAMPLE_SIZE.toLocaleString();
- };
- return ProjectionsPanel;
-}(exports.ProjectionsPanelPolymer));
-exports.ProjectionsPanel = ProjectionsPanel;
-document.registerElement(ProjectionsPanel.prototype.is, ProjectionsPanel);
-
-},{"./data":7,"./vector":25,"./vz-projector-util":33}],33:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-function PolymerElement(spec) {
- return Polymer.Class(spec);
-}
-exports.PolymerElement = PolymerElement;
-
-},{}],34:[function(require,module,exports){
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-"use strict";
-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 __());
-};
-var analyticsLogger_1 = require('./analyticsLogger');
-var data = require('./data');
-var data_1 = require('./data');
-var data_provider_demo_1 = require('./data-provider-demo');
-var data_provider_proto_1 = require('./data-provider-proto');
-var data_provider_server_1 = require('./data-provider-server');
-var logging = require('./logging');
-var projectorScatterPlotAdapter_1 = require('./projectorScatterPlotAdapter');
-var scatterPlot_1 = require('./scatterPlot');
-var util = require('./util');
-// tslint:disable-next-line:no-unused-variable
-var vz_projector_util_1 = require('./vz-projector-util');
-/**
- * The minimum number of dimensions the data should have to automatically
- * decide to normalize the data.
- */
-var THRESHOLD_DIM_NORMALIZE = 50;
-var POINT_COLOR_MISSING = 'black';
-exports.ProjectorPolymer = vz_projector_util_1.PolymerElement({
- is: 'vz-projector',
- properties: {
- routePrefix: String,
- dataProto: { type: String, observer: '_dataProtoChanged' },
- servingMode: String,
- projectorConfigJsonPath: String,
- pageViewLogging: Boolean,
- eventLogging: Boolean
- }
-});
-var INDEX_METADATA_FIELD = '__index__';
-var Projector = (function (_super) {
- __extends(Projector, _super);
- function Projector() {
- _super.apply(this, arguments);
- }
- Projector.prototype.ready = function () {
- this.dom = d3.select(this);
- logging.setDomContainer(this);
- this.analyticsLogger =
- new analyticsLogger_1.AnalyticsLogger(this.pageViewLogging, this.eventLogging);
- this.analyticsLogger.logPageView('embeddings');
- if (!util.hasWebGLSupport()) {
- this.analyticsLogger.logWebGLDisabled();
- logging.setErrorMessage('Your browser or device does not have WebGL enabled. Please enable ' +
- 'hardware acceleration, or use a browser that supports WebGL.');
- return;
- }
- this.selectionChangedListeners = [];
- this.hoverListeners = [];
- this.projectionChangedListeners = [];
- this.distanceMetricChangedListeners = [];
- this.selectedPointIndices = [];
- this.neighborsOfFirstPoint = [];
- this.dataPanel = this.$['data-panel'];
- this.inspectorPanel = this.$['inspector-panel'];
- this.inspectorPanel.initialize(this, this);
- this.projectionsPanel = this.$['projections-panel'];
- this.projectionsPanel.initialize(this);
- this.bookmarkPanel = this.$['bookmark-panel'];
- this.bookmarkPanel.initialize(this, this);
- this.metadataCard = this.$['metadata-card'];
- this.statusBar = this.dom.select('#status-bar');
- this.scopeSubtree(this.$$('#notification-dialog'), true);
- this.setupUIControls();
- this.initializeDataProvider();
- };
- Projector.prototype.setSelectedLabelOption = function (labelOption) {
- this.selectedLabelOption = labelOption;
- this.metadataCard.setLabelOption(this.selectedLabelOption);
- this.projectorScatterPlotAdapter.setLabelPointAccessor(labelOption);
- this.projectorScatterPlotAdapter.updateScatterPlotAttributes();
- this.projectorScatterPlotAdapter.render();
- };
- Projector.prototype.setSelectedColorOption = function (colorOption) {
- this.selectedColorOption = colorOption;
- this.projectorScatterPlotAdapter.setLegendPointColorer(this.getLegendPointColorer(colorOption));
- this.projectorScatterPlotAdapter.updateScatterPlotAttributes();
- this.projectorScatterPlotAdapter.render();
- };
- Projector.prototype.setNormalizeData = function (normalizeData) {
- this.normalizeData = normalizeData;
- this.setCurrentDataSet(this.originalDataSet.getSubset());
- };
- Projector.prototype.updateDataSet = function (ds, spriteAndMetadata, metadataFile) {
- this.dataSetFilterIndices = null;
- this.originalDataSet = ds;
- if (ds != null) {
- this.normalizeData =
- this.originalDataSet.dim[1] >= THRESHOLD_DIM_NORMALIZE;
- spriteAndMetadata = spriteAndMetadata || {};
- if (spriteAndMetadata.pointsInfo == null) {
- var _a = this.makeDefaultPointsInfoAndStats(ds.points), pointsInfo = _a[0], stats = _a[1];
- spriteAndMetadata.pointsInfo = pointsInfo;
- spriteAndMetadata.stats = stats;
- }
- var metadataMergeSucceeded = ds.mergeMetadata(spriteAndMetadata);
- if (!metadataMergeSucceeded) {
- return;
- }
- }
- if (this.projectorScatterPlotAdapter != null) {
- if (ds == null) {
- this.projectorScatterPlotAdapter.setLabelPointAccessor(null);
- this.setProjection(null);
- }
- else {
- this.projectorScatterPlotAdapter.updateScatterPlotPositions();
- this.projectorScatterPlotAdapter.updateScatterPlotAttributes();
- this.projectorScatterPlotAdapter.resize();
- this.projectorScatterPlotAdapter.render();
- }
- }
- if (ds != null) {
- this.dataPanel.setNormalizeData(this.normalizeData);
- this.setCurrentDataSet(ds.getSubset());
- this.projectorScatterPlotAdapter.setLabelPointAccessor(this.selectedLabelOption);
- this.inspectorPanel.datasetChanged();
- this.inspectorPanel.metadataChanged(spriteAndMetadata);
- this.projectionsPanel.metadataChanged(spriteAndMetadata);
- this.dataPanel.metadataChanged(spriteAndMetadata, metadataFile);
- // Set the container to a fixed height, otherwise in Colab the
- // height can grow indefinitely.
- var container = this.dom.select('#container');
- container.style('height', container.property('clientHeight') + 'px');
- }
- else {
- this.setCurrentDataSet(null);
- }
- };
- Projector.prototype.setSelectedTensor = function (run, tensorInfo) {
- this.bookmarkPanel.setSelectedTensor(run, tensorInfo, this.dataProvider);
- };
- /**
- * Registers a listener to be called any time the selected point set changes.
- */
- Projector.prototype.registerSelectionChangedListener = function (listener) {
- this.selectionChangedListeners.push(listener);
- };
- Projector.prototype.filterDataset = function (pointIndices) {
- var selectionSize = this.selectedPointIndices.length;
- if (this.dataSetBeforeFilter == null) {
- this.dataSetBeforeFilter = this.dataSet;
- }
- this.setCurrentDataSet(this.dataSet.getSubset(pointIndices));
- this.dataSetFilterIndices = pointIndices;
- this.projectorScatterPlotAdapter.updateScatterPlotPositions();
- this.projectorScatterPlotAdapter.updateScatterPlotAttributes();
- this.adjustSelectionAndHover(d3.range(selectionSize));
- };
- Projector.prototype.resetFilterDataset = function () {
- var _this = this;
- var originalPointIndices = this.selectedPointIndices.map(function (filteredIndex) { return _this.dataSet.points[filteredIndex].index; });
- this.setCurrentDataSet(this.dataSetBeforeFilter);
- if (this.projection != null) {
- this.projection.dataSet = this.dataSetBeforeFilter;
- }
- this.dataSetBeforeFilter = null;
- this.projectorScatterPlotAdapter.updateScatterPlotPositions();
- this.projectorScatterPlotAdapter.updateScatterPlotAttributes();
- this.dataSetFilterIndices = [];
- this.adjustSelectionAndHover(originalPointIndices);
- };
- /**
- * Used by clients to indicate that a selection has occurred.
- */
- Projector.prototype.notifySelectionChanged = function (newSelectedPointIndices) {
- var _this = this;
- this.selectedPointIndices = newSelectedPointIndices;
- var neighbors = [];
- if (newSelectedPointIndices.length === 1) {
- neighbors = this.dataSet.findNeighbors(newSelectedPointIndices[0], this.inspectorPanel.distFunc, this.inspectorPanel.numNN);
- this.metadataCard.updateMetadata(this.dataSet.points[newSelectedPointIndices[0]].metadata);
- }
- else {
- this.metadataCard.updateMetadata(null);
- }
- this.selectionChangedListeners.forEach(function (l) { return l(_this.selectedPointIndices, neighbors); });
- };
- /**
- * Registers a listener to be called any time the mouse hovers over a point.
- */
- Projector.prototype.registerHoverListener = function (listener) {
- this.hoverListeners.push(listener);
- };
- /**
- * Used by clients to indicate that a hover is occurring.
- */
- Projector.prototype.notifyHoverOverPoint = function (pointIndex) {
- this.hoverListeners.forEach(function (l) { return l(pointIndex); });
- };
- Projector.prototype.registerProjectionChangedListener = function (listener) {
- this.projectionChangedListeners.push(listener);
- };
- Projector.prototype.notifyProjectionChanged = function (projection) {
- this.projectionChangedListeners.forEach(function (l) { return l(projection); });
- };
- Projector.prototype.registerDistanceMetricChangedListener = function (l) {
- this.distanceMetricChangedListeners.push(l);
- };
- Projector.prototype.notifyDistanceMetricChanged = function (distMetric) {
- this.distanceMetricChangedListeners.forEach(function (l) { return l(distMetric); });
- };
- Projector.prototype._dataProtoChanged = function (dataProtoString) {
- var dataProto = dataProtoString ? JSON.parse(dataProtoString) : null;
- this.initializeDataProvider(dataProto);
- };
- Projector.prototype.makeDefaultPointsInfoAndStats = function (points) {
- var pointsInfo = [];
- points.forEach(function (p) {
- var pointInfo = {};
- pointInfo[INDEX_METADATA_FIELD] = p.index;
- pointsInfo.push(pointInfo);
- });
- var stats = [{
- name: INDEX_METADATA_FIELD,
- isNumeric: false,
- tooManyUniqueValues: true,
- min: 0,
- max: pointsInfo.length - 1
- }];
- return [pointsInfo, stats];
- };
- Projector.prototype.initializeDataProvider = function (dataProto) {
- if (this.servingMode === 'demo') {
- var projectorConfigUrl = void 0;
- // Only in demo mode do we allow the config being passed via URL.
- var urlParams = util.getURLParams(window.location.search);
- if ('config' in urlParams) {
- projectorConfigUrl = urlParams['config'];
- }
- else {
- projectorConfigUrl = this.projectorConfigJsonPath;
- }
- this.dataProvider = new data_provider_demo_1.DemoDataProvider(projectorConfigUrl);
- }
- else if (this.servingMode === 'server') {
- if (!this.routePrefix) {
- throw 'route-prefix is a required parameter';
- }
- this.dataProvider = new data_provider_server_1.ServerDataProvider(this.routePrefix);
- }
- else if (this.servingMode === 'proto' && dataProto != null) {
- this.dataProvider = new data_provider_proto_1.ProtoDataProvider(dataProto);
- }
- this.dataPanel.initialize(this, this.dataProvider);
- };
- Projector.prototype.getLegendPointColorer = function (colorOption) {
- var _this = this;
- if ((colorOption == null) || (colorOption.map == null)) {
- return null;
- }
- var colorer = function (ds, i) {
- var value = ds.points[i].metadata[_this.selectedColorOption.name];
- if (value == null) {
- return POINT_COLOR_MISSING;
- }
- return colorOption.map(value);
- };
- return colorer;
- };
- Projector.prototype.get3DLabelModeButton = function () {
- return this.querySelector('#labels3DMode');
- };
- Projector.prototype.get3DLabelMode = function () {
- var label3DModeButton = this.get3DLabelModeButton();
- return label3DModeButton.active;
- };
- Projector.prototype.adjustSelectionAndHover = function (selectedPointIndices, hoverIndex) {
- this.notifySelectionChanged(selectedPointIndices);
- this.notifyHoverOverPoint(hoverIndex);
- this.setMouseMode(scatterPlot_1.MouseMode.CAMERA_AND_CLICK_SELECT);
- };
- Projector.prototype.setMouseMode = function (mouseMode) {
- var selectModeButton = this.querySelector('#selectMode');
- selectModeButton.active = (mouseMode === scatterPlot_1.MouseMode.AREA_SELECT);
- this.projectorScatterPlotAdapter.scatterPlot.setMouseMode(mouseMode);
- };
- Projector.prototype.setCurrentDataSet = function (ds) {
- this.adjustSelectionAndHover([]);
- if (this.dataSet != null) {
- this.dataSet.stopTSNE();
- }
- if ((ds != null) && this.normalizeData) {
- ds.normalize();
- }
- this.dim = (ds == null) ? 0 : ds.dim[1];
- this.dom.select('span.numDataPoints').text((ds == null) ? '0' : ds.dim[0]);
- this.dom.select('span.dim').text((ds == null) ? '0' : ds.dim[1]);
- this.dataSet = ds;
- this.projectionsPanel.dataSetUpdated(this.dataSet, this.originalDataSet, this.dim);
- this.projectorScatterPlotAdapter.setDataSet(this.dataSet);
- this.projectorScatterPlotAdapter.scatterPlot
- .setCameraParametersForNextCameraCreation(null, true);
- };
- Projector.prototype.setupUIControls = function () {
- var _this = this;
- // View controls
- this.querySelector('#reset-zoom').addEventListener('click', function () {
- _this.projectorScatterPlotAdapter.scatterPlot.resetZoom();
- _this.projectorScatterPlotAdapter.scatterPlot.startOrbitAnimation();
- });
- var selectModeButton = this.querySelector('#selectMode');
- selectModeButton.addEventListener('click', function (event) {
- _this.setMouseMode(selectModeButton.active ? scatterPlot_1.MouseMode.AREA_SELECT :
- scatterPlot_1.MouseMode.CAMERA_AND_CLICK_SELECT);
- });
- var nightModeButton = this.querySelector('#nightDayMode');
- nightModeButton.addEventListener('click', function () {
- _this.projectorScatterPlotAdapter.scatterPlot.setDayNightMode(nightModeButton.active);
- });
- var labels3DModeButton = this.get3DLabelModeButton();
- labels3DModeButton.addEventListener('click', function () {
- _this.projectorScatterPlotAdapter.set3DLabelMode(_this.get3DLabelMode());
- });
- window.addEventListener('resize', function () {
- var container = _this.dom.select('#container');
- var parentHeight = container.node().parentNode.clientHeight;
- container.style('height', parentHeight + 'px');
- _this.projectorScatterPlotAdapter.resize();
- });
- {
- this.projectorScatterPlotAdapter = new projectorScatterPlotAdapter_1.ProjectorScatterPlotAdapter(this.getScatterContainer(), this);
- this.projectorScatterPlotAdapter.setLabelPointAccessor(this.selectedLabelOption);
- }
- this.projectorScatterPlotAdapter.scatterPlot.onCameraMove(function (cameraPosition, cameraTarget) {
- return _this.bookmarkPanel.clearStateSelection();
- });
- this.registerHoverListener(function (hoverIndex) { return _this.onHover(hoverIndex); });
- this.registerSelectionChangedListener(function (selectedPointIndices, neighborsOfFirstPoint) {
- return _this.onSelectionChanged(selectedPointIndices, neighborsOfFirstPoint);
- });
- };
- Projector.prototype.onHover = function (hoverIndex) {
- this.hoverPointIndex = hoverIndex;
- var hoverText = null;
- if (hoverIndex != null) {
- var point = this.dataSet.points[hoverIndex];
- if (point.metadata[this.selectedLabelOption]) {
- hoverText = point.metadata[this.selectedLabelOption].toString();
- }
- }
- if (this.selectedPointIndices.length === 0) {
- this.statusBar.style('display', hoverText ? null : 'none');
- this.statusBar.text(hoverText);
- }
- };
- Projector.prototype.getScatterContainer = function () {
- return this.dom.select('#scatter');
- };
- Projector.prototype.onSelectionChanged = function (selectedPointIndices, neighborsOfFirstPoint) {
- this.selectedPointIndices = selectedPointIndices;
- this.neighborsOfFirstPoint = neighborsOfFirstPoint;
- var totalNumPoints = this.selectedPointIndices.length + neighborsOfFirstPoint.length;
- this.statusBar.text("Selected " + totalNumPoints + " points")
- .style('display', totalNumPoints > 0 ? null : 'none');
- };
- Projector.prototype.setProjection = function (projection) {
- this.projection = projection;
- if (projection != null) {
- this.analyticsLogger.logProjectionChanged(projection.projectionType);
- }
- this.notifyProjectionChanged(projection);
- };
- Projector.prototype.notifyProjectionPositionsUpdated = function () {
- this.projectorScatterPlotAdapter.notifyProjectionPositionsUpdated();
- };
- /**
- * Gets the current view of the embedding and saves it as a State object.
- */
- Projector.prototype.getCurrentState = function () {
- var state = new data_1.State();
- // Save the individual datapoint projections.
- state.projections = [];
- for (var i = 0; i < this.dataSet.points.length; i++) {
- var point = this.dataSet.points[i];
- var projections = {};
- var keys = Object.keys(point.projections);
- for (var j = 0; j < keys.length; ++j) {
- projections[keys[j]] = point.projections[keys[j]];
- }
- state.projections.push(projections);
- }
- state.selectedProjection = this.projection.projectionType;
- state.dataSetDimensions = this.dataSet.dim;
- state.tSNEIteration = this.dataSet.tSNEIteration;
- state.selectedPoints = this.selectedPointIndices;
- state.filteredPoints = this.dataSetFilterIndices;
- this.projectorScatterPlotAdapter.populateBookmarkFromUI(state);
- state.selectedColorOptionName = this.dataPanel.selectedColorOptionName;
- state.forceCategoricalColoring = this.dataPanel.forceCategoricalColoring;
- state.selectedLabelOption = this.selectedLabelOption;
- this.projectionsPanel.populateBookmarkFromUI(state);
- return state;
- };
- /** Loads a State object into the world. */
- Projector.prototype.loadState = function (state) {
- this.setProjection(null);
- {
- this.projectionsPanel.disablePolymerChangesTriggerReprojection();
- if (this.dataSetBeforeFilter != null) {
- this.resetFilterDataset();
- }
- if (state.filteredPoints != null) {
- this.filterDataset(state.filteredPoints);
- }
- this.projectionsPanel.enablePolymerChangesTriggerReprojection();
- }
- for (var i = 0; i < state.projections.length; i++) {
- var point = this.dataSet.points[i];
- var projection = state.projections[i];
- var keys = Object.keys(projection);
- for (var j = 0; j < keys.length; ++j) {
- point.projections[keys[j]] = projection[keys[j]];
- }
- }
- this.dataSet.hasTSNERun = (state.selectedProjection === 'tsne');
- this.dataSet.tSNEIteration = state.tSNEIteration;
- this.projectionsPanel.restoreUIFromBookmark(state);
- this.inspectorPanel.restoreUIFromBookmark(state);
- this.dataPanel.selectedColorOptionName = state.selectedColorOptionName;
- this.dataPanel.setForceCategoricalColoring(!!state.forceCategoricalColoring);
- this.selectedLabelOption = state.selectedLabelOption;
- this.projectorScatterPlotAdapter.restoreUIFromBookmark(state);
- {
- var dimensions = data_1.stateGetAccessorDimensions(state);
- var components = data.getProjectionComponents(state.selectedProjection, dimensions);
- var projection = new data_1.Projection(state.selectedProjection, components, dimensions.length, this.dataSet);
- this.setProjection(projection);
- }
- this.notifySelectionChanged(state.selectedPoints);
- };
- return Projector;
-}(exports.ProjectorPolymer));
-exports.Projector = Projector;
-document.registerElement(Projector.prototype.is, Projector);
-
-},{"./analyticsLogger":1,"./data":7,"./data-provider-demo":3,"./data-provider-proto":4,"./data-provider-server":5,"./logging":12,"./projectorScatterPlotAdapter":14,"./scatterPlot":16,"./util":24,"./vz-projector-util":33}],35:[function(require,module,exports){
-arguments[4][8][0].apply(exports,arguments)
-},{"dup":8}]},{},[35,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34]);
-</script>
-</dom-module>
-</body></html>
diff --git a/tensorflow/tensorboard/gulp_tasks/bower.js b/tensorflow/tensorboard/gulp_tasks/bower.js
deleted file mode 100644
index 8f4666a8c1..0000000000
--- a/tensorflow/tensorboard/gulp_tasks/bower.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-const gulp = require('gulp');
-const bower = require('gulp-bower');
-
-module.exports = function() {
- return function() {
- return bower();
- }
-}
diff --git a/tensorflow/tensorboard/gulp_tasks/compile.js b/tensorflow/tensorboard/gulp_tasks/compile.js
deleted file mode 100644
index 01af60eba7..0000000000
--- a/tensorflow/tensorboard/gulp_tasks/compile.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-const gulp = require('gulp');
-const ts = require('gulp-typescript');
-const typescript = require('typescript');
-const gutil = require('gulp-util');
-const filter = require('gulp-filter');
-const merge = require('merge2');
-const browserify = require('browserify');
-const tsify = require('tsify');
-const source = require('vinyl-source-stream');
-const glob = require('glob').sync;
-const concat = require('gulp-concat');
-
-const tsProject = ts.createProject('./tsconfig.json', {
- typescript: typescript,
- noExternalResolve: true, // opt-in for faster compilation!
-});
-
-/** List of components (and their external deps) that are using es6 modules. */
-const ES6_COMPONENTS = [{
- name: 'vz_projector',
- deps: [
- 'd3/d3.min.js', 'weblas/dist/weblas.js', 'three.js/build/three.min.js',
- 'three.js/examples/js/controls/OrbitControls.js',
- 'numericjs/lib/numeric-1.2.6.js'
- ]
-}];
-
-module.exports = function(includeDeps) {
- return function() {
- // Compile all components that are using ES6 modules into a bundle.js
- // using browserify.
- const entries = ['typings/index.d.ts'];
- const deps = {};
- ES6_COMPONENTS.forEach(function(component) {
- // Collect all the typescript files across the components.
- entries = entries.concat(glob(
- 'components/' + component.name + '/**/*.ts',
- // Do not include tests or IDE-purposed files.
- {ignore: ['**/*_test.ts', '**/deps.d.ts']}));
- // Collect the unique external deps across all components using es6
- // modules.
- component.deps.forEach(function(dep) {
- deps['components/' + dep] = true;
- });
- });
- deps = Object.keys(deps);
-
- // Compile, bundle all the typescript files and prepend their deps.
- browserify(entries)
- .plugin(tsify)
- .bundle()
- .on('error', function(error) { console.error(error.toString()); })
- .pipe(source('bundle.js'))
- .pipe(gulp.dest('components'))
- .on('end', function() {
- // Typescript was compiled and bundled. Now we need to prepend
- // the external dependencies.
- if (includeDeps) {
- gulp.src(deps.concat(['components/bundle.js']))
- .pipe(concat('bundle.js'))
- .pipe(gulp.dest('components'));
- }
- });
-
- // Compile components that are using global namespaces producing 1 js file
- // for each ts file.
- const isComponent = filter([
- 'components/tf_*/**/*.ts', 'components/vz_*/**/*.ts', 'typings/**/*.ts',
- 'components/plottable/plottable.d.ts'
- // Ignore components that use es6 modules.
- ].concat(ES6_COMPONENTS.map(function(component) {
- return '!components/' + component.name + '/**/*.ts';
- })));
-
- return tsProject.src()
- .pipe(isComponent)
- .pipe(ts(tsProject))
- .js.pipe(gulp.dest('.'));
- };
-};
diff --git a/tensorflow/tensorboard/gulp_tasks/test.js b/tensorflow/tensorboard/gulp_tasks/test.js
deleted file mode 100644
index 0c8b14a4cd..0000000000
--- a/tensorflow/tensorboard/gulp_tasks/test.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-const gulp = require('gulp');
-const tester = require('web-component-tester').test;
-
-module.exports = function(done) {
- tester({}, function(error) {
- if (error) {
- // Pretty error for gulp.
- error = new Error(error.message || error);
- error.showStack = false;
- }
- done(error);
- });
-}
diff --git a/tensorflow/tensorboard/gulp_tasks/util.js b/tensorflow/tensorboard/gulp_tasks/util.js
deleted file mode 100644
index 0d73f69c73..0000000000
--- a/tensorflow/tensorboard/gulp_tasks/util.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-const fs = require('fs');
-const path = require('path');
-
-/**
- * Returns a list of web components inside the components directory for which
- * the name predicate is true.
- */
-exports.getComponents = function(namePredicate) {
- return fs.readdirSync('components')
- .filter(function(file) {
- return fs.statSync(path.join('components', file)).isDirectory() &&
- namePredicate(file);
- })
- .map(function(dir) { return '/' + dir + '/'; });
-};
-
-/**
- * Returns a list of tensorboard web components that are inside the components
- * directory.
- */
-exports.tbComponents = exports.getComponents(function(name) {
- const prefix = name.slice(0, 3);
- return prefix == 'tf_' || prefix == 'vz_';
-});
diff --git a/tensorflow/tensorboard/gulp_tasks/vulcanize.js b/tensorflow/tensorboard/gulp_tasks/vulcanize.js
deleted file mode 100644
index d2286f1d6c..0000000000
--- a/tensorflow/tensorboard/gulp_tasks/vulcanize.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-const gulp = require('gulp');
-const path = require('path');
-const util = require('./util');
-const vulcanize = require('gulp-vulcanize');
-const replace = require('gulp-replace');
-const rename = require('gulp-rename');
-const header = require('gulp-header');
-
-const HEADER_STR =
- '<!-- Copyright 2015 The TensorFlow Authors. All Rights Reserved.\n\
-\n\
-Licensed under the Apache License, Version 2.0 (the "License");\n\
-you may not use this file except in compliance with the License.\n\
-You may obtain a copy of the License at\n\
-\n\
- http://www.apache.org/licenses/LICENSE-2.0\n\
-\n\
-Unless required by applicable law or agreed to in writing, software\n\
-distributed under the License is distributed on an "AS IS" BASIS,\n\
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\
-See the License for the specific language governing permissions and\n\
-limitations under the License.\n\
-============================================================================\n\
-\n\
-This file is generated by `gulp` & `vulcanize`. Do not directly change it.\n\
-Instead, use `gulp regenerate` to create a new version with your changes.\n\
--->\n\n'
-
-const base = path.join(__dirname, '../components');
-// List of redirects of the form path1|path2 for every tensorboard component
-// in order to replace dashes with underscores.
-// E.g. .../tf-tensorboard|.../tf_tensorboard
-const redirects = util.tbComponents.map(function(dir) {
- return path.join(base, dir.replace(/_/g, '-')) + '|' + path.join(base, dir);
-});
-
-const nonTBComponents = util.getComponents(function(name) {
- const prefix = name.slice(0, 3);
- return prefix !== 'tf_' && prefix !== 'vz_';
-});
-
-// These manual additions are necessary. The task should not inline these
-// third-party javascript files. However, vulcanization still needs the HTML
-// files found within those directories. Upon adding new third-party javascript,
-// consider updating this list.
-nonTBComponents.push('/tf-imports/d3.js');
-nonTBComponents.push('/tf-imports/dagre.js');
-nonTBComponents.push('/tf-imports/graphlib.js');
-nonTBComponents.push('/tf-imports/lodash.js');
-nonTBComponents.push('/tf-imports/plottable.js');
-
-module.exports = function(overwrite) {
- return function() {
- const suffix = overwrite ? '' : '.OPENSOURCE';
- // Vulcanize TensorBoard without external libraries.
- gulp.src('components/tf_tensorboard/tf-tensorboard.html')
- .pipe(vulcanize({
- inlineScripts: true,
- inlineCss: true,
- stripComments: true,
- excludes: nonTBComponents,
- redirects: redirects
- }))
- .pipe(header(HEADER_STR))
- .pipe(rename('tf-tensorboard.html' + suffix))
- .pipe(gulp.dest('./dist'));
- }
-}
diff --git a/tensorflow/tensorboard/gulpfile.js b/tensorflow/tensorboard/gulpfile.js
deleted file mode 100644
index c03c4faebc..0000000000
--- a/tensorflow/tensorboard/gulpfile.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* Copyright 2015 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-const gulp = require('gulp');
-const server = require('gulp-server-livereload');
-const minimist = require('minimist');
-const util = require('./gulp_tasks/util');
-
-const options = minimist(process.argv.slice(2), {
- default: {
- p: 8000, // port for gulp server
- h: '0.0.0.0', // host to serve on
- }
-});
-
-function getTask(task) {
- return require('./gulp_tasks/' + task);
-}
-
-
-gulp.task('compile', getTask('compile')(true));
-gulp.task('first-compile', getTask('compile')(true));
-gulp.task('compile-without-deps', getTask('compile')(false));
-gulp.task('test.onlytest', getTask('test'));
-gulp.task('test', ['compile'], getTask('test'));
-
-gulp.task('watch', [], function() {
- // Avoid watching generated .d.ts in the build (aka output) directory.
- return gulp.watch(
- ['components/tf_*/**/*.ts', 'components/vz_*/**/*.ts'],
- {ignoreInitial: true}, ['compile']);
-});
-
-const httpPrefix = 'http://' + options.h + ':' + options.p + '/components';
-const proxies = util.tbComponents.map(function(component) {
- return {
- source: '/components' + component.replace(/_/g, '-'),
- target: httpPrefix + component
- };
-});
-
-// Do first-compile before turning on server, to avoid spamming
-// livereload info
-// TODO(danmane): Disconnect this once we can get livereload to
-// no longer spam.
-gulp.task('server', ['first-compile'], function() {
- gulp.src('.').pipe(server({
- host: options.h,
- port: options.p,
- livereload: {
- enable: true,
- // Don't livereload on .ts changes, since they aren't loaded by browser.
- filter: function(filePath, cb) { cb(!(/\.ts$/.test(filePath))); },
- port: 27729 + options.p
- },
- proxies: proxies,
- directoryListing: true,
- }));
-});
-
-// TODO(danmane): When testing is nicer, integrate into vulcanize task
-// gulp vulcanize: Regenerate the tf-tensorboard.html.OPENSOURCE file for pre-release
-gulp.task(
- 'vulcanize', ['compile-without-deps'],
- getTask('vulcanize')(false));
-// gulp regenerate: Regenerate the tf-tensorboard.html for interactive bazel development
-gulp.task(
- 'regenerate', ['compile-without-deps'],
- getTask('vulcanize')(true));
-
-// TODO(danmane): consider making bower install part of default task
-gulp.task('default', ['watch', 'server']);
-
-// Clean all compiled JS files.
-const cleanCompiledTypeScript = require('gulp-clean-compiled-typescript');
-gulp.task('clean', function () {
- return gulp.src(['./components/**/*.ts', '!./components/**/deps.d.ts'])
- .pipe(cleanCompiledTypeScript());
-});
diff --git a/tensorflow/tensorboard/java/org/tensorflow/tensorboard/vulcanize/BUILD b/tensorflow/tensorboard/java/org/tensorflow/tensorboard/vulcanize/BUILD
index 07fc3a70a7..de57d7ce2e 100644
--- a/tensorflow/tensorboard/java/org/tensorflow/tensorboard/vulcanize/BUILD
+++ b/tensorflow/tensorboard/java/org/tensorflow/tensorboard/vulcanize/BUILD
@@ -5,6 +5,7 @@ licenses(["notice"]) # Apache 2.0
java_binary(
name = "Vulcanize",
srcs = ["Vulcanize.java"],
+ visibility = ["//visibility:public"],
deps = [
"@com_google_guava",
"@com_google_protobuf_java",
diff --git a/tensorflow/tensorboard/lib/BUILD b/tensorflow/tensorboard/lib/BUILD
deleted file mode 100644
index 9c497396c6..0000000000
--- a/tensorflow/tensorboard/lib/BUILD
+++ /dev/null
@@ -1,22 +0,0 @@
-# Description:
-# BUILD rules for the static resources in TensorBoard.
-
-package(default_visibility = [
- "//tensorflow:internal",
-])
-
-licenses(["notice"]) # Apache 2.0
-
-exports_files([
- "LICENSE",
-])
-
-filegroup(
- name = "all_files",
- srcs = glob(
- [
- "**/*",
- ],
- ),
- visibility = ["//tensorflow:__subpackages__"],
-)
diff --git a/tensorflow/tensorboard/lib/css/global.css b/tensorflow/tensorboard/lib/css/global.css
deleted file mode 100644
index cb6e966fdd..0000000000
--- a/tensorflow/tensorboard/lib/css/global.css
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright 2016 The TensorFlow Authors. 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.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-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.
-==============================================================================*/
-
-html, body {
- margin: 0;
- padding: 0;
- height: 100%;
- font-family: "RobotoDraft","Roboto",sans-serif;
-}
diff --git a/tensorflow/tensorboard/package.json b/tensorflow/tensorboard/package.json
deleted file mode 100644
index d424f103dd..0000000000
--- a/tensorflow/tensorboard/package.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "name": "tensorflow-vis",
- "version": "0.0.0",
- "description": "Visualizers for TensorFlow",
- "scripts": {
- "test": "gulp test",
- "prep": "npm install && bower install && typings install",
- "compile": "gulp compile"
- },
- "keywords": [
- "tensorflow"
- ],
- "author": "Google",
- "license": "Apache-2.0",
- "devDependencies": {
- "browserify": "^13.1.0",
- "gulp": "~3.9.0",
- "gulp-bower": "0.0.13",
- "gulp-clean-compiled-typescript": "~1.0.1",
- "gulp-cli": "^1.1.0",
- "gulp-concat": "^2.6.0",
- "gulp-filter": "~3.0.1",
- "gulp-header": "~1.7.1",
- "gulp-rename": "~1.2.2",
- "gulp-replace": "~0.5.4",
- "gulp-server-livereload": "1.9.2",
- "gulp-typescript": "~2.10.0",
- "gulp-util": "~3.0.7",
- "gulp-vulcanize": "~6.1.0",
- "merge2": "~0.3.6",
- "minimist": "~1.2.0",
- "tsify": "^0.14.8",
- "typescript": "2.3.1",
- "typings": "1.4.0",
- "vinyl-source-stream": "^1.1.0",
- "vulcanize": "^1.14.0",
- "web-component-tester": "4.2.2"
- }
-}
diff --git a/tensorflow/tensorboard/tsconfig.json b/tensorflow/tensorboard/tsconfig.json
deleted file mode 100644
index ac69c30533..0000000000
--- a/tensorflow/tensorboard/tsconfig.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "compilerOptions": {
- "noImplicitAny": false,
- "noEmitOnError": true,
- "target": "ES5",
- "module": "commonjs"
- },
- "compileOnSave": false,
- "exclude": [
- "node_modules",
- "typings/main.d.ts",
- "typings/main",
- "lib",
- "components/**/deps.d.ts"
- ]
-}
diff --git a/tensorflow/tensorboard/wct.conf.json b/tensorflow/tensorboard/wct.conf.json
deleted file mode 100644
index 519218ce41..0000000000
--- a/tensorflow/tensorboard/wct.conf.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "suites": [
- "components/tf_*/test",
- "components/vz_*/test"
- ],
- "plugins": ["local"]
-}
diff --git a/tensorflow/tools/pip_package/BUILD b/tensorflow/tools/pip_package/BUILD
index 377a687c34..c44b966451 100644
--- a/tensorflow/tools/pip_package/BUILD
+++ b/tensorflow/tools/pip_package/BUILD
@@ -96,6 +96,7 @@ filegroup(
"//third_party/hadoop:LICENSE.txt",
"@boringssl//:LICENSE",
"@com_googlesource_code_re2//:LICENSE",
+ "@com_microsoft_typescript//:LICENSE.txt",
"@curl//:COPYING",
"@eigen_archive//:COPYING.MPL2",
"@farmhash_archive//:COPYING",
@@ -109,13 +110,14 @@ filegroup(
"@libxsmm_archive//:LICENSE",
"@local_config_sycl//sycl:LICENSE.text",
"@nanopb_git//:LICENSE.txt",
- "@png_archive//:LICENSE",
- "@protobuf//:LICENSE",
- "@six_archive//:LICENSE",
"@org_html5lib//:LICENSE",
"@org_mozilla_bleach//:LICENSE",
+ "@org_nodejs//:LICENSE",
"@org_pocoo_werkzeug//:LICENSE",
"@org_pythonhosted_markdown//:LICENSE.md",
+ "@png_archive//:LICENSE",
+ "@protobuf//:LICENSE",
+ "@six_archive//:LICENSE",
"@snappy//:COPYING",
"@zlib_archive//:zlib.h",
] + if_not_windows([
diff --git a/tensorflow/tools/pip_package/setup.py b/tensorflow/tools/pip_package/setup.py
index f8982073c3..ae6516db89 100644
--- a/tensorflow/tools/pip_package/setup.py
+++ b/tensorflow/tools/pip_package/setup.py
@@ -190,10 +190,7 @@ setup(
package_data={
'tensorflow': [
EXTENSION_NAME,
- 'tensorboard/dist/bazel-html-imports.html',
- 'tensorboard/dist/index.html',
- 'tensorboard/dist/tf-tensorboard.html',
- 'tensorboard/lib/css/global.css',
+ 'tensorboard/components/index.html',
'tensorboard/TAG',
] + matches,
},
diff --git a/tensorflow/workspace.bzl b/tensorflow/workspace.bzl
index 2a206b0acd..f41dffefec 100644
--- a/tensorflow/workspace.bzl
+++ b/tensorflow/workspace.bzl
@@ -669,6 +669,10 @@ def tf_workspace(path_prefix="", tf_repo_name=""):
],
},
sha256_urls_windows = {
+ "3d4cfca9dcec556a077a2324bf5bd165ea3e6e64a2bfd7fc6e7a1f0dc4eb552b": [
+ "http://mirror.bazel.build/raw.githubusercontent.com/nodejs/node/v4.3.2/LICENSE",
+ "https://raw.githubusercontent.com/nodejs/node/v4.3.2/LICENSE",
+ ],
"606c44c42d17866c017c50c0afadad411d9492ac4281d2431b937f881911614e": [
"http://mirror.bazel.build/nodejs.org/dist/v4.3.2/win-x64/node.exe",
"http://nodejs.org/dist/v4.3.2/win-x64/node.exe",
@@ -698,6 +702,10 @@ def tf_workspace(path_prefix="", tf_repo_name=""):
name = "com_microsoft_typescript",
licenses = ["notice"], # Apache 2.0
sha256_urls = {
+ "a7d00bfd54525bc694b6e32f64c7ebcf5e6b7ae3657be5cc12767bce74654a47": [
+ "http://mirror.bazel.build/raw.githubusercontent.com/Microsoft/TypeScript/v2.3.1/LICENSE.txt",
+ "https://raw.githubusercontent.com/Microsoft/TypeScript/v2.3.1/LICENSE.txt",
+ ],
"8465342c318f9c4cf0a29b109fa63ee3742dd4dc7080d05d9fd8f604814d04cf": [
"http://mirror.bazel.build/raw.githubusercontent.com/Microsoft/TypeScript/v2.3.1/lib/tsc.js",
"https://raw.githubusercontent.com/Microsoft/TypeScript/v2.3.1/lib/tsc.js",
@@ -754,6 +762,10 @@ def tf_workspace(path_prefix="", tf_repo_name=""):
# no @license header
licenses = ["notice"], # MIT
sha256_urls = {
+ "0e94aada97f12dee6118064add9170484c55022f5d53206ee4407143cd36ddcd": [
+ "http://mirror.bazel.build/raw.githubusercontent.com/sloisel/numeric/v1.2.6/license.txt",
+ "https://raw.githubusercontent.com/sloisel/numeric/v1.2.6/license.txt",
+ ],
"dfaca3b8485bee735788cc6eebca82ea25719adc1fb8911c7799c6bd5a95df3b": [
"http://mirror.bazel.build/raw.githubusercontent.com/sloisel/numeric/v1.2.6/src/numeric.js",
"https://raw.githubusercontent.com/sloisel/numeric/v1.2.6/src/numeric.js",
@@ -802,6 +814,10 @@ def tf_workspace(path_prefix="", tf_repo_name=""):
# no @license header
licenses = ["notice"], # MIT
sha256_urls = {
+ "6a349742a6cb219d5a2fc8d0844f6d89a6efc62e20c664450d884fc7ff2d6015": [
+ "http://mirror.bazel.build/raw.githubusercontent.com/cpettitt/dagre/v0.7.4/LICENSE",
+ "https://raw.githubusercontent.com/cpettitt/dagre/v0.7.4/LICENSE",
+ ],
"7323829ddd77924a69e2b1235ded3eac30acd990da0f037e0fbd3c8e9035b50d": [
"http://mirror.bazel.build/raw.githubusercontent.com/cpettitt/dagre/v0.7.4/dist/dagre.core.js",
"https://raw.githubusercontent.com/cpettitt/dagre/v0.7.4/dist/dagre.core.js",
@@ -813,6 +829,10 @@ def tf_workspace(path_prefix="", tf_repo_name=""):
name = "io_github_cpettitt_graphlib",
licenses = ["notice"], # MIT
sha256_urls = {
+ "6a349742a6cb219d5a2fc8d0844f6d89a6efc62e20c664450d884fc7ff2d6015": [
+ "http://mirror.bazel.build/raw.githubusercontent.com/cpettitt/graphlib/v1.0.7/LICENSE",
+ "https://raw.githubusercontent.com/cpettitt/graphlib/v1.0.7/LICENSE",
+ ],
"772045d412b1513b549be991c2e1846c38019429d43974efcae943fbe83489bf": [
"http://mirror.bazel.build/raw.githubusercontent.com/cpettitt/graphlib/v1.0.7/dist/graphlib.core.js",
"https://raw.githubusercontent.com/cpettitt/graphlib/v1.0.7/dist/graphlib.core.js",
@@ -825,6 +845,10 @@ def tf_workspace(path_prefix="", tf_repo_name=""):
# no @license header
licenses = ["notice"], # MIT
sha256_urls = {
+ "633f2861a9a862b9cd7967e841e14dd3527912f209d6563595774fa31e3d84cb": [
+ "http://mirror.bazel.build/raw.githubusercontent.com/waylonflinn/weblas/v0.9.0/LICENSES",
+ "https://raw.githubusercontent.com/waylonflinn/weblas/v0.9.0/LICENSE",
+ ],
"f138fce57f673ca8a633f4aee5ae5b6fcb6ad0de59069a42a74e996fd04d8fcc": [
"http://mirror.bazel.build/raw.githubusercontent.com/waylonflinn/weblas/v0.9.0/dist/weblas.js",
"https://raw.githubusercontent.com/waylonflinn/weblas/v0.9.0/dist/weblas.js",