tensor-ee 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.
Files changed (84) hide show
  1. tensor_ee/__init__.py +28 -0
  2. tensor_ee/_swin_parameters.py +304 -0
  3. tensor_ee/blocks/__init__.py +0 -0
  4. tensor_ee/blocks/raster/__init__.py +36 -0
  5. tensor_ee/blocks/raster/activations.py +183 -0
  6. tensor_ee/blocks/raster/conv.py +183 -0
  7. tensor_ee/blocks/raster/merge.py +66 -0
  8. tensor_ee/blocks/raster/outputs.py +211 -0
  9. tensor_ee/blocks/raster/pooling.py +182 -0
  10. tensor_ee/blocks/raster/resample.py +64 -0
  11. tensor_ee/blocks/window/__init__.py +44 -0
  12. tensor_ee/blocks/window/activations.py +54 -0
  13. tensor_ee/blocks/window/attention.py +366 -0
  14. tensor_ee/blocks/window/conv.py +323 -0
  15. tensor_ee/blocks/window/linear.py +129 -0
  16. tensor_ee/blocks/window/merge.py +67 -0
  17. tensor_ee/blocks/window/normalization.py +197 -0
  18. tensor_ee/blocks/window/patch.py +362 -0
  19. tensor_ee/blocks/window/pooling.py +275 -0
  20. tensor_ee/blocks/window/resample.py +286 -0
  21. tensor_ee/blocks/window/segformer.py +718 -0
  22. tensor_ee/blocks/window/swin.py +602 -0
  23. tensor_ee/blocks/window/swin_upernet_config.py +361 -0
  24. tensor_ee/blocks/window/upernet.py +1016 -0
  25. tensor_ee/comparison.py +213 -0
  26. tensor_ee/compatibility.py +180 -0
  27. tensor_ee/compiler.py +356 -0
  28. tensor_ee/converter/__init__.py +72 -0
  29. tensor_ee/converter/build.py +120 -0
  30. tensor_ee/converter/convert.py +388 -0
  31. tensor_ee/converter/export.py +846 -0
  32. tensor_ee/converter/graph.py +417 -0
  33. tensor_ee/converter/operations.py +790 -0
  34. tensor_ee/converter/plan.py +675 -0
  35. tensor_ee/converter/recognizers.py +1273 -0
  36. tensor_ee/converter/request.py +146 -0
  37. tensor_ee/converter/run.py +950 -0
  38. tensor_ee/core/__init__.py +8 -0
  39. tensor_ee/core/errors.py +25 -0
  40. tensor_ee/core/grid.py +323 -0
  41. tensor_ee/core/metadata.py +398 -0
  42. tensor_ee/core/ops.py +336 -0
  43. tensor_ee/core/precision.py +133 -0
  44. tensor_ee/core/tensor.py +129 -0
  45. tensor_ee/examples/__init__.py +1 -0
  46. tensor_ee/examples/nonwater.py +251 -0
  47. tensor_ee/layouts/__init__.py +17 -0
  48. tensor_ee/layouts/boundaries.py +103 -0
  49. tensor_ee/layouts/raster.py +117 -0
  50. tensor_ee/layouts/shift.py +226 -0
  51. tensor_ee/layouts/window.py +201 -0
  52. tensor_ee/models/__init__.py +16 -0
  53. tensor_ee/models/fixtures.py +119 -0
  54. tensor_ee/models/segformer_b0/__init__.py +9 -0
  55. tensor_ee/models/segformer_b0/configuration.py +209 -0
  56. tensor_ee/models/segformer_b0/decoder.py +130 -0
  57. tensor_ee/models/segformer_b0/encoder.py +90 -0
  58. tensor_ee/models/swin_t224/__init__.py +30 -0
  59. tensor_ee/models/swin_t224/configuration.py +103 -0
  60. tensor_ee/models/swin_t224/decoder.py +53 -0
  61. tensor_ee/models/swin_t224/encoder.py +50 -0
  62. tensor_ee/onnx_import.py +431 -0
  63. tensor_ee/outputs.py +16 -0
  64. tensor_ee/primitives.py +110 -0
  65. tensor_ee/receptive_field.py +257 -0
  66. tensor_ee/resnet_models.py +185 -0
  67. tensor_ee/segwater_inference.py +235 -0
  68. tensor_ee/segwater_preprocessing.py +182 -0
  69. tensor_ee/segwater_pytorch.py +249 -0
  70. tensor_ee/semantics.py +389 -0
  71. tensor_ee/smp.py +155 -0
  72. tensor_ee/swin.py +197 -0
  73. tensor_ee/swin_workflow.py +470 -0
  74. tensor_ee/tiling.py +287 -0
  75. tensor_ee/weight_assets.py +371 -0
  76. tensor_ee/weight_mapping.py +51 -0
  77. tensor_ee/weights/__init__.py +14 -0
  78. tensor_ee/weights/assets.py +160 -0
  79. tensor_ee/weights/constants.py +156 -0
  80. tensor_ee-0.1.0.dist-info/METADATA +172 -0
  81. tensor_ee-0.1.0.dist-info/RECORD +84 -0
  82. tensor_ee-0.1.0.dist-info/WHEEL +5 -0
  83. tensor_ee-0.1.0.dist-info/licenses/LICENSE +21 -0
  84. tensor_ee-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,602 @@
