treescope 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- treescope/__init__.py +171 -0
- treescope/_internal/__init__.py +13 -0
- treescope/_internal/api/__init__.py +13 -0
- treescope/_internal/api/array_autovisualizer.py +397 -0
- treescope/_internal/api/arrayviz.py +1034 -0
- treescope/_internal/api/autovisualize.py +128 -0
- treescope/_internal/api/default_renderer.py +209 -0
- treescope/_internal/api/ipython_integration.py +417 -0
- treescope/_internal/arrayviz_impl.py +708 -0
- treescope/_internal/docs_util.py +25 -0
- treescope/_internal/figures_impl.py +102 -0
- treescope/_internal/handlers/__init__.py +15 -0
- treescope/_internal/handlers/autovisualizer_hook.py +181 -0
- treescope/_internal/handlers/basic_types_handler.py +456 -0
- treescope/_internal/handlers/canonical_alias_postprocessor.py +124 -0
- treescope/_internal/handlers/custom_type_handlers.py +102 -0
- treescope/_internal/handlers/function_reflection_handlers.py +134 -0
- treescope/_internal/handlers/generic_pytree_handler.py +90 -0
- treescope/_internal/handlers/generic_repr_handler.py +117 -0
- treescope/_internal/handlers/repr_html_postprocessor.py +120 -0
- treescope/_internal/handlers/shared_value_postprocessor.py +351 -0
- treescope/_internal/html_encapsulation.py +385 -0
- treescope/_internal/html_escaping.py +50 -0
- treescope/_internal/js/arrayviz.js +1291 -0
- treescope/_internal/object_inspection.py +77 -0
- treescope/_internal/parts/__init__.py +22 -0
- treescope/_internal/parts/basic_parts.py +1620 -0
- treescope/_internal/parts/common_structures.py +280 -0
- treescope/_internal/parts/common_styles.py +573 -0
- treescope/_internal/parts/custom_dataclass_util.py +163 -0
- treescope/_internal/parts/embedded_iframe.py +174 -0
- treescope/_internal/parts/foldable_impl.py +460 -0
- treescope/_internal/parts/part_interface.py +379 -0
- treescope/canonical_aliases.py +557 -0
- treescope/context.py +174 -0
- treescope/dataclass_util.py +105 -0
- treescope/dtype_util.py +89 -0
- treescope/external/__init__.py +21 -0
- treescope/external/jax_support.py +606 -0
- treescope/external/numpy_support.py +314 -0
- treescope/external/torch_support.py +532 -0
- treescope/figures.py +262 -0
- treescope/formatting_util.py +110 -0
- treescope/handlers.py +66 -0
- treescope/layout_algorithms.py +244 -0
- treescope/lowering.py +444 -0
- treescope/ndarray_adapters.py +296 -0
- treescope/renderers.py +415 -0
- treescope/rendering_parts.py +115 -0
- treescope/repr_lib.py +308 -0
- treescope/type_registries.py +251 -0
- treescope-0.1.0.dist-info/LICENSE +202 -0
- treescope-0.1.0.dist-info/METADATA +148 -0
- treescope-0.1.0.dist-info/RECORD +55 -0
- treescope-0.1.0.dist-info/WHEEL +4 -0
treescope/__init__.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Copyright 2024 The Treescope Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Treescope: An interactive HTML pretty-printer and array visualizer.
|
|
16
|
+
|
|
17
|
+
You can configure treescope as the default IPython pretty-printer using ::
|
|
18
|
+
|
|
19
|
+
treescope.basic_interactive_setup()
|
|
20
|
+
|
|
21
|
+
or, for more control: ::
|
|
22
|
+
|
|
23
|
+
treescope.register_as_default()
|
|
24
|
+
treescope.register_autovisualize_magic()
|
|
25
|
+
treescope.active_autovisualizer.set_globally(
|
|
26
|
+
treescope.ArrayAutovisualizer()
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
You can also pretty-print individual values using `treescope.show` or
|
|
30
|
+
`treescope.display`.
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
# pylint: disable=g-importing-member,g-multiple-import,unused-import
|
|
35
|
+
|
|
36
|
+
import typing
|
|
37
|
+
|
|
38
|
+
from . import _internal
|
|
39
|
+
from . import canonical_aliases
|
|
40
|
+
from . import context
|
|
41
|
+
from . import dataclass_util
|
|
42
|
+
from . import figures
|
|
43
|
+
from . import formatting_util
|
|
44
|
+
from . import handlers
|
|
45
|
+
from . import lowering
|
|
46
|
+
from . import ndarray_adapters
|
|
47
|
+
from . import renderers
|
|
48
|
+
from . import rendering_parts
|
|
49
|
+
from . import repr_lib
|
|
50
|
+
from . import type_registries
|
|
51
|
+
|
|
52
|
+
from ._internal.api.array_autovisualizer import (
|
|
53
|
+
ArrayAutovisualizer,
|
|
54
|
+
)
|
|
55
|
+
from ._internal.api.arrayviz import (
|
|
56
|
+
default_diverging_colormap,
|
|
57
|
+
default_sequential_colormap,
|
|
58
|
+
integer_digitbox,
|
|
59
|
+
render_array,
|
|
60
|
+
render_array_sharding,
|
|
61
|
+
)
|
|
62
|
+
from ._internal.api.autovisualize import (
|
|
63
|
+
IPythonVisualization,
|
|
64
|
+
VisualizationFromTreescopePart,
|
|
65
|
+
ChildAutovisualizer,
|
|
66
|
+
Autovisualizer,
|
|
67
|
+
active_autovisualizer,
|
|
68
|
+
)
|
|
69
|
+
from ._internal.api.default_renderer import (
|
|
70
|
+
active_renderer,
|
|
71
|
+
active_expansion_strategy,
|
|
72
|
+
render_to_html,
|
|
73
|
+
render_to_text,
|
|
74
|
+
using_expansion_strategy,
|
|
75
|
+
)
|
|
76
|
+
from ._internal.api.ipython_integration import (
|
|
77
|
+
default_magic_autovisualizer,
|
|
78
|
+
basic_interactive_setup,
|
|
79
|
+
display,
|
|
80
|
+
register_as_default,
|
|
81
|
+
register_autovisualize_magic,
|
|
82
|
+
register_context_manager_magic,
|
|
83
|
+
show,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if typing.TYPE_CHECKING:
|
|
87
|
+
Callable = typing.Callable
|
|
88
|
+
|
|
89
|
+
# Type annotations and docstrings for imported constants. Used by sphinx to
|
|
90
|
+
# generate documentation.
|
|
91
|
+
|
|
92
|
+
default_diverging_colormap: context.ContextualValue[
|
|
93
|
+
list[tuple[int, int, int]]
|
|
94
|
+
] = default_diverging_colormap
|
|
95
|
+
"""Default diverging colormap.
|
|
96
|
+
|
|
97
|
+
Used by `render_array` when ``around_zero`` is True. Intended for user
|
|
98
|
+
customization in an interactive setting.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
default_sequential_colormap: context.ContextualValue[
|
|
102
|
+
list[tuple[int, int, int]]
|
|
103
|
+
] = default_sequential_colormap
|
|
104
|
+
"""Default sequential colormap.
|
|
105
|
+
|
|
106
|
+
Used by `render_array` when ``around_zero`` is False. Intended for user
|
|
107
|
+
customization in an interactive setting.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
active_autovisualizer: context.ContextualValue[Autovisualizer | None] = (
|
|
111
|
+
active_autovisualizer
|
|
112
|
+
)
|
|
113
|
+
"""The active autovisualizer to use when rendering a tree to HTML.
|
|
114
|
+
|
|
115
|
+
This can be overridden interactively to enable rich visualizations in
|
|
116
|
+
treescope. Users are free to set this to an arbitrary renderer of their
|
|
117
|
+
choice; a common choice is arrayviz's `ArrayAutovisualizer()`.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
active_renderer: context.ContextualValue[renderers.TreescopeRenderer] = (
|
|
121
|
+
active_renderer
|
|
122
|
+
)
|
|
123
|
+
"""The default renderer to use when rendering a tree to HTML.
|
|
124
|
+
|
|
125
|
+
This determines the set of handlers and postprocessors to use when
|
|
126
|
+
rendering an object.
|
|
127
|
+
|
|
128
|
+
This can be overridden locally to reconfigure how nodes are rendered
|
|
129
|
+
with treescope. Users are free to set this to an arbitrary renderer of
|
|
130
|
+
their choice, and programs should not assume the renderer has a
|
|
131
|
+
particular form. Library functions can retrieve the current value of
|
|
132
|
+
this to render objects in a user-configurable way, and can optionally
|
|
133
|
+
make further adjustments using `TreescopeRenderer.extend_with`.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
active_expansion_strategy: context.ContextualValue[
|
|
137
|
+
Callable[[rendering_parts.RenderableTreePart], None]
|
|
138
|
+
] = active_expansion_strategy
|
|
139
|
+
"""The default expansion strategy to use when rendering a tree to HTML.
|
|
140
|
+
|
|
141
|
+
Expansion strategies are used to figure out how deeply to unfold an object
|
|
142
|
+
by default. They should operate by setting the expand states of the
|
|
143
|
+
foldable nodes inside the object.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
# Package version.
|
|
147
|
+
__version__ = '0.1.0'
|
|
148
|
+
|
|
149
|
+
default_magic_autovisualizer: context.ContextualValue[Autovisualizer] = (
|
|
150
|
+
default_magic_autovisualizer
|
|
151
|
+
)
|
|
152
|
+
"""The default autovisualizer to use for the ``%%autovisualize`` magic.
|
|
153
|
+
|
|
154
|
+
This can be overridden interactively to customize the autovisualizer
|
|
155
|
+
used by ``%%autovisualize``.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# Set up canonical aliases for the treescope API itself.
|
|
160
|
+
def _setup_canonical_aliases_for_api():
|
|
161
|
+
import types # pylint: disable=g-import-not-at-top
|
|
162
|
+
|
|
163
|
+
for key, value in globals().items():
|
|
164
|
+
if isinstance(value, types.FunctionType) or isinstance(value, type):
|
|
165
|
+
canonical_aliases.add_alias(
|
|
166
|
+
value, canonical_aliases.ModuleAttributePath(__name__, (key,))
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
_setup_canonical_aliases_for_api()
|
|
171
|
+
del _setup_canonical_aliases_for_api, typing
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2024 The Treescope Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2024 The Treescope Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Copyright 2024 The Treescope Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""An automatic NDArray visualizer using arrayviz."""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import dataclasses
|
|
19
|
+
import sys
|
|
20
|
+
from typing import Any, Callable, Collection
|
|
21
|
+
|
|
22
|
+
import numpy as np
|
|
23
|
+
from treescope import dtype_util
|
|
24
|
+
from treescope import lowering
|
|
25
|
+
from treescope import ndarray_adapters
|
|
26
|
+
from treescope import rendering_parts
|
|
27
|
+
from treescope import type_registries
|
|
28
|
+
from treescope._internal import arrayviz_impl
|
|
29
|
+
from treescope._internal.api import arrayviz
|
|
30
|
+
from treescope._internal.api import autovisualize
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
PositionalAxisInfo = ndarray_adapters.PositionalAxisInfo
|
|
34
|
+
NamedPositionlessAxisInfo = ndarray_adapters.NamedPositionlessAxisInfo
|
|
35
|
+
NamedPositionalAxisInfo = ndarray_adapters.NamedPositionalAxisInfo
|
|
36
|
+
AxisInfo = ndarray_adapters.AxisInfo
|
|
37
|
+
|
|
38
|
+
ArrayInRegistry = Any
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _supported_dtype(dtype: np.dtype | None):
|
|
42
|
+
return dtype is not None and (
|
|
43
|
+
dtype_util.is_integer_dtype(dtype)
|
|
44
|
+
or dtype_util.is_floating_dtype(dtype)
|
|
45
|
+
or np.issubdtype(dtype, np.bool_)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclasses.dataclass
|
|
50
|
+
class ArrayAutovisualizer:
|
|
51
|
+
"""An automatic visualizer for arrays.
|
|
52
|
+
|
|
53
|
+
ArrayAutovisualizer supports any array type registered with an NDArrayAdapter
|
|
54
|
+
in the global type registry.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
maximum_size: Maximum numer of elements of an array to show. Arrays larger
|
|
58
|
+
than this will be truncated along one or more axes.
|
|
59
|
+
cutoff_size_per_axis: Maximum number of elements of each individual axis to
|
|
60
|
+
show without truncation. Any axis longer than this will be truncated, with
|
|
61
|
+
their visual size increasing logarithmically with the true axis size
|
|
62
|
+
beyond this point.
|
|
63
|
+
edge_items: How many values to keep along each axis for truncated arrays.
|
|
64
|
+
prefers_column: Names that should always be assigned to columns.
|
|
65
|
+
prefers_row: Names that should always be assigned to rows.
|
|
66
|
+
around_zero: Whether to center continous data around zero.
|
|
67
|
+
force_continuous: Whether to always render integer arrays as continuous.
|
|
68
|
+
include_repr_line_threshold: A threshold such that, if the `repr` of the
|
|
69
|
+
array has fewer than that many lines, we will include that repr in the
|
|
70
|
+
visualization. Useful for seeing small array values.
|
|
71
|
+
token_lookup_fn: Optional function that looks up token IDs and adds them to
|
|
72
|
+
the visualization on hover.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
maximum_size: int = 4_000
|
|
76
|
+
cutoff_size_per_axis: int = 128
|
|
77
|
+
edge_items: int = 5
|
|
78
|
+
prefers_column: Collection[str] = ()
|
|
79
|
+
prefers_row: Collection[str] = ()
|
|
80
|
+
around_zero: bool = True
|
|
81
|
+
force_continuous: bool = False
|
|
82
|
+
include_repr_line_threshold: int = 5
|
|
83
|
+
token_lookup_fn: Callable[[int], str] | None = None
|
|
84
|
+
|
|
85
|
+
def _autovisualize_array(
|
|
86
|
+
self,
|
|
87
|
+
array: ArrayInRegistry,
|
|
88
|
+
adapter: ndarray_adapters.NDArrayAdapter,
|
|
89
|
+
path: str | None,
|
|
90
|
+
label: str,
|
|
91
|
+
expand_state: rendering_parts.ExpandState,
|
|
92
|
+
) -> rendering_parts.RenderableTreePart:
|
|
93
|
+
"""Helper to visualize an array."""
|
|
94
|
+
# Extract information about axis names, indices, and sizes.
|
|
95
|
+
array_axis_info = adapter.get_axis_info_for_array_data(array)
|
|
96
|
+
|
|
97
|
+
# Assign axes, using preferred axes if possible.
|
|
98
|
+
row_axes = []
|
|
99
|
+
column_axes = []
|
|
100
|
+
for info in array_axis_info:
|
|
101
|
+
if isinstance(info, NamedPositionalAxisInfo | NamedPositionlessAxisInfo):
|
|
102
|
+
if info.axis_name in self.prefers_column:
|
|
103
|
+
column_axes.append(info.axis_name)
|
|
104
|
+
elif info.axis_name in self.prefers_row:
|
|
105
|
+
row_axes.append(info.axis_name)
|
|
106
|
+
|
|
107
|
+
# Infer a good truncated shape for this array.
|
|
108
|
+
edge_items_per_axis = arrayviz_impl.infer_balanced_truncation(
|
|
109
|
+
tuple(info.size for info in array_axis_info),
|
|
110
|
+
maximum_size=self.maximum_size,
|
|
111
|
+
cutoff_size_per_axis=self.cutoff_size_per_axis,
|
|
112
|
+
minimum_edge_items=self.edge_items,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
row_axes, column_axes = arrayviz_impl.infer_rows_and_columns(
|
|
116
|
+
all_axes=array_axis_info,
|
|
117
|
+
known_rows=row_axes,
|
|
118
|
+
known_columns=column_axes,
|
|
119
|
+
edge_items_per_axis=edge_items_per_axis,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Obtain truncated array and mask data from the adapter.
|
|
123
|
+
truncated_array_data, truncated_mask_data = (
|
|
124
|
+
adapter.get_array_data_with_truncation(
|
|
125
|
+
array=array,
|
|
126
|
+
mask=None,
|
|
127
|
+
edge_items_per_axis=edge_items_per_axis,
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Maybe infer value labels from a tokenizer.
|
|
132
|
+
if (
|
|
133
|
+
self.token_lookup_fn
|
|
134
|
+
and not self.force_continuous
|
|
135
|
+
and dtype_util.is_integer_dtype(truncated_array_data.dtype)
|
|
136
|
+
):
|
|
137
|
+
tokens = np.unique(truncated_array_data.flatten()).tolist()
|
|
138
|
+
value_item_labels = {
|
|
139
|
+
token: self.token_lookup_fn(token) for token in tokens
|
|
140
|
+
}
|
|
141
|
+
else:
|
|
142
|
+
value_item_labels = None
|
|
143
|
+
|
|
144
|
+
array_rendering = arrayviz._render_pretruncated( # pylint: disable=protected-access
|
|
145
|
+
array_axis_info=array_axis_info,
|
|
146
|
+
row_infos=row_axes,
|
|
147
|
+
column_infos=column_axes,
|
|
148
|
+
slider_infos=(),
|
|
149
|
+
truncated_array_data=truncated_array_data,
|
|
150
|
+
truncated_mask_data=truncated_mask_data,
|
|
151
|
+
edge_items_per_axis=edge_items_per_axis,
|
|
152
|
+
continuous="auto",
|
|
153
|
+
around_zero=self.around_zero,
|
|
154
|
+
vmax=None,
|
|
155
|
+
vmin=None,
|
|
156
|
+
trim_outliers=True,
|
|
157
|
+
dynamic_colormap="auto",
|
|
158
|
+
colormap=None,
|
|
159
|
+
axis_item_labels=None,
|
|
160
|
+
value_item_labels=value_item_labels,
|
|
161
|
+
axis_labels=None,
|
|
162
|
+
)
|
|
163
|
+
outputs = [array_rendering]
|
|
164
|
+
last_line_parts = []
|
|
165
|
+
|
|
166
|
+
# Render the sharding as well.
|
|
167
|
+
sharding_info = adapter.get_sharding_info_for_array_data(array)
|
|
168
|
+
if sharding_info:
|
|
169
|
+
num_devices = len(sharding_info.device_index_to_shard_slices)
|
|
170
|
+
if num_devices == 1:
|
|
171
|
+
[device_id] = sharding_info.device_index_to_shard_slices.keys()
|
|
172
|
+
last_line_parts.append(
|
|
173
|
+
f"| Device: {sharding_info.device_type} {device_id}"
|
|
174
|
+
)
|
|
175
|
+
else:
|
|
176
|
+
if sharding_info.fully_replicated:
|
|
177
|
+
sharding_summary_str = (
|
|
178
|
+
"Replicated across"
|
|
179
|
+
f" {num_devices} {sharding_info.device_type} devices"
|
|
180
|
+
)
|
|
181
|
+
else:
|
|
182
|
+
sharding_summary_str = (
|
|
183
|
+
"Sharded across"
|
|
184
|
+
f" {num_devices} {sharding_info.device_type} devices"
|
|
185
|
+
)
|
|
186
|
+
sharding_rendering = arrayviz.render_array_sharding(
|
|
187
|
+
array,
|
|
188
|
+
columns=[c.logical_key() for c in column_axes],
|
|
189
|
+
rows=[r.logical_key() for r in row_axes],
|
|
190
|
+
).treescope_part
|
|
191
|
+
outputs.append(
|
|
192
|
+
rendering_parts.build_custom_foldable_tree_node(
|
|
193
|
+
label=rendering_parts.abbreviation_color(
|
|
194
|
+
rendering_parts.siblings(
|
|
195
|
+
rendering_parts.text(sharding_summary_str),
|
|
196
|
+
rendering_parts.fold_condition(
|
|
197
|
+
expanded=rendering_parts.text(":"),
|
|
198
|
+
collapsed=rendering_parts.text(
|
|
199
|
+
" (click to expand)"
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
)
|
|
203
|
+
),
|
|
204
|
+
contents=rendering_parts.fold_condition(
|
|
205
|
+
expanded=rendering_parts.indented_children(
|
|
206
|
+
[sharding_rendering]
|
|
207
|
+
),
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# We render it with a path, but remove the copy path button. This will be
|
|
213
|
+
# added back by the caller.
|
|
214
|
+
if last_line_parts:
|
|
215
|
+
last_line = rendering_parts.siblings(
|
|
216
|
+
rendering_parts.fold_condition(
|
|
217
|
+
expanded=rendering_parts.text("".join(last_line_parts)),
|
|
218
|
+
),
|
|
219
|
+
rendering_parts.text(">"),
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
last_line = rendering_parts.text(">")
|
|
223
|
+
custom_rendering = rendering_parts.build_custom_foldable_tree_node(
|
|
224
|
+
label=rendering_parts.abbreviation_color(label),
|
|
225
|
+
contents=rendering_parts.siblings(
|
|
226
|
+
rendering_parts.fold_condition(
|
|
227
|
+
expanded=rendering_parts.indented_children(outputs)
|
|
228
|
+
),
|
|
229
|
+
rendering_parts.abbreviation_color(last_line),
|
|
230
|
+
),
|
|
231
|
+
path=path,
|
|
232
|
+
expand_state=expand_state,
|
|
233
|
+
)
|
|
234
|
+
return custom_rendering.renderable
|
|
235
|
+
|
|
236
|
+
def __call__(
|
|
237
|
+
self, value: Any, path: str | None
|
|
238
|
+
) -> autovisualize.VisualizationFromTreescopePart | None:
|
|
239
|
+
"""Implementation of an autovisualizer, visualizing arrays."""
|
|
240
|
+
# Retrieve the adapter for this array, which we will use to construct
|
|
241
|
+
# the rendering.
|
|
242
|
+
adapter = type_registries.lookup_ndarray_adapter(value)
|
|
243
|
+
if adapter is not None:
|
|
244
|
+
if not adapter.should_autovisualize(value):
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
# This is an array we can visualize!
|
|
248
|
+
# Extract information about axis names, indices, and sizes.
|
|
249
|
+
array_axis_info = adapter.get_axis_info_for_array_data(value)
|
|
250
|
+
total_size = np.prod([ax.size for ax in array_axis_info])
|
|
251
|
+
if total_size == 1:
|
|
252
|
+
# Don't visualize scalars.
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
np_dtype = adapter.get_numpy_dtype(value)
|
|
256
|
+
if not _supported_dtype(np_dtype):
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
def _placeholder() -> rendering_parts.RenderableTreePart:
|
|
260
|
+
summary = adapter.get_array_summary(value, fast=True)
|
|
261
|
+
return rendering_parts.deferred_placeholder_style(
|
|
262
|
+
rendering_parts.text(f"<{summary}>")
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def _thunk(
|
|
266
|
+
expand_state: rendering_parts.ExpandState | None,
|
|
267
|
+
) -> rendering_parts.RenderableTreePart:
|
|
268
|
+
# Full rendering of the array.
|
|
269
|
+
if expand_state is None:
|
|
270
|
+
expand_state = rendering_parts.ExpandState.WEAKLY_EXPANDED
|
|
271
|
+
summary = adapter.get_array_summary(value, fast=False)
|
|
272
|
+
label = rendering_parts.abbreviation_color(
|
|
273
|
+
rendering_parts.text(f"<{summary}")
|
|
274
|
+
)
|
|
275
|
+
return self._autovisualize_array(
|
|
276
|
+
value, adapter, path, label, expand_state
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return autovisualize.VisualizationFromTreescopePart(
|
|
280
|
+
rendering_parts.RenderableAndLineAnnotations(
|
|
281
|
+
renderable=lowering.maybe_defer_rendering(
|
|
282
|
+
_thunk, _placeholder, expanded_newlines_for_layout=8
|
|
283
|
+
),
|
|
284
|
+
annotations=rendering_parts.build_copy_button(path),
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Not an array in the registry. But it might be a JAX sharding that we can
|
|
289
|
+
# visualize (if JAX is imported).
|
|
290
|
+
if "jax" in sys.modules:
|
|
291
|
+
import jax # pylint: disable=g-import-not-at-top
|
|
292
|
+
|
|
293
|
+
if isinstance(
|
|
294
|
+
value,
|
|
295
|
+
jax.sharding.PositionalSharding
|
|
296
|
+
| jax.sharding.NamedSharding
|
|
297
|
+
| jax.sharding.Mesh,
|
|
298
|
+
):
|
|
299
|
+
raw_repr = repr(value)
|
|
300
|
+
repr_oneline = " ".join(line.strip() for line in raw_repr.split("\n"))
|
|
301
|
+
|
|
302
|
+
if isinstance(value, jax.sharding.PositionalSharding):
|
|
303
|
+
sharding = value
|
|
304
|
+
fake_axis_info = [
|
|
305
|
+
PositionalAxisInfo(i, size)
|
|
306
|
+
for i, size in enumerate(sharding.shape)
|
|
307
|
+
]
|
|
308
|
+
elif isinstance(value, jax.sharding.NamedSharding):
|
|
309
|
+
sharding = value
|
|
310
|
+
# Named shardings still act on positional arrays, so show them for
|
|
311
|
+
# the positional shape they require.
|
|
312
|
+
fake_sizes = []
|
|
313
|
+
for part in value.spec:
|
|
314
|
+
if part is None:
|
|
315
|
+
fake_sizes.append(1)
|
|
316
|
+
elif isinstance(part, str):
|
|
317
|
+
fake_sizes.append(value.mesh.shape[part])
|
|
318
|
+
else:
|
|
319
|
+
size = int(np.prod([value.mesh.shape[a] for a in part]))
|
|
320
|
+
fake_sizes.append(size)
|
|
321
|
+
fake_axis_info = [
|
|
322
|
+
PositionalAxisInfo(i, size) for i, size in enumerate(fake_sizes)
|
|
323
|
+
]
|
|
324
|
+
else:
|
|
325
|
+
# Meshes are based on named axes. We build a temporary positional
|
|
326
|
+
# sharding for visualization, but keep track of name order.
|
|
327
|
+
assert isinstance(value, jax.sharding.Mesh)
|
|
328
|
+
mesh = value
|
|
329
|
+
sharding = jax.sharding.NamedSharding(
|
|
330
|
+
value, jax.sharding.PartitionSpec(*mesh.axis_names)
|
|
331
|
+
)
|
|
332
|
+
fake_axis_info = [
|
|
333
|
+
NamedPositionlessAxisInfo(name, mesh.shape[name])
|
|
334
|
+
for name in mesh.axis_names
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
fake_shape = tuple(ax.size for ax in fake_axis_info)
|
|
338
|
+
some_device = next(iter(sharding.device_set))
|
|
339
|
+
device_index_map = sharding.devices_indices_map(fake_shape)
|
|
340
|
+
sharding_info = ndarray_adapters.ShardingInfo(
|
|
341
|
+
shard_shape=sharding.shard_shape(fake_shape),
|
|
342
|
+
device_index_to_shard_slices={
|
|
343
|
+
d.id: v for d, v in device_index_map.items()
|
|
344
|
+
},
|
|
345
|
+
device_type=some_device.platform.upper(),
|
|
346
|
+
fully_replicated=sharding.is_fully_replicated,
|
|
347
|
+
)
|
|
348
|
+
shardvis = arrayviz.render_sharding_info(
|
|
349
|
+
array_axis_info=fake_axis_info,
|
|
350
|
+
sharding_info=sharding_info,
|
|
351
|
+
).treescope_part
|
|
352
|
+
custom_rendering = rendering_parts.build_custom_foldable_tree_node(
|
|
353
|
+
label=rendering_parts.abbreviation_color(
|
|
354
|
+
rendering_parts.text("<" + repr_oneline)
|
|
355
|
+
),
|
|
356
|
+
contents=rendering_parts.siblings(
|
|
357
|
+
rendering_parts.fold_condition(
|
|
358
|
+
expanded=rendering_parts.indented_children([shardvis])
|
|
359
|
+
),
|
|
360
|
+
rendering_parts.abbreviation_color(rendering_parts.text(">")),
|
|
361
|
+
),
|
|
362
|
+
path=path,
|
|
363
|
+
expand_state=rendering_parts.ExpandState.EXPANDED,
|
|
364
|
+
)
|
|
365
|
+
return autovisualize.VisualizationFromTreescopePart(custom_rendering)
|
|
366
|
+
else:
|
|
367
|
+
return None
|
|
368
|
+
|
|
369
|
+
@classmethod
|
|
370
|
+
def for_tokenizer(cls, tokenizer: Any):
|
|
371
|
+
"""Builds an autovisualizer for a tokenizer.
|
|
372
|
+
|
|
373
|
+
This method constructs an ArrayAutovisualizer that annotates integer array
|
|
374
|
+
elements with their token strings. This can then be used to autovisualize
|
|
375
|
+
tokenized arrays.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
tokenizer: A tokenizer to use. Either a callable mapping token IDs to
|
|
379
|
+
strings, or a SentencePieceProcessor.
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
An ArrayAutovisualizer that annotates integer array elements with their
|
|
383
|
+
token strings.
|
|
384
|
+
"""
|
|
385
|
+
if callable(tokenizer):
|
|
386
|
+
return cls(token_lookup_fn=lambda x: repr(tokenizer(x)))
|
|
387
|
+
elif hasattr(tokenizer, "IdToPiece") and hasattr(tokenizer, "GetPieceSize"):
|
|
388
|
+
|
|
389
|
+
def lookup(x):
|
|
390
|
+
if x >= 0 and x < tokenizer.GetPieceSize():
|
|
391
|
+
return repr(tokenizer.IdToPiece(x))
|
|
392
|
+
else:
|
|
393
|
+
return f"<out of bounds: {x}>"
|
|
394
|
+
|
|
395
|
+
return cls(token_lookup_fn=lookup)
|
|
396
|
+
else:
|
|
397
|
+
raise ValueError(f"Unknown tokenizer type: {tokenizer}")
|