arrscope 0.2.0__tar.gz → 0.4.0__tar.gz

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.
Files changed (31) hide show
  1. arrscope-0.4.0/PKG-INFO +168 -0
  2. arrscope-0.4.0/README.md +141 -0
  3. arrscope-0.4.0/examples/renders.py +71 -0
  4. {arrscope-0.2.0 → arrscope-0.4.0}/main.py +7 -2
  5. {arrscope-0.2.0 → arrscope-0.4.0}/pyproject.toml +1 -1
  6. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_api.py +53 -87
  7. arrscope-0.4.0/src/arrscope/_config.py +10 -0
  8. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_format.py +12 -0
  9. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_layout.py +29 -3
  10. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/_types.py +21 -7
  11. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/renderers/html.py +85 -48
  12. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/renderers/terminal.py +93 -56
  13. {arrscope-0.2.0 → arrscope-0.4.0}/uv.lock +1 -1
  14. arrscope-0.2.0/PKG-INFO +0 -185
  15. arrscope-0.2.0/README.md +0 -158
  16. arrscope-0.2.0/src/arrscope/_config.py +0 -15
  17. {arrscope-0.2.0 → arrscope-0.4.0}/.gitignore +0 -0
  18. {arrscope-0.2.0 → arrscope-0.4.0}/docs/ADR-001-visual-grammar.md +0 -0
  19. {arrscope-0.2.0 → arrscope-0.4.0}/docs/ADR-002-architecture.md +0 -0
  20. {arrscope-0.2.0 → arrscope-0.4.0}/docs/ADR-003-api-design.md +0 -0
  21. {arrscope-0.2.0 → arrscope-0.4.0}/docs/GLOSSARY.md +0 -0
  22. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/__init__.py +0 -0
  23. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/__main__.py +0 -0
  24. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/adapters/__init__.py +0 -0
  25. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/adapters/_core.py +0 -0
  26. {arrscope-0.2.0 → arrscope-0.4.0}/src/arrscope/renderers/__init__.py +0 -0
  27. {arrscope-0.2.0 → arrscope-0.4.0}/test.ipynb +0 -0
  28. {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_adapter.py +0 -0
  29. {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_format.py +0 -0
  30. {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_layout.py +0 -0
  31. {arrscope-0.2.0 → arrscope-0.4.0}/tests/test_stats.py +0 -0
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.4
2
+ Name: arrscope
3
+ Version: 0.4.0
4
+ Summary: Beautiful n-dimensional array visualization for Python
5
+ Project-URL: Homepage, https://github.com/vizarray/arrscope
6
+ Project-URL: Repository, https://github.com/vizarray/arrscope
7
+ Author: arrscope contributors
8
+ License: MIT
9
+ Keywords: arrays,data-science,machine-learning,numpy,visualization
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Scientific/Engineering :: Visualization
17
+ Requires-Python: >=3.11
18
+ Requires-Dist: numpy>=1.24
19
+ Requires-Dist: rich>=13.0
20
+ Provides-Extra: jax
21
+ Requires-Dist: jax>=0.4; extra == 'jax'
22
+ Provides-Extra: tf
23
+ Requires-Dist: tensorflow>=2.12; extra == 'tf'
24
+ Provides-Extra: torch
25
+ Requires-Dist: torch>=2.0; extra == 'torch'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # arrscope
29
+
30
+ Visualize n-dimensional arrays in the terminal and Jupyter — with structural trees, tiled mosaics, array diffing, and distribution sparklines.
31
+
32
+ ```python
33
+ from arrscope import scope
34
+ import numpy as np
35
+
36
+ scope(np.random.rand(3, 4, 5))
37
+ ```
38
+
39
+ ## Features
40
+
41
+ - **1D → 6D+**: Tiered visual grammar — lists, grids, trees, nested layers
42
+ - **Named axes**: Attach semantics (`batch`, `heads`, `h`, `w`)
43
+ - **Four color modes**:
44
+ - `dtype` — semantic colors by data type (blue=float, green=int, …)
45
+ - `heatmap` — diverging colormap (red → light → blue) by value
46
+ - `sparsity` — zeros as `·`, non-zeros in bold
47
+ - `diff` — compare with a reference array (red=increased, blue=decreased, grey=unchanged)
48
+ - **Two render styles**:
49
+ - `tree` (default) — hierarchical branch view with colored guide lines
50
+ - `mosaic` — all 2D sub-slices tiled side by side as numeric tables
51
+ - **Distribution sparkline**: every output shows a unicode histogram (`▁▂▃▄▅▆▇█`) of value distribution — replaces text stats
52
+ - **Array diffing**: pass `reference=` to compare any two arrays. Color-coded per-cell changes + aggregate metrics (MSE, % changed)
53
+ - **Head/tail truncation**: large dims show first/last N slices with `…` (default 20)
54
+ - **Smart precision**: auto-detects significant figures for floats
55
+ - **Terminal + Jupyter**: Rich ANSI + static HTML/CSS with dark mode auto-detect
56
+ - **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX, tinygrad (lazy imports)
57
+ - **Zero config**: color always on, stats always on, auto-detect terminal vs notebook
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pip install arrscope
63
+ ```
64
+
65
+ Only requires `numpy` + `rich`. Torch/TF/JAX/tinygrad are optional.
66
+
67
+ ## Quick start
68
+
69
+ ```python
70
+ from arrscope import scope
71
+ import numpy as np
72
+
73
+ # Auto-detect — last 2 dims form the grid
74
+ scope(np.random.rand(3, 4, 5))
75
+
76
+ # Named axes
77
+ scope(
78
+ np.random.rand(2, 8, 32, 32),
79
+ axes=['batch', 'heads', 'h', 'w'],
80
+ grid=['h', 'w'],
81
+ title='Attention heads',
82
+ )
83
+
84
+ # Custom grid dims
85
+ scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
86
+
87
+ # Method chaining
88
+ r = scope(arr, mode='heatmap')
89
+ print(r.tree())
90
+ print(r.mosaic())
91
+ ```
92
+
93
+ ## Color modes
94
+
95
+ ```python
96
+ scope(arr, mode='dtype') # default — blue floats, green ints, ...
97
+ scope(arr, mode='heatmap') # diverging colormap by value
98
+ scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
99
+ scope(arr, mode='diff', # red/blue by change direction
100
+ reference=original_array) # Δ -0.5 ▁▂▃▄▅▆▇█ +0.5 mse=0.02 12% changed
101
+ ```
102
+
103
+ ## Array diffing
104
+
105
+ Compare any two arrays of the same shape. Values show the current array; color encodes the change:
106
+
107
+ ```python
108
+ before = np.random.rand(3, 4, 5)
109
+ after = before + np.random.normal(0, 0.1, before.shape)
110
+
111
+ scope(after, reference=before, mode="diff")
112
+ ```
113
+
114
+ Diff stats replace the sparkline: range of deltas, MSE, and percentage of elements that changed.
115
+
116
+ ## Render styles
117
+
118
+ ```python
119
+ scope(arr) # tree (default) — click-to-expand layers
120
+ scope(arr, render_style='mosaic') # all sub-slices tiled side by side
121
+ ```
122
+
123
+ ## CLI
124
+
125
+ ```bash
126
+ arrscope 3x4x5
127
+ arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
128
+ arrscope 20x4x5 --max-height 6
129
+ ```
130
+
131
+ ## Framework support
132
+
133
+ ```python
134
+ import torch; scope(torch.randn(2, 3, 4))
135
+ import tensorflow as tf; scope(tf.random.uniform((2, 3, 4)))
136
+ import jax.numpy as jnp; scope(jnp.array([[1, 2], [3, 4]]))
137
+ from tinygrad import Tensor; scope(Tensor.randn(3, 4))
138
+ ```
139
+
140
+ ## API
141
+
142
+ ```python
143
+ scope(
144
+ arr,
145
+ axes=None, # list[str] — name each dimension
146
+ grid=None, # list[str | int] — which dims form the leaf grid
147
+ title=None, # str — heading above the visualization
148
+ max_height=20, # int | None — rows before truncation, None disables
149
+ fmt=None, # str — format spec like '.4f'
150
+ mode='dtype', # 'dtype' | 'heatmap' | 'sparsity' | 'diff'
151
+ render_style='tree', # 'tree' | 'mosaic'
152
+ reference=None, # array-like — reference for diff mode
153
+ )
154
+ ```
155
+
156
+ ## Development
157
+
158
+ ```bash
159
+ git clone https://github.com/vizarray/arrscope
160
+ cd arrscope
161
+ uv sync
162
+ uv run pytest
163
+ uv run python main.py
164
+ ```
165
+
166
+ ## License
167
+
168
+ MIT
@@ -0,0 +1,141 @@
1
+ # arrscope
2
+
3
+ Visualize n-dimensional arrays in the terminal and Jupyter — with structural trees, tiled mosaics, array diffing, and distribution sparklines.
4
+
5
+ ```python
6
+ from arrscope import scope
7
+ import numpy as np
8
+
9
+ scope(np.random.rand(3, 4, 5))
10
+ ```
11
+
12
+ ## Features
13
+
14
+ - **1D → 6D+**: Tiered visual grammar — lists, grids, trees, nested layers
15
+ - **Named axes**: Attach semantics (`batch`, `heads`, `h`, `w`)
16
+ - **Four color modes**:
17
+ - `dtype` — semantic colors by data type (blue=float, green=int, …)
18
+ - `heatmap` — diverging colormap (red → light → blue) by value
19
+ - `sparsity` — zeros as `·`, non-zeros in bold
20
+ - `diff` — compare with a reference array (red=increased, blue=decreased, grey=unchanged)
21
+ - **Two render styles**:
22
+ - `tree` (default) — hierarchical branch view with colored guide lines
23
+ - `mosaic` — all 2D sub-slices tiled side by side as numeric tables
24
+ - **Distribution sparkline**: every output shows a unicode histogram (`▁▂▃▄▅▆▇█`) of value distribution — replaces text stats
25
+ - **Array diffing**: pass `reference=` to compare any two arrays. Color-coded per-cell changes + aggregate metrics (MSE, % changed)
26
+ - **Head/tail truncation**: large dims show first/last N slices with `…` (default 20)
27
+ - **Smart precision**: auto-detects significant figures for floats
28
+ - **Terminal + Jupyter**: Rich ANSI + static HTML/CSS with dark mode auto-detect
29
+ - **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX, tinygrad (lazy imports)
30
+ - **Zero config**: color always on, stats always on, auto-detect terminal vs notebook
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install arrscope
36
+ ```
37
+
38
+ Only requires `numpy` + `rich`. Torch/TF/JAX/tinygrad are optional.
39
+
40
+ ## Quick start
41
+
42
+ ```python
43
+ from arrscope import scope
44
+ import numpy as np
45
+
46
+ # Auto-detect — last 2 dims form the grid
47
+ scope(np.random.rand(3, 4, 5))
48
+
49
+ # Named axes
50
+ scope(
51
+ np.random.rand(2, 8, 32, 32),
52
+ axes=['batch', 'heads', 'h', 'w'],
53
+ grid=['h', 'w'],
54
+ title='Attention heads',
55
+ )
56
+
57
+ # Custom grid dims
58
+ scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
59
+
60
+ # Method chaining
61
+ r = scope(arr, mode='heatmap')
62
+ print(r.tree())
63
+ print(r.mosaic())
64
+ ```
65
+
66
+ ## Color modes
67
+
68
+ ```python
69
+ scope(arr, mode='dtype') # default — blue floats, green ints, ...
70
+ scope(arr, mode='heatmap') # diverging colormap by value
71
+ scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
72
+ scope(arr, mode='diff', # red/blue by change direction
73
+ reference=original_array) # Δ -0.5 ▁▂▃▄▅▆▇█ +0.5 mse=0.02 12% changed
74
+ ```
75
+
76
+ ## Array diffing
77
+
78
+ Compare any two arrays of the same shape. Values show the current array; color encodes the change:
79
+
80
+ ```python
81
+ before = np.random.rand(3, 4, 5)
82
+ after = before + np.random.normal(0, 0.1, before.shape)
83
+
84
+ scope(after, reference=before, mode="diff")
85
+ ```
86
+
87
+ Diff stats replace the sparkline: range of deltas, MSE, and percentage of elements that changed.
88
+
89
+ ## Render styles
90
+
91
+ ```python
92
+ scope(arr) # tree (default) — click-to-expand layers
93
+ scope(arr, render_style='mosaic') # all sub-slices tiled side by side
94
+ ```
95
+
96
+ ## CLI
97
+
98
+ ```bash
99
+ arrscope 3x4x5
100
+ arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
101
+ arrscope 20x4x5 --max-height 6
102
+ ```
103
+
104
+ ## Framework support
105
+
106
+ ```python
107
+ import torch; scope(torch.randn(2, 3, 4))
108
+ import tensorflow as tf; scope(tf.random.uniform((2, 3, 4)))
109
+ import jax.numpy as jnp; scope(jnp.array([[1, 2], [3, 4]]))
110
+ from tinygrad import Tensor; scope(Tensor.randn(3, 4))
111
+ ```
112
+
113
+ ## API
114
+
115
+ ```python
116
+ scope(
117
+ arr,
118
+ axes=None, # list[str] — name each dimension
119
+ grid=None, # list[str | int] — which dims form the leaf grid
120
+ title=None, # str — heading above the visualization
121
+ max_height=20, # int | None — rows before truncation, None disables
122
+ fmt=None, # str — format spec like '.4f'
123
+ mode='dtype', # 'dtype' | 'heatmap' | 'sparsity' | 'diff'
124
+ render_style='tree', # 'tree' | 'mosaic'
125
+ reference=None, # array-like — reference for diff mode
126
+ )
127
+ ```
128
+
129
+ ## Development
130
+
131
+ ```bash
132
+ git clone https://github.com/vizarray/arrscope
133
+ cd arrscope
134
+ uv sync
135
+ uv run pytest
136
+ uv run python main.py
137
+ ```
138
+
139
+ ## License
140
+
141
+ MIT
@@ -0,0 +1,71 @@
1
+ """arrscope render style examples.
2
+
3
+ Run this file to see both render styles in action.
4
+ Use method chaining to switch styles without re-specifying params.
5
+
6
+ Usage:
7
+ uv run python examples/renders.py
8
+ """
9
+
10
+ import numpy as np
11
+
12
+ from arrscope import scope
13
+
14
+ # ── data ──────────────────────────────────────────────────────────────
15
+ arr_3d = np.arange(60).reshape(3, 4, 5).astype(float)
16
+ eye = np.eye(8)
17
+ sparse = np.zeros((6, 8), dtype=float)
18
+ sparse[0, 0] = 1.5
19
+ sparse[2, 3] = -2.3
20
+ sparse[4, 5] = 42.0
21
+ sparse[5, 7] = 0.7
22
+
23
+
24
+ # ── individual calls ──────────────────────────────────────────────────
25
+ print("=" * 56)
26
+ print("1 tree — hierarchical branch view (default)")
27
+ print("=" * 56)
28
+ print(scope(arr_3d, render_style="tree"))
29
+
30
+ print("=" * 56)
31
+ print("2 mosaic — numeric tables tiled side by side")
32
+ print("=" * 56)
33
+ print(scope(arr_3d, render_style="mosaic"))
34
+
35
+
36
+ # ── method chaining ───────────────────────────────────────────────────
37
+ print("=" * 56)
38
+ print("3 method chaining — scope().tree() / .mosaic()")
39
+ print("=" * 56)
40
+
41
+ r = scope(eye, title="Identity", mode="heatmap")
42
+
43
+ print("--- .tree() ---")
44
+ print(r.tree())
45
+
46
+ print("--- .mosaic() ---")
47
+ print(r.mosaic())
48
+
49
+
50
+ # ── all three modes with mosaic ──────────────────────────────────────
51
+ print("=" * 56)
52
+ print("4 mosaic + all three color modes")
53
+ print("=" * 56)
54
+
55
+ print("--- dtype ---")
56
+ print(scope(sparse, render_style="mosaic", mode="dtype"))
57
+
58
+ print("--- heatmap ---")
59
+ print(scope(sparse, render_style="mosaic", mode="heatmap"))
60
+
61
+ print("--- sparsity ---")
62
+ print(scope(sparse, render_style="mosaic", mode="sparsity"))
63
+
64
+
65
+ # ── large array in mosaic ────────────────────────────────────────────
66
+ print("=" * 56)
67
+ print("5 large array — (8, 8, 8) as mosaic + heatmap")
68
+ print("=" * 56)
69
+ print(scope(np.random.rand(8, 8, 8), render_style="mosaic", mode="heatmap"))
70
+
71
+ print("\nDone!")
@@ -62,8 +62,8 @@ print(scope(sparse, mode="sparsity"))
62
62
  print("\n1️⃣2️⃣ HEATMAP + 3D + global stats")
63
63
  print(scope(np.random.rand(2, 3, 4), mode="heatmap"))
64
64
 
65
- print("\n1️⃣3️⃣ STATS OFF")
66
- print(scope(np.eye(5), stats=False))
65
+ print("\n1️⃣3️⃣ TRUNCATION OFF (max_height=None)")
66
+ print(scope(np.arange(60).reshape(3, 4, 5).astype(float), max_height=None))
67
67
 
68
68
  print("\n1️⃣4️⃣ MOSAIC — 3×4×5 tiled (no tree)")
69
69
  arr = np.arange(60).reshape(3, 4, 5).astype(float)
@@ -89,4 +89,9 @@ print(
89
89
  )
90
90
  )
91
91
 
92
+ print("\n1️⃣6️⃣ DIFF — compare two arrays (value = current, color = change)")
93
+ a = np.random.rand(4, 5)
94
+ b = a + np.random.normal(0, 0.1, a.shape)
95
+ print(scope(a, reference=b, mode="diff"))
96
+
92
97
  print("\n Done!")
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arrscope"
7
- version = "0.2.0"
7
+ version = "0.4.0"
8
8
  description = "Beautiful n-dimensional array visualization for Python"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,15 +1,11 @@
1
- from __future__ import annotations
2
-
3
1
  from typing import Literal
4
2
 
5
- from arrscope._config import config
6
- from arrscope._format import format_value
7
3
  from arrscope._layout import build_layout, compute_stats
8
4
  from arrscope._types import VizOutput
9
5
  from arrscope.adapters import to_numpy
10
6
  from arrscope.renderers import render_html, render_terminal
11
7
 
12
- Mode = Literal["dtype", "heatmap", "sparsity"]
8
+ Mode = Literal["dtype", "heatmap", "sparsity", "diff"]
13
9
  RenderStyle = Literal["tree", "mosaic"]
14
10
 
15
11
 
@@ -19,14 +15,11 @@ def scope(
19
15
  axes: list[str] | None = None,
20
16
  grid: list[str | int] | None = None,
21
17
  title: str | None = None,
22
- max_width: int | None = None,
23
- max_height: int | None = None,
18
+ max_height: int | None = 20,
24
19
  fmt: str | None = None,
25
- color: bool | None = None,
26
- style: Literal["auto", "terminal", "html"] = "auto",
27
20
  mode: Mode | None = None,
28
21
  render_style: RenderStyle | None = None,
29
- stats: bool | None = None,
22
+ reference=None,
30
23
  ) -> VizOutput:
31
24
  """Visualize an n-dimensional array in the terminal and/or Jupyter.
32
25
 
@@ -45,41 +38,36 @@ def scope(
45
38
  title: Optional heading displayed above the visualization.
46
39
  max_height: Max tree rows before truncation. The first half and
47
40
  last half of children are shown with ``... (N more)`` in between.
48
- max_width: Max character width (terminal only). Not yet enforced.
41
+ Defaults to 20. Set to ``None`` to disable truncation.
49
42
  fmt: Format spec for float values, e.g. ``".4f"``, ``".2e"``.
50
43
  Auto-detected from value range when not set.
51
- color: Enable or disable ANSI / CSS color output.
52
- Defaults to ``True``.
53
- style: Output target. ``"auto"`` detects Jupyter and renders
54
- HTML there, ANSI elsewhere. ``"terminal"`` forces ANSI,
55
- ``"html"`` forces HTML.
56
44
  mode: Colorization strategy:
57
45
 
58
- - ``"dtype"`` — Semantic colors by data type (blue=float,
59
- green=int, magenta=bool, yellow=str, red=complex).
46
+ - ``"dtype"`` (default) — Semantic colors by data type
47
+ (blue=float, green=int, magenta=bool, yellow=str).
60
48
  Negative values tinted red, positive tinted blue.
61
49
  - ``"heatmap"`` — Diverging colormap (red → light → blue)
62
- scaled to the array's global min/max range. Best for
63
- spotting value patterns.
50
+ scaled to the array's global min/max range.
64
51
  - ``"sparsity"`` — Zeros rendered as dim ``·``, non-zero
65
- values in bold. Best for inspecting sparse or masked arrays.
52
+ values in bold.
53
+ - ``"diff"`` — Compare with a reference array. Values
54
+ colored red/blue by how much they increased/decreased.
55
+ Requires ``reference``.
66
56
 
67
- render_style: Visual layout style:
57
+ render_style: Visual layout:
68
58
 
69
- - ``"tree"`` (default) — Hierarchical branch view with 2D
70
- grids nested at the leaves. Each depth level gets a
71
- distinct branch color for readability.
72
- - ``"mosaic"`` — All 2D sub-slices tiled side by side in a
73
- contact-sheet grid. Best for comparing slices in 3D/4D
74
- arrays (batches, attention heads, channels).
59
+ - ``"tree"`` (default) — Hierarchical branch view with
60
+ click-to-expand nested layers.
61
+ - ``"mosaic"`` All 2D sub-slices tiled side by side as
62
+ numeric tables.
75
63
 
76
- stats: Show summary statistics below the tree (min, max, mean,
77
- std, zeros %, NaN count). Defaults to ``True``.
64
+ reference: A reference array to compare against. Must have
65
+ the same shape as ``arr``. When provided, ``mode`` defaults
66
+ to ``"diff"`` and stats show difference metrics.
78
67
 
79
68
  Returns:
80
- A ``VizOutput`` object with ``.ansi`` (str) and ``.html`` (str)
81
- attributes. In Jupyter it auto-displays via ``_repr_html_``.
82
- In the terminal, ``print()`` or ``str()`` renders the ANSI output.
69
+ A ``VizOutput`` with ``.tree()`` and ``.mosaic()`` methods.
70
+ Renders as ANSI in the terminal and as HTML in Jupyter.
83
71
 
84
72
  Examples:
85
73
  >>> import numpy as np
@@ -88,59 +76,53 @@ def scope(
88
76
 
89
77
  >>> scope(np.eye(5), title="Identity", mode="sparsity")
90
78
 
91
- >>> scope(
92
- ... np.random.rand(2, 8, 32, 32),
93
- ... axes=["batch", "heads", "h", "w"],
94
- ... mode="heatmap",
95
- ... )
79
+ >>> a = np.random.rand(3, 4, 5)
80
+ >>> b = a + np.random.normal(0, 0.1, a.shape)
81
+ >>> scope(a, reference=b, mode="diff")
96
82
  """
97
83
  arr_np = to_numpy(arr)
98
84
 
99
- color = config.color if color is None else color
100
- max_width = max_width or config.max_width
101
- max_height = max_height or config.max_height
102
- mode = mode or config.mode
103
- rs = render_style or config.render_style
104
- show_stats = config.stats if stats is None else stats
85
+ if reference is not None:
86
+ ref_np = to_numpy(reference)
87
+ if ref_np.shape != arr_np.shape:
88
+ raise ValueError(
89
+ f"reference shape {ref_np.shape} does not match arr shape {arr_np.shape}"
90
+ )
91
+ else:
92
+ ref_np = None
93
+
94
+ mode = mode or ("diff" if ref_np is not None else "dtype")
95
+ rs = render_style or "tree"
105
96
 
106
- if mode not in ("dtype", "heatmap", "sparsity"):
107
- raise ValueError(f"Unknown mode '{mode}'. Expected one of: dtype, heatmap, sparsity")
97
+ if mode not in ("dtype", "heatmap", "sparsity", "diff"):
98
+ raise ValueError(f"Unknown mode '{mode}'. Expected: dtype, heatmap, sparsity, diff")
99
+
100
+ if mode == "diff" and ref_np is None:
101
+ raise ValueError("mode='diff' requires a reference array via the `reference` parameter")
108
102
 
109
103
  if rs not in ("tree", "mosaic"):
110
104
  raise ValueError(f"Unknown render_style '{rs}'. Expected one of: tree, mosaic")
111
105
 
112
106
  grid_dims = _resolve_grid_dims(arr_np, axes, grid)
107
+ use_html = _in_jupyter()
113
108
 
114
- node = build_layout(
115
- arr_np,
116
- grid_dims=grid_dims,
117
- max_height=max_height,
118
- )
119
-
120
- global_stats = compute_stats(arr_np) if (show_stats or mode == "heatmap") else None
121
- if global_stats and show_stats:
122
- node.stats = global_stats
123
-
124
- use_html = _use_html(style)
125
- use_terminal = _use_terminal(style)
126
-
127
- output = VizOutput()
128
-
129
- if use_html:
130
- output.html = _wrap_html(
131
- render_html(node, color=color, mode=mode, global_stats=global_stats, render_style=rs), title
132
- )
109
+ def _render(style: str) -> VizOutput:
110
+ node = build_layout(arr_np, grid_dims=grid_dims, max_height=max_height, reference=ref_np)
111
+ gs = compute_stats(arr_np, reference=ref_np)
112
+ if gs:
113
+ node.stats = gs
133
114
 
134
- if use_terminal:
135
- output.ansi = _wrap_ansi(
136
- render_terminal(node, color=color, mode=mode, global_stats=global_stats, render_style=rs), title
137
- )
115
+ out = VizOutput(_rerender=_render)
116
+ if use_html:
117
+ out._html = _wrap_html(render_html(node, mode=mode, global_stats=gs, render_style=style), title)
118
+ out._ansi = _wrap_ansi(render_terminal(node, mode=mode, global_stats=gs, render_style=style), title)
119
+ return out
138
120
 
139
- return output
121
+ return _render(rs)
140
122
 
141
123
 
142
124
  def _resolve_grid_dims(
143
- arr: np.ndarray,
125
+ arr,
144
126
  axes: list[str] | None,
145
127
  grid: list[str | int] | None,
146
128
  ) -> list[int] | None:
@@ -166,22 +148,6 @@ def _resolve_grid_dims(
166
148
  return resolved
167
149
 
168
150
 
169
- def _use_html(style: str) -> bool:
170
- if style == "html":
171
- return True
172
- if style == "terminal":
173
- return False
174
- return _in_jupyter()
175
-
176
-
177
- def _use_terminal(style: str) -> bool:
178
- if style == "terminal":
179
- return True
180
- if style == "html":
181
- return False
182
- return True
183
-
184
-
185
151
  def _in_jupyter() -> bool:
186
152
  try:
187
153
  from IPython import get_ipython
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class Config:
6
+ mode: str = "dtype"
7
+ render_style: str = "tree" # tree | mosaic
8
+
9
+
10
+ config: Config = Config()
@@ -23,6 +23,18 @@ def format_value(val, fmt: str | None = None) -> str:
23
23
  return str(val)
24
24
 
25
25
 
26
+ def sparkline(stats: dict) -> str:
27
+ """Return a unicode histogram sparkline from pre-computed bins."""
28
+ bins = stats.get("histogram")
29
+ if not bins:
30
+ return ""
31
+ mx = max(bins)
32
+ if mx == 0:
33
+ return "▁" * len(bins)
34
+ chars = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
35
+ return "".join(chars[min(int(b / mx * 7), 7)] for b in bins)
36
+
37
+
26
38
  def _format_float(val: float, fmt: str | None) -> str:
27
39
  if fmt is not None:
28
40
  return f"{val:{fmt}}"