1
+ """The Swin encoder's composites in window layout: the MLP branch, the W-MSA and SW-MSA blocks,
2
+ the stage, the patch embedding at a configuration, the encoder grids and the encoder, with
3
+ their constants builders.
4
+
5
+ Moved from ``tensor_ee/models/swin_t224/encoder.py``, renamed here where the model's
6
+ name does not say Swin: ``BlockConstants`` -> ``SwinBlockConstants``, ``block_constants`` ->
7
+ ``swin_block_constants``, ``mlp_branch`` -> ``swin_mlp_branch``, ``stage_blocks`` ->
8
+ ``swin_stage_blocks``, ``merge_constants`` -> ``swin_merge_constants``,
9
+ ``patch_embedding_constants`` -> ``swin_patch_embedding_constants``, ``patch_embedding`` ->
10
+ ``swin_patch_embedding``, ``EncoderConstants`` -> ``SwinEncoderConstants``, ``EncoderGrids``
11
+ -> ``SwinEncoderGrids``, ``encoder_grids`` -> ``swin_encoder_grids`` and ``encoder`` ->
12
+ ``swin_encoder``; ``swin_block``, ``shifted_swin_block``, ``swin_stage``, ``DIM``, ``HEADS``
13
+ and ``EXTENSION_MODES`` keep their names. The Swin-T224 model re-exports these objects under
14
+ its former names; there is one implementation.
15
+
16
+ Every tensor is in window layout: one [w^2, dim] float32 array per w x w window ([49, dim]
17
+ at Swin-T224's window 7), on the coarse grid of its stage. A W-MSA block is per token
18
+ (LayerNorm, the projections, GELU, residuals) or within one window (attention), so it runs
19
+ on that raster with no layout conversion; an SW-MSA block re-partitions with the exact cyclic shift
20
+ (``tensor_ee.layouts.shift``) and adds Swin's shifted-window mask. Patch Merging is the
21
+ library's ``patch_merge``. Parameters are timm state_dict names (NumPy arrays embedded
22
+ inline, or asset-backed ``ServerArray``s).
23
+
24
+ **Configuration.** The composites take a ``SwinUPerNetConfig`` (``swin_upernet_config``;
25
+ default ``SWIN_T224``): ``swin_stage_blocks``, ``swin_merge_constants``,
26
+ ``swin_patch_embedding_constants``, ``swin_patch_embedding``, ``swin_encoder_grids`` and
27
+ ``SwinEncoderConstants`` take it as ``config``, and ``swin_encoder`` reads it from its constants
28
+ and grids, refusing a pair built for different configurations. A ``SwinBlockConstants`` records
29
+ its window, and the blocks run at that window, with the stage's shift (w // 2) in the SW-MSA
30
+ blocks. Parameters whose shapes break the configuration's channel contract are refused
31
+ (client-side; the graph is unchanged).
32
+
33
+ **Layouts.** ``swin_block_constants`` builds every per-channel constant for its ``layout``
34
+ (``WINDOW_LAYOUT``, ``WindowLayout(7)``: 49 rows, by default; the configuration's layout
35
+ through ``swin_stage_blocks``), recording the rows: the LayerNorm gamma and beta as
36
+ ``RowConstant``s and the projections' biases in ``LinearConstants``. The blocks run in that
37
+ layout and pass it to ``layer_norm`` and ``linear``, which refuse constants built for
38
+ another row count (client-side; the graph is unchanged). ``window_attention`` reads the
39
+ ``SwinBlockConstants`` shape (``qkv``, ``relative_bias``, ``dim``, ``heads``, ``head_dim``)
40
+ and its window.
41
+
42
+ **Policy** ``composite``: the blocks are called with their defaults: LayerNorm, Linear and
43
+ GELU in ``double_round_once``, the attention core in ``round_each_op``; residual additions are
44
+ one float32 rounding.
45
+
46
+ **Extension.** ``extension_mode="per_block"``, what the adapter runs and the only mode, extends
47
+ each SW-MSA block's input by one window (``periodic_extension``) before the block. The research
48
+ variants (the ``per_stage`` and ``propagated`` modes with their halo registry
49
+ ``EXTENSION_HALOS``, the fine-grid merge gather, the shifted block's ablations and the extra
50
+ materialization points) were removed when the research code was archived; they are in the research code archived at the
51
+ tag ``research-archive-2026-10-12``.
52
+
53
+ **Locality.** The MLP branch is per token and the W-MSA block reads its own window. The
54
+ SW-MSA block reads a ring of w - w // 2 tokens of its neighbours (the shifted windows straddle
55
+ them), and its mask marks the chip's last window row and column, so it is chip-edge
56
+ dependent. The stage and the encoder extend each SW-MSA block's input periodically, so an
57
+ edge window reads the opposite edge of the chip: ``EXTENT``, chip-edge dependent. The patch
58
+ embedding reads each token's patch (radius patch // 2).
59
+
60
+ **Cost.** ``METADATA``'s cost functions sum the parts' (``layer_norm``, ``linear``,
61
+ ``window_attention``, ``shifted_window_mask``, ``patch_merge``, ``patch_embed``).
62
+
63
+ **Validated.** Live only inside two whole models: Swin-T224 (window 7, shift 3, Swin-T
64
+ depths, heads and widths; 224 x 224, two bands) in the trained-model rehearsal against PyTorch
65
+ (docs/evidence/t06-rehearsal.json) and in the trained model's parity check
66
+ (docs/history/upernet/notes/milestone12_findings.md), and the window-8 Swin-UPerNet validation model (window 8, shift 4,
67
+ widths 48 to 384, heads 2 to 16, depths 2; 256 x 256, three bands;
68
+ docs/evidence/c1-capstone.json). Neither model runs the encoder's final norm; full Swin-encoder validation
69
+ validated the research encoder these came from, final norm included, at Swin-T's settings
70
+ (docs/history/swin_encoder/notes/milestone5_findings.md). Other configurations are validated
71
+ only where the blocks' statuses cover them (docs/building-blocks.md); the builders accept
72
+ more, such as other widths, which is not evidence that they work.
73
+ """
74
+
75
+ from __future__ import annotations
76
+
77
+ from dataclasses import dataclass
78
+
79
+ import numpy as np
80
+
81
+ from ...core.grid import TokenGrid
82
+ from ...core.metadata import COMPOSITE, BlockMetadata, Locality, Validated, validate_metadata
83
+ from ...layouts.shift import _resolve_shift, materialize, periodic_extension, shift_windows, unshift_windows
84
+ from ...weights.constants import ServerArray, WindowLayout, row_constant
85
+ from .activations import gelu
86
+ from .attention import METADATA as ATTENTION_METADATA
87
+ from .attention import relative_position_bias, shifted_window_mask, window_attention
88
+ from .linear import METADATA as LINEAR_METADATA
89
+ from .linear import WINDOW_LAYOUT, LinearConstants, linear
90
+ from .normalization import METADATA as NORM_METADATA
91
+ from .normalization import layer_norm
92
+ from .patch import METADATA as PATCH_METADATA
93
+ from .patch import (PatchEmbedConstants, PatchMergeConstants, patch_embed, patch_embed_constants, patch_embed_values,
94
+ patch_locality, patch_merge, patch_merge_constants, stage2_coarse)
95
+ from .swin_upernet_config import MLP_RATIO, SWIN_T224, SwinUPerNetConfig, check_shapes, stage_shifted
96
+
97
+ DIM, HEADS = SWIN_T224.stage_dims[0], SWIN_T224.num_heads[0]
98
+ """``SwinBlockConstants`` defaults: Swin-T stage 1 (96 channels, 3 heads)."""
99
+
100
+ EXTENSION_MODES = ("per_block",)
101
+ """The encoder's extension modes: per_block, the production mode (the only one)."""
102
+
103
+
104
+ # ---------------------------------------------------------------------------
105
+ # One Swin block (W-MSA) and its constants
106
+ # ---------------------------------------------------------------------------
107
+ @dataclass
108
+ class SwinBlockConstants:
109
+ """The block's parameters as EE constants of one path (value or image),
110
+ with the block's width and head count."""
111
+
112
+ norm1_weight: object
113
+ norm1_bias: object
114
+ qkv: LinearConstants
115
+ relative_bias: list
116
+ proj: LinearConstants
117
+ norm2_weight: object
118
+ norm2_bias: object
119
+ fc1: LinearConstants
120
+ fc2: LinearConstants
121
+ dim: int = DIM
122
+ heads: int = HEADS
123
+ window: int = SWIN_T224.window
124
+ """The window the constants serve (``layout.window`` in ``swin_block_constants``): the
125
+ blocks run at it."""
126
+
127
+ @property
128
+ def head_dim(self) -> int:
129
+ return self.dim // self.heads
130
+
131
+ @property
132
+ def layout(self) -> WindowLayout:
133
+ return WindowLayout(self.window)
134
+
135
+
136
+ def _shape(value) -> tuple:
137
+ return value.shape if isinstance(value, ServerArray) else np.shape(value)
138
+
139
+
140
+ def swin_block_constants(ops, parameters: dict, *, layout=WINDOW_LAYOUT) -> SwinBlockConstants:
141
+ """parameters: timm state_dict names -> float32 NumPy arrays or ServerArrays.
142
+
143
+ ``layout``: the window layout the block runs in (``WINDOW_LAYOUT``, window 7 and 49
144
+ rows, by default; ``swin_stage_blocks`` passes the configuration's). The LayerNorm
145
+ constants are ``RowConstant``s and the biases are repeated to its rows; the
146
+ relative-position bias uses its window, which the constants record
147
+ (``SwinBlockConstants.window``)."""
148
+ p = parameters
149
+ dim = _shape(p["norm1.weight"])[0]
150
+ heads = _shape(p["attn.relative_position_bias_table"])[1]
151
+ if dim % heads:
152
+ raise ValueError("dim must be a multiple of the head count")
153
+ return SwinBlockConstants(
154
+ norm1_weight=row_constant(ops, p["norm1.weight"], layout),
155
+ norm1_bias=row_constant(ops, p["norm1.bias"], layout),
156
+ qkv=LinearConstants(ops, p["attn.qkv.weight"], p["attn.qkv.bias"], layout),
157
+ relative_bias=[ops.wrap(b) for b in relative_position_bias(
158
+ ops.ee, p["attn.relative_position_bias_table"], layout.window)],
159
+ proj=LinearConstants(ops, p["attn.proj.weight"], p["attn.proj.bias"], layout),
160
+ norm2_weight=row_constant(ops, p["norm2.weight"], layout),
161
+ norm2_bias=row_constant(ops, p["norm2.bias"], layout),
162
+ fc1=LinearConstants(ops, p["mlp.fc1.weight"], p["mlp.fc1.bias"], layout),
163
+ fc2=LinearConstants(ops, p["mlp.fc2.weight"], p["mlp.fc2.bias"], layout),
164
+ dim=dim, heads=heads, window=layout.window,
165
+ )
166
+
167
+
168
+ def swin_mlp_branch(ops, x1, c: SwinBlockConstants) -> dict:
169
+ """The block's second residual: x1 + fc2(GELU(fc1(LN2(x1)))), with every intermediate."""
170
+ t = {"ln2": layer_norm(ops, x1, c.norm2_weight, c.norm2_bias, dim=c.dim, layout=c.layout)}
171
+ t["fc1"] = linear(ops, t["ln2"], c.fc1, layout=c.layout)
172
+ t["act"] = gelu(ops, t["fc1"])
173
+ t["fc2"] = linear(ops, t["act"], c.fc2, layout=c.layout)
174
+ t["out"] = x1.add(t["fc2"]) # float32 + float32 -> float32
175
+ return t
176
+
177
+
178
+ def swin_block(ops, x, c: SwinBlockConstants) -> dict:
179
+ """Every named intermediate of the block for x: float32 [w^2, dim] per window (w =
180
+ ``c.window``; 49 rows at window 7).
181
+
182
+ EE graphs are lazy: requesting one entry evaluates only what it needs, so
183
+ prefixes of the block (e.g. "qkv") are measured by requesting that entry.
184
+ """
185
+ t = {"ln1": layer_norm(ops, x, c.norm1_weight, c.norm1_bias, dim=c.dim, layout=c.layout)}
186
+ t.update(window_attention(ops, t["ln1"], c, window=c.window))
187
+ t["proj"] = linear(ops, t["concat"], c.proj, layout=c.layout)
188
+ t["x1"] = x.add(t["proj"]) # float32 + float32 -> float32
189
+ t.update(swin_mlp_branch(ops, t["x1"], c))
190
+ return t
191
+
192
+
193
+ # ---------------------------------------------------------------------------
194
+ # One shifted-window Swin block (SW-MSA)
195
+ # ---------------------------------------------------------------------------
196
+ def shifted_swin_block(ops, x, c: SwinBlockConstants, coarse, *, mask=None, shift: int | None = None) -> dict:
197
+ """Every named intermediate of the SW-MSA block for x: float32 [w^2, dim] per window
198
+ (window layout of the full raster's coarse grid `coarse`; w = c.window, dim = c.dim).
199
+
200
+ Keys: the Swin block's per-head names (q_h0, logits_h0, ...) in shifted
201
+ layout, plus "logits_biased_h{h}", "mask", "shifted", "reverse" and
202
+ "unshifted". x is materialized on the coarse grid first, and the shift
203
+ materializes its input (``shift.roll_windows``). mask: Swin's shifted-window
204
+ mask, built for `coarse`, the window and the shift when not given. shift: timm's
205
+ stage shift, ``c.window // 2`` (3 at window 7) when not given; it must satisfy
206
+ 0 < shift < window. The cyclic shift, the mask and the attention all run at
207
+ ``c.window``.
208
+ """
209
+ ee = ops.ee
210
+ window = c.window
211
+ shift = _resolve_shift(shift, window)
212
+ x = materialize(ee, x, coarse)
213
+ if mask is None:
214
+ mask = shifted_window_mask(ee, coarse, window=window, shift=shift)
215
+ t = {"x": x, "ln1": layer_norm(ops, x, c.norm1_weight, c.norm1_bias, dim=c.dim, layout=c.layout)}
216
+ t["shifted"] = shift_windows(ee, t["ln1"], coarse, c.dim, shift, window)
217
+ t.update(window_attention(ops, t["shifted"], c, mask, window=window))
218
+ t["mask"] = mask
219
+ t["proj"] = linear(ops, t["concat"], c.proj, layout=c.layout)
220
+ t["reverse"] = t["proj"]
221
+ t["unshifted"] = unshift_windows(ee, t["proj"], coarse, c.dim, shift, window)
222
+ t["x1"] = x.add(t["unshifted"]) # float32 + float32 -> float32
223
+ t.update(swin_mlp_branch(ops, t["x1"], c))
224
+ return t
225
+
226
+
227
+ # ---------------------------------------------------------------------------
228
+ # One stage
229
+ # ---------------------------------------------------------------------------
230
+ def swin_stage(ops, x, blocks: list, coarse, *, shifted: bool = True, shift: int | None = None) -> dict:
231
+ """Every named intermediate of the blocks (keys b{i}_<name>) and "out".
232
+
233
+ x: the stage input in window layout on the full raster's coarse grid
234
+ `coarse` ([w^2, dim] per window). Each SW-MSA block's input is first extended
235
+ by one window (``periodic_extension``: the per_block mode). shift: the SW-MSA
236
+ blocks' shift (``shifted_swin_block``: w // 2 when not given).
237
+ """
238
+ ee = ops.ee
239
+ t = {}
240
+ for i, c in enumerate(blocks):
241
+ if shifted and i % 2:
242
+ x = periodic_extension(ee, x, coarse, 1)
243
+ result = shifted_swin_block(ops, x, c, coarse, shift=shift)
244
+ else:
245
+ result = swin_block(ops, x, c)
246
+ t.update({f"b{i}_{name}": value for name, value in result.items()})
247
+ x = result["out"]
248
+ t["out"] = x
249
+ return t
250
+
251
+
252
+ # ---------------------------------------------------------------------------
253
+ # Encoder constants and grids
254
+ # ---------------------------------------------------------------------------
255
+ @dataclass
256
+ class SwinEncoderConstants:
257
+ blocks: list # [stage][block] SwinBlockConstants
258
+ merges: list # PatchMergeConstants pm1, pm2, pm3
259
+ norm_weight: object # [w^2, C4] (rows repeated)
260
+ norm_bias: object
261
+ config: SwinUPerNetConfig = SWIN_T224
262
+
263
+
264
+ def _group_shapes(config: SwinUPerNetConfig, group: str) -> dict:
265
+ return config.parameter_shapes()[0][group]
266
+
267
+
268
+ def swin_stage_blocks(ops, parameters: dict, s: int, count: int | None = None, *,
269
+ config: SwinUPerNetConfig = SWIN_T224) -> list:
270
+ """SwinBlockConstants of the first `count` blocks of stage s (``config.depths[s - 1]``
271
+ when not given), built for the configuration's window layout. Parameters whose shapes
272
+ differ from the configuration's (stage width, heads, window) are refused."""
273
+ depth = config.depths[s - 1]
274
+ count = depth if count is None else count
275
+ if not 0 <= count <= depth:
276
+ raise ValueError(f"stage {s} has {depth} blocks; received count {count}")
277
+ blocks = []
278
+ for b in range(count):
279
+ group = f"s{s}b{b}"
280
+ check_shapes(parameters[group], _group_shapes(config, group), group)
281
+ blocks.append(swin_block_constants(ops, parameters[group], layout=config.layout))
282
+ return blocks
283
+
284
+
285
+ def swin_merge_constants(ops, parameters: dict, n: int, *,
286
+ config: SwinUPerNetConfig = SWIN_T224) -> PatchMergeConstants:
287
+ """Patch Merging n (stage n -> n + 1) for the configuration's window layout; parameters
288
+ whose shapes differ from the configuration's are refused."""
289
+ group = f"pm{n}"
290
+ check_shapes(parameters[group], _group_shapes(config, group), group)
291
+ return patch_merge_constants(ops, parameters[group], config.layout)
292
+
293
+
294
+ def swin_patch_embedding_constants(ops, parameters: dict, *,
295
+ config: SwinUPerNetConfig = SWIN_T224) -> PatchEmbedConstants:
296
+ """The patch embedding's constants (timm ``patch_embed.*``: ``proj.weight`` [embed_dim,
297
+ bands x patch^2] flattened (c, ky, kx), ``proj.bias``, ``norm.weight``, ``norm.bias``) for
298
+ the configuration's window layout; other shapes are refused."""
299
+ check_shapes(parameters, config.parameter_shapes()[1], "patch_embed")
300
+ return patch_embed_constants(ops, parameters, rows=config.layout)
301
+
302
+
303
+ def swin_patch_embedding(ops, x, grid, c: PatchEmbedConstants, *, config: SwinUPerNetConfig = SWIN_T224) -> dict:
304
+ """The library's ``patch_embed`` at the configuration's patch, bands and window: x (the
305
+ bands, by name, on the model input grid ``grid``) -> {"pe_columns", "pe_patches",
306
+ "pe_proj", "x"}, "x" being the stage-1 input in window layout. ``grid`` must be the
307
+ configuration's model grid (``SwinUPerNetConfig.model_grid``), whose patches are the
308
+ encoder's stage-1 tokens; another grid is refused."""
309
+ expected = config.model_grid(grid.height, grid.width)
310
+ if grid != expected:
311
+ raise ValueError(f"{grid} is not the configuration's model grid {expected}")
312
+ return patch_embed(ops, x, grid, c, patch=config.patch, bands=config.bands, window=config.window)
313
+
314
+
315
+ @dataclass(frozen=True)
316
+ class SwinEncoderGrids:
317
+ """Token and window grids of every stage for rows x cols stage-1 windows."""
318
+
319
+ tokens: tuple # stage s token grid: 2^(s-1) x coarser than stage-1 tokens
320
+ coarse: tuple # stage s window grid (one [w^2, dim] window per pixel)
321
+ config: SwinUPerNetConfig = SWIN_T224
322
+
323
+ def shifted(self, s: int) -> bool:
324
+ return stage_shifted(self.coarse[s - 1].height, self.coarse[s - 1].width)
325
+
326
+
327
+ def swin_encoder_grids(rows: int, cols: int, *, config: SwinUPerNetConfig = SWIN_T224) -> SwinEncoderGrids:
328
+ """The grids of rows x cols stage-1 windows of ``config.window`` tokens (stage-1 tokens on
329
+ the default ``TokenGrid``: 10 m, EPSG:3857, origin (0, 0); ``swin_upernet_config.TOKEN_SCALE``)."""
330
+ config.check_windows(rows, cols)
331
+ window = config.window
332
+ first = TokenGrid(rows * window, cols * window)
333
+ tokens = tuple(first.coarse(2 ** s) if s else first for s in range(config.stages))
334
+ coarse = tuple(grid.coarse(window) for grid in tokens)
335
+ for s in range(1, config.stages):
336
+ if coarse[s] != stage2_coarse(coarse[s - 1]):
337
+ raise AssertionError("stage grids disagree")
338
+ return SwinEncoderGrids(tokens, coarse, config)
339
+
340
+
341
+ # ---------------------------------------------------------------------------
342
+ # The encoder
343
+ # ---------------------------------------------------------------------------
344
+ def _check_config(c: SwinEncoderConstants, g: SwinEncoderGrids, start: int, stop: int) -> SwinUPerNetConfig:
345
+ """The configuration ``c`` and ``g`` share, after refusing constants built for another one:
346
+ every block run must serve the configuration's window, width and heads, and every merge
347
+ its stage's width (client-side; the graph is unchanged)."""
348
+ config = c.config
349
+ if g.config != config:
350
+ raise ValueError(f"the encoder grids were built for {g.config}; the constants for {config}")
351
+ for s in range(start, stop + 1):
352
+ for b, block in enumerate(c.blocks[s - 1]):
353
+ if (block.window, block.dim, block.heads) != (config.window, config.stage_dims[s - 1],
354
+ config.num_heads[s - 1]):
355
+ raise ValueError(f"block s{s}b{b} was built for window {block.window}, width {block.dim} and "
356
+ f"{block.heads} heads; the configuration's stage {s} runs window {config.window}, "
357
+ f"width {config.stage_dims[s - 1]} and {config.num_heads[s - 1]} heads")
358
+ if s > 1 and c.merges[s - 2].in_dim != config.stage_dims[s - 2]:
359
+ raise ValueError(f"merge pm{s - 1} reads {c.merges[s - 2].in_dim} channels; the configuration's "
360
+ f"stage {s - 1} has {config.stage_dims[s - 2]}")
361
+ return config
362
+
363
+
364
+ def swin_encoder(ops, x, c: SwinEncoderConstants, g: SwinEncoderGrids, *, start: int = 1,
365
+ stop: int = SWIN_T224.stages, extension_mode: str = "per_block") -> dict:
366
+ """Every named intermediate (keys as encoder_reference): x, s{s}b{b}_*,
367
+ s{s}_out, pm{n}_*, norm. EE evaluates only what a requested tensor needs.
368
+
369
+ start, stop: the stages to run. start > 1 takes x as the output of stage
370
+ start - 1 (window layout on that stage's grid, e.g. read back from an
371
+ asset; key s{start-1}_out); the final norm follows stage 4.
372
+ extension_mode: "per_block" (the default and only mode: see the module
373
+ docstring).
374
+
375
+ The configuration is the constants' and the grids' (``SwinEncoderConstants.config``,
376
+ ``SwinEncoderGrids.config``); it is refused when they differ, or when a block or merge
377
+ was built for another window, width or head count. Patch Merging, the blocks and the
378
+ final norm run at its window, and the SW-MSA blocks with its shift (w // 2).
379
+ """
380
+ if extension_mode not in EXTENSION_MODES:
381
+ raise ValueError(f"extension_mode must be one of {EXTENSION_MODES}")
382
+ config = _check_config(c, g, start, stop)
383
+ t = {"x": x} if start == 1 else {f"s{start - 1}_out": x}
384
+ for s in range(start, stop + 1):
385
+ coarse, shifted = g.coarse[s - 1], g.shifted(s)
386
+ if s > 1:
387
+ pm = patch_merge(ops, x, c.merges[s - 2], g.coarse[s - 2], window=config.window)
388
+ t.update({f"pm{s - 1}_{k}": v for k, v in pm.items()})
389
+ x = pm["out"]
390
+ result = swin_stage(ops, x, c.blocks[s - 1], coarse, shifted=shifted, shift=config.shift)
391
+ t.update({f"s{s}{k}": v for k, v in result.items() if k != "out"})
392
+ x = result["out"]
393
+ t[f"s{s}_out"] = x
394
+ if stop == config.stages and c.norm_weight is not None:
395
+ # (a model without the final norm, e.g. UPerNet's FeatureListNet, passes None)
396
+ t["norm"] = layer_norm(ops, x, c.norm_weight, c.norm_bias, dim=config.norm_dim, layout=config.layout)
397
+ return t
398
+
399
+
400
+ # ---------------------------------------------------------------------------
401
+ # Cost functions (contract section 9): sums of the parts' METADATA cost functions
402
+ # ---------------------------------------------------------------------------
403
+ _PARTS = {**LINEAR_METADATA, **NORM_METADATA, **ATTENTION_METADATA, **PATCH_METADATA}
404
+ """The parts' entries by block name: a composite's cost is the sum of its parts' costs."""
405
+
406
+
407
+ def swin_mlp_branch_values(dim: int, *, layout=WINDOW_LAYOUT) -> int:
408
+ """norm2's gamma and beta rows, and fc1 [dim, 4 dim] and fc2 [4 dim, dim] with their bias
409
+ rows (timm's ``MLP_RATIO``); GELU and the residual addition embed nothing."""
410
+ hidden = MLP_RATIO * dim
411
+ linear_values = _PARTS["linear"].embedded_constants
412
+ return (_PARTS["layer_norm"].embedded_constants(dim, layout=layout)
413
+ + linear_values(dim, hidden, layout=layout) + linear_values(hidden, dim, layout=layout))
414
+
415
+
416
+ def swin_mlp_branch_arrays() -> int:
417
+ """Server arrays read: norm2's weight and bias, and fc1's and fc2's weights and biases."""
418
+ return _PARTS["layer_norm"].asset_arrays() + 2 * _PARTS["linear"].asset_arrays(bias=True)
419
+
420
+
421
+ def swin_block_values(dim: int, heads: int, *, window: int = SWIN_T224.window) -> int:
422
+ """norm1, the window attention (the QKV projection and the relative-position table), the
423
+ output projection and the MLP branch, each per-channel constant at the window's w^2 rows."""
424
+ layout = WindowLayout(window)
425
+ return (_PARTS["layer_norm"].embedded_constants(dim, layout=layout)
426
+ + _PARTS["window_attention"].embedded_constants(dim, heads, window=window)
427
+ + _PARTS["linear"].embedded_constants(dim, dim, layout=layout)
428
+ + swin_mlp_branch_values(dim, layout=layout))
429
+
430
+
431
+ def swin_block_arrays() -> int:
432
+ """Server arrays read: the block's 13 timm parameters (norm1, the attention's QKV and
433
+ relative-position table, the projection, and the MLP branch's)."""
434
+ return (_PARTS["layer_norm"].asset_arrays() + _PARTS["window_attention"].asset_arrays()
435
+ + _PARTS["linear"].asset_arrays(bias=True) + swin_mlp_branch_arrays())
436
+
437
+
438
+ def shifted_swin_block_values(dim: int, heads: int, *, window: int = SWIN_T224.window) -> int:
439
+ """The W-MSA block's constants and the shifted-window mask's, which embeds none (it is built
440
+ on the server from the grid)."""
441
+ return swin_block_values(dim, heads, window=window) + _PARTS["shifted_window_mask"].embedded_constants()
442
+
443
+
444
+ def shifted_swin_block_arrays() -> int:
445
+ """Server arrays read: the W-MSA block's; the mask reads none."""
446
+ return swin_block_arrays() + _PARTS["shifted_window_mask"].asset_arrays()
447
+
448
+
449
+ def swin_stage_values(dim: int, heads: int, depth: int, *, window: int = SWIN_T224.window) -> int:
450
+ """``depth`` blocks, W-MSA and SW-MSA alternating (the periodic extension embeds nothing)."""
451
+ return sum((shifted_swin_block_values if b % 2 else swin_block_values)(dim, heads, window=window)
452
+ for b in range(depth))
453
+
454
+
455
+ def swin_stage_arrays(*, depth: int) -> int:
456
+ """Server arrays read: ``depth`` blocks'."""
457
+ return sum(shifted_swin_block_arrays() if b % 2 else swin_block_arrays() for b in range(depth))
458
+
459
+
460
+ def swin_patch_embedding_values(*, config: SwinUPerNetConfig = SWIN_T224) -> int:
461
+ """``patch_embed``'s at the configuration's width, patch, bands and window layout."""
462
+ return _PARTS["patch_embed"].embedded_constants(config.embed_dim, patch=config.patch, channels=config.in_chans,
463
+ layout=config.layout)
464
+
465
+
466
+ def swin_patch_embedding_arrays() -> int:
467
+ """Server arrays read: ``patch_embed``'s."""
468
+ return _PARTS["patch_embed"].asset_arrays()
469
+
470
+
471
+ def swin_encoder_values(*, config: SwinUPerNetConfig = SWIN_T224, start: int = 1, stop: int | None = None,
472
+ norm: bool = True) -> int:
473
+ """Stages ``start`` to ``stop`` (the last when not given) of the configuration, each from
474
+ stage 2 with the Patch Merging before it, and the final norm after the last stage when
475
+ ``norm`` (UPerNet's encoder runs without it).
476
+
477
+ ``norm`` defaults to True, as ``swin_encoder`` runs the final norm whenever its constants
478
+ carry one: the default counts the encoder as built with it. The validated models run
479
+ without it (``METADATA["swin_encoder"].validated``: ``norm`` False), so their count is
480
+ ``norm=False``. The default stays: defaults are not changed (``AGENTS.md``), and callers
481
+ rely on it (tests/test_x01b_swin_upernet_composites.py counts the norm as the difference)."""
482
+ stop = config.stages if stop is None else stop
483
+ total = 0
484
+ for s in range(start, stop + 1):
485
+ if s > 1:
486
+ total += _PARTS["patch_merge"].embedded_constants(config.stage_dims[s - 2], layout=config.layout)
487
+ total += swin_stage_values(config.stage_dims[s - 1], config.num_heads[s - 1], config.depths[s - 1],
488
+ window=config.window)
489
+ if norm and stop == config.stages:
490
+ total += _PARTS["layer_norm"].embedded_constants(config.norm_dim, layout=config.layout)
491
+ return total
492
+
493
+
494
+ def swin_encoder_arrays(*, config: SwinUPerNetConfig = SWIN_T224, start: int = 1, stop: int | None = None,
495
+ norm: bool = True) -> int:
496
+ """Server arrays read: the stages', the Patch Mergings' and, when ``norm``, the final norm's.
497
+ ``norm`` defaults to True for the reason ``swin_encoder_values`` gives; the validated models
498
+ run without the final norm (``norm=False``)."""
499
+ stop = config.stages if stop is None else stop
500
+ total = sum(swin_stage_arrays(depth=config.depths[s - 1])
501
+ + (_PARTS["patch_merge"].asset_arrays() if s > 1 else 0) for s in range(start, stop + 1))
502
+ if norm and stop == config.stages:
503
+ total += _PARTS["layer_norm"].asset_arrays()
504
+ return total
505
+
506
+
507
+ # ---------------------------------------------------------------------------
508
+ # Metadata
509
+ # ---------------------------------------------------------------------------
510
+ # Validated live only inside two whole models: Swin-T224 (trained-model rehearsal against PyTorch, trained Swin-UPerNet validation
511
+ # against ONNX Runtime) and the window-8 Swin-UPerNet validation model.
512
+ _STATUS = "Fixed — Swin-T224 and the capstone: "
513
+ _WIDTHS = ("widths 96, 192, 384, 768, heads 3, 6, 12, 24 at window 7; "
514
+ "widths 48, 96, 192, 384, heads 2, 4, 8, 16 at window 8")
515
+ _EVIDENCE = ("docs/evidence/t06-rehearsal.json", "docs/history/upernet/notes/milestone12_findings.md",
516
+ "docs/evidence/c1-capstone.json")
517
+
518
+ # Validated settings: the two models' settings, named by the cost functions' parameters
519
+ # (dim, heads, depth, window) and SwinUPerNetConfig's fields, and joined per stage. The window-8 Swin-UPerNet validation model
520
+ # is tools/capstone.py's CONFIG (docs/evidence/c1-capstone.json).
521
+ _MODELS = ({"window": 7, "stage_dims": (96, 192, 384, 768), "num_heads": (3, 6, 12, 24), "depths": (2, 2, 6, 2),
522
+ "embed_dim": 96, "channels": 2},
523
+ {"window": 8, "stage_dims": (48, 96, 192, 384), "num_heads": (2, 4, 8, 16), "depths": (2, 2, 2, 2),
524
+ "embed_dim": 48, "channels": 3})
525
+ _STAGES = tuple({"dim": (dim,), "heads": (heads,), "depth": (depth,), "window": (model["window"],),
526
+ "shift": (model["window"] // 2, None), "shifted": (s < 3,)}
527
+ for model in _MODELS
528
+ for s, (dim, heads, depth) in enumerate(zip(model["stage_dims"], model["num_heads"], model["depths"])))
529
+ """Stage 4 is one window in both models (7 x 7 and 8 x 8 tokens), so timm's rule runs it unshifted."""
530
+
531
+
532
+ def _pick(*names, shifted=None):
533
+ """The stages' configurations reduced to ``names`` (only the shifted stages when asked)."""
534
+ return tuple({name: stage[name] for name in names} for stage in _STAGES
535
+ if shifted is None or stage["shifted"] == (shifted,))
536
+
537
+
538
+ METADATA = validate_metadata({
539
+ "swin_mlp_branch": BlockMetadata(
540
+ name="swin_mlp_branch", layout_in="W", layout_out="W", locality=Locality.POINTWISE,
541
+ policy=COMPOSITE, embedded_constants=swin_mlp_branch_values, asset_arrays=swin_mlp_branch_arrays,
542
+ status=_STATUS + "widths 96, 192, 384, 768 at window 7 and 48, 96, 192, 384 at window 8; hidden 4 × width",
543
+ evidence=_EVIDENCE + ("docs/history/swin_block/notes/findings.md",
544
+ "parts: layer_norm, linear and gelu (double_round_once), a float32 addition"),
545
+ validated=Validated(tuple({"dim": model["stage_dims"], "window": (model["window"],)} for model in _MODELS))),
546
+ "swin_block": BlockMetadata(
547
+ name="swin_block", layout_in="W", layout_out="W", locality=Locality.WINDOW,
548
+ policy=COMPOSITE, embedded_constants=swin_block_values, asset_arrays=swin_block_arrays,
549
+ status=_STATUS + _WIDTHS,
550
+ evidence=_EVIDENCE + ("docs/history/swin_block/notes/findings.md",
551
+ "parts: layer_norm, window_attention, linear, swin_mlp_branch"),
552
+ validated=Validated(_pick("dim", "heads", "window"))),
553
+ "shifted_swin_block": BlockMetadata(
554
+ # The shifted windows straddle their neighbours: after the roll by w // 2 and the
555
+ # unroll, an output window reads w - w // 2 tokens into them (4 at windows 7 and 8).
556
+ # Validated at stages 1-3 only: stage 4 is one window in both models (224 / 32 = 7 and
557
+ # 256 / 32 = 8 tokens), so timm never shifts it.
558
+ name="shifted_swin_block", layout_in="W", layout_out="W", locality=Locality.WINDOW_RING, radius=4,
559
+ policy=COMPOSITE, embedded_constants=shifted_swin_block_values, asset_arrays=shifted_swin_block_arrays,
560
+ chip_edge_dependent=True,
561
+ status=_STATUS + ("widths 96, 192, 384, heads 3, 6, 12 at window 7; widths 48, 96, 192, heads 2, 4, 8 "
562
+ "at window 8; shift ⌊w/2⌋; not stage 4 (one window, unshifted)"),
563
+ evidence=_EVIDENCE + ("docs/history/swin_pair/notes/findings.md",
564
+ "parts: layer_norm, shift_windows (none), window_attention with shifted_window_mask, "
565
+ "linear, swin_mlp_branch"),
566
+ validated=Validated(_pick("dim", "heads", "window", "shift", shifted=True))),
567
+ "swin_stage": BlockMetadata(
568
+ name="swin_stage", layout_in="W", layout_out="W", locality=Locality.EXTENT,
569
+ policy=COMPOSITE, embedded_constants=swin_stage_values, asset_arrays=swin_stage_arrays,
570
+ chip_edge_dependent=True, status=_STATUS + _WIDTHS + "; depths 2, 2, 6, 2 and 2, 2, 2, 2",
571
+ evidence=_EVIDENCE + ("docs/history/swin_encoder/notes/milestone5_findings.md",
572
+ "parts: swin_block, periodic_extension (none), shifted_swin_block"),
573
+ validated=Validated(_pick("dim", "heads", "depth", "window", "shift", "shifted"))),
574
+ "swin_patch_embedding": BlockMetadata(
575
+ # patch // 2 at the validated patch 4 (patch_embed's own radius 4 covers the validated patch size 8)
576
+ name="swin_patch_embedding", layout_in="R", layout_out="W", locality=Locality.KERNEL, radius=2,
577
+ policy=COMPOSITE, embedded_constants=swin_patch_embedding_values, asset_arrays=swin_patch_embedding_arrays,
578
+ status=_STATUS + "patch 4; 2 bands → width 96 at window 7, 3 bands → width 48 at window 8",
579
+ evidence=_EVIDENCE + ("docs/history/swin_image_encoder/notes/milestone6_findings.md",
580
+ "parts: patch_embed (double_round_once)"),
581
+ # channels: the number of bands (patch_embed_values'; config.in_chans), the names being labels.
582
+ validated=Validated(tuple({"patch": (4,), "channels": (model["channels"],), "embed_dim": (model["embed_dim"],),
583
+ "window": (model["window"],)} for model in _MODELS),
584
+ sources=(SwinUPerNetConfig, patch_embed_values)),
585
+ per_setting=patch_locality),
586
+ "swin_encoder": BlockMetadata(
587
+ name="swin_encoder", layout_in="W", layout_out="W", locality=Locality.EXTENT,
588
+ policy=COMPOSITE, embedded_constants=swin_encoder_values, asset_arrays=swin_encoder_arrays,
589
+ chip_edge_dependent=True,
590
+ status=_STATUS + "8 × 8 stage-1 windows of 7 (stages 1–3, then 4) and of 8 (stages 1–4), "
591
+ "without the final norm",
592
+ evidence=_EVIDENCE + ("docs/history/swin_encoder/notes/milestone5_findings.md",
593
+ "parts: patch_merge, swin_stage, layer_norm"),
594
+ # rows x cols stage-1 windows (swin_encoder_grids); norm=False: the cost functions' name for
595
+ # constants without the final norm (UPerNet's FeatureListNet), which both models run.
596
+ validated=Validated(tuple(
597
+ {"window": (model["window"],), "stage_dims": (model["stage_dims"],), "num_heads": (model["num_heads"],),
598
+ "depths": (model["depths"],), "rows": (8,), "cols": (8,), "start": (start,), "stop": (stop,),
599
+ "norm": (False,)}
600
+ for model, start, stop in ((_MODELS[0], 1, 3), (_MODELS[0], 4, 4), (_MODELS[1], 1, 4))),
601
+ sources=(SwinUPerNetConfig, swin_encoder_grids))),
602
+ })