arrscope 0.1.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.
- arrscope-0.1.0/.gitignore +10 -0
- arrscope-0.1.0/PKG-INFO +185 -0
- arrscope-0.1.0/README.md +158 -0
- arrscope-0.1.0/docs/ADR-001-visual-grammar.md +77 -0
- arrscope-0.1.0/docs/ADR-002-architecture.md +102 -0
- arrscope-0.1.0/docs/ADR-003-api-design.md +106 -0
- arrscope-0.1.0/docs/GLOSSARY.md +40 -0
- arrscope-0.1.0/main.py +68 -0
- arrscope-0.1.0/pyproject.toml +60 -0
- arrscope-0.1.0/src/arrscope/__init__.py +4 -0
- arrscope-0.1.0/src/arrscope/__main__.py +80 -0
- arrscope-0.1.0/src/arrscope/_api.py +131 -0
- arrscope-0.1.0/src/arrscope/_config.py +14 -0
- arrscope-0.1.0/src/arrscope/_format.py +58 -0
- arrscope-0.1.0/src/arrscope/_layout.py +151 -0
- arrscope-0.1.0/src/arrscope/_types.py +39 -0
- arrscope-0.1.0/src/arrscope/adapters/__init__.py +3 -0
- arrscope-0.1.0/src/arrscope/adapters/_core.py +33 -0
- arrscope-0.1.0/src/arrscope/renderers/__init__.py +4 -0
- arrscope-0.1.0/src/arrscope/renderers/html.py +295 -0
- arrscope-0.1.0/src/arrscope/renderers/terminal.py +277 -0
- arrscope-0.1.0/test.ipynb +209 -0
- arrscope-0.1.0/tests/test_adapter.py +24 -0
- arrscope-0.1.0/tests/test_format.py +30 -0
- arrscope-0.1.0/tests/test_layout.py +52 -0
- arrscope-0.1.0/tests/test_stats.py +46 -0
- arrscope-0.1.0/uv.lock +1502 -0
arrscope-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arrscope
|
|
3
|
+
Version: 0.1.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
|
+
Beautiful n-dimensional array visualization for Python — in the terminal and Jupyter.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from arrscope import scope
|
|
34
|
+
import numpy as np
|
|
35
|
+
|
|
36
|
+
scope(np.random.rand(2, 3, 8, 8), axes=['batch', 'heads', 'h', 'w'])
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
├── [0] (3, 8, 8)
|
|
41
|
+
│ ├── [0,0]: 8×8 grid
|
|
42
|
+
│ ├── [0,1]: 8×8 grid
|
|
43
|
+
│ └── [0,2]: 8×8 grid
|
|
44
|
+
└── [1] (3, 8, 8)
|
|
45
|
+
...
|
|
46
|
+
min=0.001 max=0.999 mean=0.5 std=0.29 zeros=0.0%
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- **1D → 6D+**: Tiered visual grammar — lists, grids, trees, collapsed hierarchies
|
|
52
|
+
- **Named axes**: Attach semantics to dimensions (`batch`, `heads`, `h`, `w`)
|
|
53
|
+
- **Configurable grid**: Pick which axes form the leaf 2D matrix
|
|
54
|
+
- **Three color modes**:
|
|
55
|
+
- `dtype` — semantic colors by data type (float=blue, int=green, bool=magenta, …)
|
|
56
|
+
- `heatmap` — diverging colormap (red→light→blue) by value magnitude
|
|
57
|
+
- `sparsity` — zeros as `·`, non-zeros highlighted in bold
|
|
58
|
+
- **Stats overlay**: min, max, mean, std, zero%, NaN count
|
|
59
|
+
- **Head/tail truncation**: large dimensions show first/last N slices with `…`
|
|
60
|
+
- **Smart precision**: auto-detects significant figures for floats
|
|
61
|
+
- **Terminal + Jupyter**: Rich ANSI output + static HTML/CSS with dark mode
|
|
62
|
+
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX (lazy imports, no hard deps)
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install arrscope
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Only requires `numpy` + `rich`. Torch/TF/JAX are optional — pass any array type, it just works.
|
|
71
|
+
|
|
72
|
+
## Quick start
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from arrscope import scope
|
|
76
|
+
import numpy as np
|
|
77
|
+
|
|
78
|
+
# Auto-detect — last 2 dims form the grid
|
|
79
|
+
scope(np.random.rand(3, 4, 5))
|
|
80
|
+
|
|
81
|
+
# Named axes for clarity
|
|
82
|
+
scope(
|
|
83
|
+
np.random.rand(2, 8, 32, 32),
|
|
84
|
+
axes=['batch', 'heads', 'h', 'w'],
|
|
85
|
+
grid=['h', 'w'],
|
|
86
|
+
title='Attention heads',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Pick any dims as the leaf grid
|
|
90
|
+
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Color modes
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
scope(arr, mode='dtype') # default — blue floats, green ints, etc.
|
|
97
|
+
scope(arr, mode='heatmap') # diverging colormap by value
|
|
98
|
+
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Stats overlay
|
|
102
|
+
|
|
103
|
+
Stats are shown by default. Hide with:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
scope(arr, stats=False)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Shows: `min=0.0 max=1.0 mean=0.5 std=0.29 zeros=12.5%`
|
|
110
|
+
|
|
111
|
+
## Truncation
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
scope(np.random.rand(100, 32, 32), max_height=8)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Shows first 4 + last 4 slices with `… (92 more)` in between.
|
|
118
|
+
|
|
119
|
+
## Custom formatting
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
scope(arr, fmt='.2f') # fixed precision
|
|
123
|
+
scope(arr, color=False) # monochrome
|
|
124
|
+
scope(arr, style='html') # force HTML output in terminal
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## CLI
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Install globally (ships with the library)
|
|
131
|
+
pip install arrscope
|
|
132
|
+
|
|
133
|
+
# Use from anywhere
|
|
134
|
+
arrscope 3x4x5
|
|
135
|
+
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
136
|
+
arrscope 20x4x5 --max-height 6 --no-stats
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Framework support
|
|
140
|
+
|
|
141
|
+
Pass any array-like — conversion is automatic:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
import torch
|
|
145
|
+
scope(torch.randn(2, 3, 4)) # PyTorch
|
|
146
|
+
|
|
147
|
+
import tensorflow as tf
|
|
148
|
+
scope(tf.random.uniform((2, 3, 4))) # TensorFlow
|
|
149
|
+
|
|
150
|
+
import jax.numpy as jnp
|
|
151
|
+
scope(jnp.array([[1, 2], [3, 4]])) # JAX
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## API
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
scope(
|
|
158
|
+
arr, # np.ndarray | torch.Tensor | tf.Tensor | jax.Array
|
|
159
|
+
axes=None, # list[str] — name each dimension
|
|
160
|
+
grid=None, # list[str | int] — which dims form the leaf grid
|
|
161
|
+
title=None, # str — heading above the visualization
|
|
162
|
+
max_width=None, # int — max characters wide
|
|
163
|
+
max_height=None, # int — max rows before truncation
|
|
164
|
+
fmt=None, # str — format spec like '.4f'
|
|
165
|
+
color=True, # bool — enable/disable color
|
|
166
|
+
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity'
|
|
167
|
+
stats=True, # bool — show min/max/mean/std/zeros
|
|
168
|
+
style='auto', # 'auto' | 'terminal' | 'html'
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
git clone https://github.com/vizarray/arrscope
|
|
176
|
+
cd arrscope
|
|
177
|
+
uv sync
|
|
178
|
+
uv run pytest
|
|
179
|
+
uv run python main.py # demo script
|
|
180
|
+
uv run jupyter notebook test.ipynb # notebook demo
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT
|
arrscope-0.1.0/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# arrscope
|
|
2
|
+
|
|
3
|
+
Beautiful n-dimensional array visualization for Python — in the terminal and Jupyter.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from arrscope import scope
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
scope(np.random.rand(2, 3, 8, 8), axes=['batch', 'heads', 'h', 'w'])
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
├── [0] (3, 8, 8)
|
|
14
|
+
│ ├── [0,0]: 8×8 grid
|
|
15
|
+
│ ├── [0,1]: 8×8 grid
|
|
16
|
+
│ └── [0,2]: 8×8 grid
|
|
17
|
+
└── [1] (3, 8, 8)
|
|
18
|
+
...
|
|
19
|
+
min=0.001 max=0.999 mean=0.5 std=0.29 zeros=0.0%
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **1D → 6D+**: Tiered visual grammar — lists, grids, trees, collapsed hierarchies
|
|
25
|
+
- **Named axes**: Attach semantics to dimensions (`batch`, `heads`, `h`, `w`)
|
|
26
|
+
- **Configurable grid**: Pick which axes form the leaf 2D matrix
|
|
27
|
+
- **Three color modes**:
|
|
28
|
+
- `dtype` — semantic colors by data type (float=blue, int=green, bool=magenta, …)
|
|
29
|
+
- `heatmap` — diverging colormap (red→light→blue) by value magnitude
|
|
30
|
+
- `sparsity` — zeros as `·`, non-zeros highlighted in bold
|
|
31
|
+
- **Stats overlay**: min, max, mean, std, zero%, NaN count
|
|
32
|
+
- **Head/tail truncation**: large dimensions show first/last N slices with `…`
|
|
33
|
+
- **Smart precision**: auto-detects significant figures for floats
|
|
34
|
+
- **Terminal + Jupyter**: Rich ANSI output + static HTML/CSS with dark mode
|
|
35
|
+
- **Multi-framework**: NumPy, PyTorch, TensorFlow, JAX (lazy imports, no hard deps)
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install arrscope
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Only requires `numpy` + `rich`. Torch/TF/JAX are optional — pass any array type, it just works.
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from arrscope import scope
|
|
49
|
+
import numpy as np
|
|
50
|
+
|
|
51
|
+
# Auto-detect — last 2 dims form the grid
|
|
52
|
+
scope(np.random.rand(3, 4, 5))
|
|
53
|
+
|
|
54
|
+
# Named axes for clarity
|
|
55
|
+
scope(
|
|
56
|
+
np.random.rand(2, 8, 32, 32),
|
|
57
|
+
axes=['batch', 'heads', 'h', 'w'],
|
|
58
|
+
grid=['h', 'w'],
|
|
59
|
+
title='Attention heads',
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Pick any dims as the leaf grid
|
|
63
|
+
scope(data, axes=['a', 'b', 'c', 'd'], grid=['a', 'b'])
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Color modes
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
scope(arr, mode='dtype') # default — blue floats, green ints, etc.
|
|
70
|
+
scope(arr, mode='heatmap') # diverging colormap by value
|
|
71
|
+
scope(arr, mode='sparsity') # · for zeros, bold for non-zeros
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Stats overlay
|
|
75
|
+
|
|
76
|
+
Stats are shown by default. Hide with:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
scope(arr, stats=False)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Shows: `min=0.0 max=1.0 mean=0.5 std=0.29 zeros=12.5%`
|
|
83
|
+
|
|
84
|
+
## Truncation
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
scope(np.random.rand(100, 32, 32), max_height=8)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Shows first 4 + last 4 slices with `… (92 more)` in between.
|
|
91
|
+
|
|
92
|
+
## Custom formatting
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
scope(arr, fmt='.2f') # fixed precision
|
|
96
|
+
scope(arr, color=False) # monochrome
|
|
97
|
+
scope(arr, style='html') # force HTML output in terminal
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## CLI
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Install globally (ships with the library)
|
|
104
|
+
pip install arrscope
|
|
105
|
+
|
|
106
|
+
# Use from anywhere
|
|
107
|
+
arrscope 3x4x5
|
|
108
|
+
arrscope 2x3x32x32 --axes batch heads h w --grid h w --mode heatmap
|
|
109
|
+
arrscope 20x4x5 --max-height 6 --no-stats
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Framework support
|
|
113
|
+
|
|
114
|
+
Pass any array-like — conversion is automatic:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import torch
|
|
118
|
+
scope(torch.randn(2, 3, 4)) # PyTorch
|
|
119
|
+
|
|
120
|
+
import tensorflow as tf
|
|
121
|
+
scope(tf.random.uniform((2, 3, 4))) # TensorFlow
|
|
122
|
+
|
|
123
|
+
import jax.numpy as jnp
|
|
124
|
+
scope(jnp.array([[1, 2], [3, 4]])) # JAX
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## API
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
scope(
|
|
131
|
+
arr, # np.ndarray | torch.Tensor | tf.Tensor | jax.Array
|
|
132
|
+
axes=None, # list[str] — name each dimension
|
|
133
|
+
grid=None, # list[str | int] — which dims form the leaf grid
|
|
134
|
+
title=None, # str — heading above the visualization
|
|
135
|
+
max_width=None, # int — max characters wide
|
|
136
|
+
max_height=None, # int — max rows before truncation
|
|
137
|
+
fmt=None, # str — format spec like '.4f'
|
|
138
|
+
color=True, # bool — enable/disable color
|
|
139
|
+
mode='dtype', # 'dtype' | 'heatmap' | 'sparsity'
|
|
140
|
+
stats=True, # bool — show min/max/mean/std/zeros
|
|
141
|
+
style='auto', # 'auto' | 'terminal' | 'html'
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git clone https://github.com/vizarray/arrscope
|
|
149
|
+
cd arrscope
|
|
150
|
+
uv sync
|
|
151
|
+
uv run pytest
|
|
152
|
+
uv run python main.py # demo script
|
|
153
|
+
uv run jupyter notebook test.ipynb # notebook demo
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# ADR-001: Visual Grammar & Display Strategy
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-30
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
arrscope needs to visually represent n-dimensional arrays (1D to 6D+) in a way that humans can intuitively parse. The core challenge is that humans can natively perceive:
|
|
9
|
+
|
|
10
|
+
- **1D** — a linear list
|
|
11
|
+
- **2D** — a grid/table/matrix
|
|
12
|
+
- **3D** — a stack of grids (with mental effort)
|
|
13
|
+
|
|
14
|
+
Beyond 3D, direct spatial intuition fails. The library must provide a principled decomposition of higher dimensions into comprehensible visual hierarchies.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
### Tiered visual grammar
|
|
19
|
+
|
|
20
|
+
The display strategy is a fixed **tiered system** based on the number of dimensions being displayed as the leaf grid (default: 2):
|
|
21
|
+
|
|
22
|
+
| Grid dims | Total dims | Strategy | Example shape |
|
|
23
|
+
|-----------|------------|----------|--------------|
|
|
24
|
+
| 1 | 1 | Horizontal row `[a, b, c]` | `(5,)` |
|
|
25
|
+
| 2 | 2 | Table/grid with borders | `(4, 5)` |
|
|
26
|
+
| 2 | 3 | Stacked grids with index headers | `(3, 4, 5)` |
|
|
27
|
+
| 2 | 4 | 2D spatial arrangement of grids (grid-of-grids) | `(2, 3, 4, 5)` |
|
|
28
|
+
| 2 | 5 | Collapsed tree — outer dims as foldable sections | `(2, 3, 4, 5, 6)` |
|
|
29
|
+
| 2 | 6+ | Summary mode — shape + stats + spark, expandable | `(2, 3, 4, 5, 6, 7)` |
|
|
30
|
+
|
|
31
|
+
The **leaf grid** is the 2D block of actual values. Everything prior is structural nesting.
|
|
32
|
+
|
|
33
|
+
### Grid-of-grids for 4D
|
|
34
|
+
|
|
35
|
+
For shapes like `(batch=3, heads=4, h=8, w=8)`:
|
|
36
|
+
- The 8×8 matrices are the leaf grids
|
|
37
|
+
- They are arranged in a 3×4 visual grid, each cell labeled `[b=0, h=0]`, `[b=0, h=1]`, etc.
|
|
38
|
+
- The outer arrangement uses the same box-drawing characters as a 2D table, creating a unified visual language
|
|
39
|
+
|
|
40
|
+
### Collapsed tree for 5D+
|
|
41
|
+
|
|
42
|
+
For shapes beyond 4 total dimensions:
|
|
43
|
+
- Show a tree where each leaf is a grid (or summary)
|
|
44
|
+
- Branches show index labels and sub-shapes
|
|
45
|
+
- The tree is truncated via head/tail sampling when branches exceed a threshold
|
|
46
|
+
|
|
47
|
+
Example tree output:
|
|
48
|
+
```
|
|
49
|
+
(3, 4, 5, 6) float64
|
|
50
|
+
├── [0]: (4, 5, 6)
|
|
51
|
+
│ ├── [0,0]: 5×6 grid
|
|
52
|
+
│ ├── [0,1]: 5×6 grid
|
|
53
|
+
│ ├── ...
|
|
54
|
+
│ └── [0,3]: 5×6 grid
|
|
55
|
+
├── [1]: (4, 5, 6)
|
|
56
|
+
├── [2]: (4, 5, 6)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Truncation
|
|
60
|
+
|
|
61
|
+
Large dimensions use **head/tail truncation**: show first N and last N slices, with an ellipsis indicator in between. The threshold is adaptively chosen based on available terminal height / viewport.
|
|
62
|
+
|
|
63
|
+
### Configurable axes
|
|
64
|
+
|
|
65
|
+
The user can specify which axes form the leaf grid. By default, the last 2 axes are the grid. With named axes:
|
|
66
|
+
```python
|
|
67
|
+
viz(arr, axes=['batch', 'heads', 'h', 'w'], grid=['h', 'w'])
|
|
68
|
+
```
|
|
69
|
+
This changes which dimensions form the leaf 2D grid, allowing the user to prioritize the spatial dimensions most relevant to them.
|
|
70
|
+
|
|
71
|
+
## Consequences
|
|
72
|
+
|
|
73
|
+
- **Positive:** The grammar is deterministic and predictable. Given axes and a shape, the output layout is fully determined.
|
|
74
|
+
- **Positive:** Dimensionality reduction is automatic — the user doesn't configure the tier, just the axes.
|
|
75
|
+
- **Positive:** The grid-of-grids strategy for 4D is novel and immediately readable for ML practitioners.
|
|
76
|
+
- **Negative:** The tree view for 5D+ is not as visually rich as the grid-of-grids; it trades density for clarity.
|
|
77
|
+
- **Negative:** Head/tail truncation loses information in the middle. A future enhancement could add summary stats for truncated regions.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ADR-002: Architecture & Rendering Pipeline
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-30
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
arrscope must render to two distinct output media (terminal ANSI and Jupyter HTML) with different layouts per medium. The architecture must be extensible, testable, and avoid duplicating layout logic.
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
|
|
12
|
+
### Three-layer architecture
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─────────────────────────────────────────────┐
|
|
16
|
+
│ Public API │
|
|
17
|
+
│ viz(arr, axes, grid, ...) │
|
|
18
|
+
├─────────────────────────────────────────────┤
|
|
19
|
+
│ Layout Engine │
|
|
20
|
+
│ Builds a tree of VizNode objects from │
|
|
21
|
+
│ array + axis spec → intermediate repr │
|
|
22
|
+
├──────────────────┬──────────────────────────┤
|
|
23
|
+
│ TerminalRenderer│ HTMLRenderer │
|
|
24
|
+
│ (Rich library) │ (HTML/CSS, no JS) │
|
|
25
|
+
└──────────────────┴──────────────────────────┘
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Layout Engine (`arrscope/_layout.py`)
|
|
29
|
+
|
|
30
|
+
The core algorithm:
|
|
31
|
+
|
|
32
|
+
1. **Validate inputs** — check array is rectilinear, axes match shape, grid dims are valid
|
|
33
|
+
2. **Normalize axes** — assign default names if not provided, resolve grid/layout dims
|
|
34
|
+
3. **Build node tree** — recursively slice the array along non-grid dimensions:
|
|
35
|
+
- Each node has: `indices`, `label`, `shape`, `children`, `grid_data`
|
|
36
|
+
- Leaves hold the 2D slice (last 2 dims after slicing outer dims)
|
|
37
|
+
4. **Apply truncation** — trim child lists to head/tail at each level
|
|
38
|
+
|
|
39
|
+
The `VizNode` tree is the **intermediate representation**: it contains all information needed to render, and is independent of any rendering library.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
class VizNode:
|
|
43
|
+
indices: tuple[int, ...] # position in parent, e.g. (0, 2)
|
|
44
|
+
label: str # e.g. "[batch=0, heads=2]"
|
|
45
|
+
shape: tuple[int, ...] # full shape of this sub-array
|
|
46
|
+
children: list[VizNode] # sub-nodes for outer dims
|
|
47
|
+
grid_data: np.ndarray | None # the 2D slice at this leaf
|
|
48
|
+
truncated: bool # whether children were truncated
|
|
49
|
+
truncated_head: int # count shown from start
|
|
50
|
+
truncated_tail: int # count shown from end
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Terminal Renderer (`arrscope/renderers/terminal.py`)
|
|
54
|
+
|
|
55
|
+
Uses the **Rich** library to render nodes:
|
|
56
|
+
|
|
57
|
+
- **1D:** `rich.text.Text` with brackets
|
|
58
|
+
- **2D:** `rich.table.Table` with box-drawing borders
|
|
59
|
+
- **3D:** `rich.table.Table` with labeled sections, each section containing a sub-Table
|
|
60
|
+
- **4D:** `rich.layout.Layout` arranging sub-Tables in a grid, or `rich.table.Table` with nested Tables in cells
|
|
61
|
+
- **5D+:** `rich.tree.Tree` with `rich.table.Table` at leaves
|
|
62
|
+
|
|
63
|
+
Color is applied per cell based on dtype (see ADR-001 for color scheme).
|
|
64
|
+
|
|
65
|
+
### HTML Renderer (`arrscope/renderers/html.py`)
|
|
66
|
+
|
|
67
|
+
Generates static HTML/CSS strings:
|
|
68
|
+
|
|
69
|
+
- Uses `<table>` elements with inline CSS for styling
|
|
70
|
+
- Collapsible sections use `<details>`/`<summary>` (no JavaScript)
|
|
71
|
+
- CSS respects `prefers-color-scheme` for dark/light mode
|
|
72
|
+
- Each output is an `HTML` object (IPython display hook)
|
|
73
|
+
|
|
74
|
+
### Framework Adapters (`arrscope/adapters/`)
|
|
75
|
+
|
|
76
|
+
Lazy imports with guard:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
def _to_numpy(arr):
|
|
80
|
+
if hasattr(arr, '__array__'):
|
|
81
|
+
return np.asarray(arr)
|
|
82
|
+
module = type(arr).__module__
|
|
83
|
+
if 'torch' in module:
|
|
84
|
+
import torch
|
|
85
|
+
return arr.detach().cpu().numpy()
|
|
86
|
+
if 'tensorflow' in module or 'keras' in module:
|
|
87
|
+
return arr.numpy()
|
|
88
|
+
if 'jax' in module:
|
|
89
|
+
import jax
|
|
90
|
+
return np.asarray(arr)
|
|
91
|
+
return np.asarray(arr)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Each adapter is an optional import — failure raises a clear `ImportError` with install instructions.
|
|
95
|
+
|
|
96
|
+
## Consequences
|
|
97
|
+
|
|
98
|
+
- **Positive:** The VizNode tree is pure data — it can be serialized, tested, and cached independently of rendering.
|
|
99
|
+
- **Positive:** Adding a new renderer (e.g., SVG, PNG) requires no changes to the layout engine.
|
|
100
|
+
- **Positive:** Framework adapters are isolated and easy to extend.
|
|
101
|
+
- **Negative:** The full array is materialized in memory during layout tree construction. For gigantic arrays, this is unavoidable (you need the values to display them).
|
|
102
|
+
- **Negative:** Rich is a heavy dependency (~500KB + deps). Tradeoff accepted for faster development.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ADR-003: Public API Design
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-06-30
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The API must be intuitive for ML practitioners, composable, and discoverable via tab-completion. The library should be importable as `arrscope` with a single top-level `scope` function as the primary entry point.
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
|
|
12
|
+
### Top-level import
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from arrscope import scope
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The library is installed as `pip install arrscope` and imported as `arrscope`. The `scope` function is the only public API surface in v1.
|
|
19
|
+
|
|
20
|
+
### Function signature
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
def scope(
|
|
24
|
+
arr: ArrayLike,
|
|
25
|
+
*,
|
|
26
|
+
axes: list[str] | None = None,
|
|
27
|
+
grid: list[str | int] | None = None,
|
|
28
|
+
title: str | None = None,
|
|
29
|
+
max_width: int | None = None,
|
|
30
|
+
max_height: int | None = None,
|
|
31
|
+
fmt: str | None = None,
|
|
32
|
+
color: bool = True,
|
|
33
|
+
style: str = "auto",
|
|
34
|
+
) -> VizOutput:
|
|
35
|
+
...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Parameters
|
|
39
|
+
|
|
40
|
+
| Parameter | Type | Default | Description |
|
|
41
|
+
|-----------|------|---------|-------------|
|
|
42
|
+
| `arr` | `ArrayLike` | required | NumPy array, torch tensor, TF tensor, JAX array, or nested Python list |
|
|
43
|
+
| `axes` | `list[str] \| None` | `None` | Names for each dimension. If None, auto-named as `dim_0, dim_1, ...` |
|
|
44
|
+
| `grid` | `list[str \| int] \| None` | `None` | Which axes form the leaf 2D grid. Default: last 2. Accepts names or indices |
|
|
45
|
+
| `title` | `str \| None` | `None` | Optional heading displayed above the visualization |
|
|
46
|
+
| `max_width` | `int \| None` | `None` | Max characters wide. Auto-detected from terminal in TTY mode |
|
|
47
|
+
| `max_height` | `int \| None` | `None` | Max rows. Triggers truncation when exceeded |
|
|
48
|
+
| `fmt` | `str \| None` | `None` | Format spec for values, e.g. `'.4f'`, `'.2e'`. Default: smart precision |
|
|
49
|
+
| `color` | `bool` | `True` | Enable semantic color by dtype |
|
|
50
|
+
| `style` | `str` | `"auto"` | Output style: `"auto"` (detect terminal vs Jupyter), `"terminal"`, `"html"` |
|
|
51
|
+
|
|
52
|
+
### Return type
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
class VizOutput:
|
|
56
|
+
"""Holds both representations. Auto-displays in the appropriate medium."""
|
|
57
|
+
html: str # HTML/CSS string
|
|
58
|
+
ansi: str # Rich-rendered ANSI string (or Rich Renderable)
|
|
59
|
+
|
|
60
|
+
def _repr_html_(self) -> str: ...
|
|
61
|
+
def _repr_rich_(self) -> RenderableType: ...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
In Jupyter, `_repr_html_` is called automatically. In a terminal, when the result is the last expression in a cell, Rich's `_repr_rich_` is used. Users can also explicitly call `scope(..., style='html')` to get the HTML string.
|
|
65
|
+
|
|
66
|
+
### Examples
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# Basic usage — auto-detect axes
|
|
70
|
+
scope(np.random.rand(3, 4, 5))
|
|
71
|
+
|
|
72
|
+
# With named axes and explicit grid
|
|
73
|
+
scope(
|
|
74
|
+
np.random.rand(2, 8, 32, 32),
|
|
75
|
+
axes=['batch', 'heads', 'h', 'w'],
|
|
76
|
+
grid=['h', 'w'],
|
|
77
|
+
title='Attention heads'
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Custom format
|
|
81
|
+
scope(np.eye(5), fmt='.2f', color=False)
|
|
82
|
+
|
|
83
|
+
# Select specific grid dims by index
|
|
84
|
+
scope(data, grid=(-2, -1)) # last two dims (same as default)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Configuration via `arrscope.config`
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from arrscope import config
|
|
91
|
+
|
|
92
|
+
config.max_width = 120
|
|
93
|
+
config.color = False
|
|
94
|
+
config.precision = 4
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Module-level settings for global overrides.
|
|
98
|
+
|
|
99
|
+
## Consequences
|
|
100
|
+
|
|
101
|
+
- **Positive:** Single function with sensible defaults — zero-config for basic use.
|
|
102
|
+
- **Positive:** Named axes system integrates naturally with ML workflows where axes have semantics.
|
|
103
|
+
- **Positive:** Auto-detect of terminal vs Jupyter means no boilerplate for the user.
|
|
104
|
+
- **Positive:** The `VizOutput` object leverages IPython's display protocol and Rich's repr protocol simultaneously.
|
|
105
|
+
- **Negative:** The `style="auto"` heuristic may misdetect in edge cases (e.g., inside a pipe). Users can always override.
|
|
106
|
+
- **Negative:** Named axes require the user to pass the `axes` list, which adds friction for quick one-offs.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# arrscope Glossary
|
|
2
|
+
|
|
3
|
+
### ArrayLike
|
|
4
|
+
Any array-like object accepted by arrscope: `np.ndarray`, `torch.Tensor`, `tf.Tensor`, `jax.Array`, or deeply nested Python lists of uniform shape.
|
|
5
|
+
|
|
6
|
+
### Axis
|
|
7
|
+
A named dimension of an array. In `viz(arr, axes=['batch', 'h', 'w', 'c'])`, each string is an axis name. Axes can be referenced by name or index throughout the API.
|
|
8
|
+
|
|
9
|
+
### Grid
|
|
10
|
+
The leaf 2D representation of an array. The `grid` parameter specifies which axes form this matrix. Default is the last 2 axes.
|
|
11
|
+
|
|
12
|
+
### Grid-of-grids
|
|
13
|
+
The display strategy for 4D arrays: a 2D spatial arrangement of 2D matrices, each labeled by its outer indices. For `(batch=3, heads=4, h=8, w=8)` with `grid=['h','w']`, the output is a 3×4 arrangement of 8×8 grids.
|
|
14
|
+
|
|
15
|
+
### Head/tail truncation
|
|
16
|
+
Display strategy for large dimensions: show the first N and last N slices, with `...` (and count) between them. N is adaptively chosen based on available viewport.
|
|
17
|
+
|
|
18
|
+
### Layout Engine
|
|
19
|
+
The core module that converts an array + axis specification into a `VizNode` tree — a renderer-independent intermediate representation.
|
|
20
|
+
|
|
21
|
+
### Leaf grid
|
|
22
|
+
The final 2D slice displayed at the leaves of the node tree. This is where the actual numeric values are shown.
|
|
23
|
+
|
|
24
|
+
### Semantic coloring
|
|
25
|
+
Color scheme where color is determined by data type: floats → blue/cyan, ints → green, bools → magenta, strings → yellow, complex → red.
|
|
26
|
+
|
|
27
|
+
### Smart precision
|
|
28
|
+
Auto-detected float formatting: displays 2–4 significant figures based on magnitude. Uses scientific notation for very small/large values.
|
|
29
|
+
|
|
30
|
+
### Stacked grids
|
|
31
|
+
Display strategy for 3D: a sequence of 2D grids separated by section headers like `[0, :, :]`, `[1, :, :]`, etc.
|
|
32
|
+
|
|
33
|
+
### Tree view
|
|
34
|
+
Display strategy for 5D+: a hierarchical tree with collapsible branches, where each leaf contains a 2D grid. Outer dimensions form the tree structure.
|
|
35
|
+
|
|
36
|
+
### VizNode
|
|
37
|
+
The intermediate representation node. Contains `indices`, `label`, `shape`, `children`, and `grid_data`. Produced by the layout engine, consumed by renderers.
|
|
38
|
+
|
|
39
|
+
### VizOutput
|
|
40
|
+
The return type of `viz()`. Holds both HTML and ANSI representations and auto-displays in the correct medium via `_repr_html_` and `_repr_rich_`.
|