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.
- tensor_ee/__init__.py +28 -0
- tensor_ee/_swin_parameters.py +304 -0
- tensor_ee/blocks/__init__.py +0 -0
- tensor_ee/blocks/raster/__init__.py +36 -0
- tensor_ee/blocks/raster/activations.py +183 -0
- tensor_ee/blocks/raster/conv.py +183 -0
- tensor_ee/blocks/raster/merge.py +66 -0
- tensor_ee/blocks/raster/outputs.py +211 -0
- tensor_ee/blocks/raster/pooling.py +182 -0
- tensor_ee/blocks/raster/resample.py +64 -0
- tensor_ee/blocks/window/__init__.py +44 -0
- tensor_ee/blocks/window/activations.py +54 -0
- tensor_ee/blocks/window/attention.py +366 -0
- tensor_ee/blocks/window/conv.py +323 -0
- tensor_ee/blocks/window/linear.py +129 -0
- tensor_ee/blocks/window/merge.py +67 -0
- tensor_ee/blocks/window/normalization.py +197 -0
- tensor_ee/blocks/window/patch.py +362 -0
- tensor_ee/blocks/window/pooling.py +275 -0
- tensor_ee/blocks/window/resample.py +286 -0
- tensor_ee/blocks/window/segformer.py +718 -0
- tensor_ee/blocks/window/swin.py +602 -0
- tensor_ee/blocks/window/swin_upernet_config.py +361 -0
- tensor_ee/blocks/window/upernet.py +1016 -0
- tensor_ee/comparison.py +213 -0
- tensor_ee/compatibility.py +180 -0
- tensor_ee/compiler.py +356 -0
- tensor_ee/converter/__init__.py +72 -0
- tensor_ee/converter/build.py +120 -0
- tensor_ee/converter/convert.py +388 -0
- tensor_ee/converter/export.py +846 -0
- tensor_ee/converter/graph.py +417 -0
- tensor_ee/converter/operations.py +790 -0
- tensor_ee/converter/plan.py +675 -0
- tensor_ee/converter/recognizers.py +1273 -0
- tensor_ee/converter/request.py +146 -0
- tensor_ee/converter/run.py +950 -0
- tensor_ee/core/__init__.py +8 -0
- tensor_ee/core/errors.py +25 -0
- tensor_ee/core/grid.py +323 -0
- tensor_ee/core/metadata.py +398 -0
- tensor_ee/core/ops.py +336 -0
- tensor_ee/core/precision.py +133 -0
- tensor_ee/core/tensor.py +129 -0
- tensor_ee/examples/__init__.py +1 -0
- tensor_ee/examples/nonwater.py +251 -0
- tensor_ee/layouts/__init__.py +17 -0
- tensor_ee/layouts/boundaries.py +103 -0
- tensor_ee/layouts/raster.py +117 -0
- tensor_ee/layouts/shift.py +226 -0
- tensor_ee/layouts/window.py +201 -0
- tensor_ee/models/__init__.py +16 -0
- tensor_ee/models/fixtures.py +119 -0
- tensor_ee/models/segformer_b0/__init__.py +9 -0
- tensor_ee/models/segformer_b0/configuration.py +209 -0
- tensor_ee/models/segformer_b0/decoder.py +130 -0
- tensor_ee/models/segformer_b0/encoder.py +90 -0
- tensor_ee/models/swin_t224/__init__.py +30 -0
- tensor_ee/models/swin_t224/configuration.py +103 -0
- tensor_ee/models/swin_t224/decoder.py +53 -0
- tensor_ee/models/swin_t224/encoder.py +50 -0
- tensor_ee/onnx_import.py +431 -0
- tensor_ee/outputs.py +16 -0
- tensor_ee/primitives.py +110 -0
- tensor_ee/receptive_field.py +257 -0
- tensor_ee/resnet_models.py +185 -0
- tensor_ee/segwater_inference.py +235 -0
- tensor_ee/segwater_preprocessing.py +182 -0
- tensor_ee/segwater_pytorch.py +249 -0
- tensor_ee/semantics.py +389 -0
- tensor_ee/smp.py +155 -0
- tensor_ee/swin.py +197 -0
- tensor_ee/swin_workflow.py +470 -0
- tensor_ee/tiling.py +287 -0
- tensor_ee/weight_assets.py +371 -0
- tensor_ee/weight_mapping.py +51 -0
- tensor_ee/weights/__init__.py +14 -0
- tensor_ee/weights/assets.py +160 -0
- tensor_ee/weights/constants.py +156 -0
- tensor_ee-0.1.0.dist-info/METADATA +172 -0
- tensor_ee-0.1.0.dist-info/RECORD +84 -0
- tensor_ee-0.1.0.dist-info/WHEEL +5 -0
- tensor_ee-0.1.0.dist-info/licenses/LICENSE +21 -0
- tensor_ee-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"""The window-layout 3 x 3 convolution and its input-channel groups.
|
|
2
|
+
|
|
3
|
+
Moved from ``upernet/earth_engine/conv2d.py`` (``Conv3x3Constants``, ``conv3x3_window``
|
|
4
|
+
and its helper ``_finish``) and ``upernet/earth_engine/fusion.py`` (``group_weight``,
|
|
5
|
+
``group_product``), research code archived at the tag ``research-archive-2026-10-12``
|
|
6
|
+
(docs/history/README.md). The dense formulations (``dense_im2col``, ``conv3x3_dense``,
|
|
7
|
+
``conv_convolve``) were research only; the fusion composites are with the model.
|
|
8
|
+
|
|
9
|
+
Inference Conv2d (stride 1, dilation 1, groups 1, zero padding k // 2), PyTorch
|
|
10
|
+
semantics (weight [Cout, Cin, kh, kw], padding p = k // 2):
|
|
11
|
+
|
|
12
|
+
y[o, i, j] = b[o] + sum_{c, ky, kx} W[o, c, ky, kx] * x_pad[c, i + ky, j + kx]
|
|
13
|
+
|
|
14
|
+
i.e. an im2col matrix product: the zero-padded k x k neighbourhood of each token as a
|
|
15
|
+
row in (ky, kx, c) order times W^T [k*k*Cin, Cout] in the same row order (the weight
|
|
16
|
+
permutation (o, c, ky, kx) -> (ky, kx, c, o) is applied once, to the constant). In
|
|
17
|
+
window layout the neighbourhood is the [w + 2, w + 2, C] halo of each window (its
|
|
18
|
+
one-token ring from the 8 neighbouring windows, zeros outside the raster):
|
|
19
|
+
``tensor_ee.layouts.window.halo`` and ``im2col``.
|
|
20
|
+
|
|
21
|
+
**Policy** ``double_round_once``: EE's matrixMultiply accumulates every dot product
|
|
22
|
+
sequentially in double; the bias is added in double and the sum rounded to float32
|
|
23
|
+
once. ``group_product`` leaves its partial in double (no rounding): the fusion's
|
|
24
|
+
``grouped_double`` variant sums the four group partials in double before its one
|
|
25
|
+
rounding, so ``group_product`` accepts both ``double_round_once`` and
|
|
26
|
+
``grouped_double``.
|
|
27
|
+
|
|
28
|
+
The research ``window_taps`` method (nine [N, Cin] x [Cin, Cout] products summed in
|
|
29
|
+
double, with per-tap constants) was removed when the research code was archived; the im2col product is the only form.
|
|
30
|
+
Kernel 3, window 7 and 4 groups are the defaults. The 3 x 3 kernel is the window
|
|
31
|
+
convolution's identity, not a setting: other kernels are refused. ``conv3x3_window`` is
|
|
32
|
+
validated at windows 5, 7, 8 and 12. ``group_product`` is validated at 1, 2, 4 and 8
|
|
33
|
+
contiguous input-channel groups, at windows 7 and 12, and at 4 groups at window 8 (the
|
|
34
|
+
window-8 Swin-UPerNet validation model's fusion), its partials summed in double in group order and rounded once by
|
|
35
|
+
the caller, against the unsplit convolution. Other group counts and windows are not
|
|
36
|
+
validated.
|
|
37
|
+
|
|
38
|
+
``depthwise_conv3x3_window`` computes y[d] = bias[d] + sum_t x[t,d] weight[d,t].
|
|
39
|
+
Its halo/im2col taps are [rows,9,D], multiplied once in double and reduced over
|
|
40
|
+
the nine taps in double, with one final float32 rounding. Weights and optional
|
|
41
|
+
bias are pre-repeated to the layout rows. Live coverage is window7, D4 with/without
|
|
42
|
+
bias, D1024 with bias, and D16 with bias within the C4/D16 Mix-FFN fixture.
|
|
43
|
+
Other widths/windows and ServerArray asset transport have no live coverage here.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
import operator
|
|
49
|
+
|
|
50
|
+
import numpy as np
|
|
51
|
+
|
|
52
|
+
from ...core.errors import reject
|
|
53
|
+
from ...core.metadata import BlockMetadata, Locality, Validated, validate_metadata
|
|
54
|
+
from ...core.ops import WindowOps
|
|
55
|
+
from ...core.precision import DOUBLE_ROUND_ONCE, GROUPED_DOUBLE, validate_policy
|
|
56
|
+
from ...core.tensor import WINDOW
|
|
57
|
+
from ...layouts.window import halo, im2col
|
|
58
|
+
from ...weights.constants import ServerArray, WindowLayout, check_rows, constant_value
|
|
59
|
+
from .linear import WINDOW_LAYOUT, builder_rows
|
|
60
|
+
|
|
61
|
+
KERNEL = 3
|
|
62
|
+
"""The window convolution's kernel: its identity (other kernels are refused)."""
|
|
63
|
+
|
|
64
|
+
GROUPS = 4
|
|
65
|
+
"""Default input-channel groups of the fusion convolution (the model's four levels). 1, 2,
|
|
66
|
+
4 and 8 groups are validated."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _depthwise_parameter(value, name):
|
|
70
|
+
"""Validate coefficient transport before building any new depthwise expression."""
|
|
71
|
+
if isinstance(value, ServerArray):
|
|
72
|
+
shape = value.shape
|
|
73
|
+
if not isinstance(shape, (tuple, list)) or any(
|
|
74
|
+
isinstance(n, bool) or not isinstance(n, (int, np.integer)) or n < 1 for n in shape):
|
|
75
|
+
reject(f"depthwise {name} shape must contain positive integer dimensions; received {shape!r}")
|
|
76
|
+
return value
|
|
77
|
+
try:
|
|
78
|
+
with np.errstate(over="ignore", invalid="ignore"):
|
|
79
|
+
array = np.asarray(value, dtype=np.float32)
|
|
80
|
+
except (TypeError, ValueError):
|
|
81
|
+
reject(f"depthwise {name} must contain float32 coefficients")
|
|
82
|
+
if not np.isfinite(array).all():
|
|
83
|
+
reject(f"depthwise {name} coefficients must be finite float32 values")
|
|
84
|
+
return array
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class DepthwiseConv3x3Constants:
|
|
88
|
+
"""Depthwise [D,1,3,3] NumPy or [D,9] ServerArray weights, pre-repeated
|
|
89
|
+
as [rows,9,D] in double; optional [D] bias repeated as [rows,D].
|
|
90
|
+
|
|
91
|
+
Only kernel3, stride1, padding1, dilation1 and multiplier1 are supported.
|
|
92
|
+
``rows`` is the window layout or its positive row count; consumption checks it.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, ops, weight, bias=None, rows=WINDOW_LAYOUT, *, k=3,
|
|
96
|
+
stride=1, padding=1, dilation=1, multiplier=1,
|
|
97
|
+
policy: str = DOUBLE_ROUND_ONCE):
|
|
98
|
+
self.policy = validate_policy(policy, allowed=DOUBLE_ROUND_ONCE)
|
|
99
|
+
for name, value, expected in (("k", k, 3), ("stride", stride, 1),
|
|
100
|
+
("padding", padding, 1), ("dilation", dilation, 1),
|
|
101
|
+
("multiplier", multiplier, 1)):
|
|
102
|
+
if isinstance(value, bool) or not isinstance(value, (int, np.integer)) or value != expected:
|
|
103
|
+
reject(f"depthwise_conv3x3_window requires {name}={expected}; received {value!r}")
|
|
104
|
+
weight = _depthwise_parameter(weight, "weight")
|
|
105
|
+
if bias is not None:
|
|
106
|
+
bias = _depthwise_parameter(bias, "bias")
|
|
107
|
+
shape = weight.shape
|
|
108
|
+
expected = (9,) if isinstance(weight, ServerArray) else (1, 3, 3)
|
|
109
|
+
if len(shape) != len(expected) + 1 or tuple(shape[1:]) != expected or shape[0] < 1:
|
|
110
|
+
reject(f"depthwise weight must be [D,9] ServerArray or [D,1,3,3] NumPy; received {shape}")
|
|
111
|
+
self.channels = int(shape[0])
|
|
112
|
+
if bias is not None and tuple(bias.shape if isinstance(bias, ServerArray) else np.shape(bias)) != (self.channels,):
|
|
113
|
+
reject(f"depthwise bias must be [{self.channels}]")
|
|
114
|
+
self.rows = builder_rows(rows)
|
|
115
|
+
source = constant_value(ops.ee, weight)
|
|
116
|
+
repeated = source.reshape([self.channels, 9]).transpose().reshape([1, 9, self.channels]).repeat(0, self.rows)
|
|
117
|
+
self.weight = ops.to_double(ops.wrap(repeated))
|
|
118
|
+
self.bias = None if bias is None else ops.constant(bias, repeat_rows=self.rows)
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def embedded_values(self) -> int:
|
|
122
|
+
return depthwise_conv3x3_values(self.channels, bias=self.bias is not None, layout=self.rows)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def depthwise_conv3x3_window(ops, x, c: DepthwiseConv3x3Constants, coarse, *,
|
|
126
|
+
window: int = WINDOW, policy: str = DOUBLE_ROUND_ONCE):
|
|
127
|
+
"""W [window²,D] → W [window²,D], finite-chip zero padding, radius1.
|
|
128
|
+
|
|
129
|
+
One double product and one nine-tap double reduction, optional double bias
|
|
130
|
+
addition, then one float32 output cast. Live window7 coverage: D4 optional
|
|
131
|
+
bias, D1024 biased, D16 biased within C4/D16 Mix-FFN. Constants for mismatched
|
|
132
|
+
rows/policy are refused; other widths/windows are not live validated here.
|
|
133
|
+
"""
|
|
134
|
+
validate_policy(policy, allowed=DOUBLE_ROUND_ONCE)
|
|
135
|
+
if c.policy != policy:
|
|
136
|
+
reject("depthwise constants and block precision policies differ")
|
|
137
|
+
check_rows(c, WindowLayout(window))
|
|
138
|
+
neighbourhood = halo(ops.ee, x, coarse, c.channels, window=window, ring=1)
|
|
139
|
+
columns = im2col(ops.ee, neighbourhood, c.channels, 3, window=window)
|
|
140
|
+
taps = ops.reshape(ops.to_double(columns), [c.rows, 9, c.channels])
|
|
141
|
+
product = taps.multiply(c.weight)
|
|
142
|
+
# row_total reduces axis1: put all nine taps in that axis, one row per token/channel.
|
|
143
|
+
products = ops.reshape(ops.transpose(product, 1, 2), [c.rows * c.channels, 9])
|
|
144
|
+
total = ops.reshape(ops.row_total(products), [c.rows, c.channels])
|
|
145
|
+
return ops.to_float(total if c.bias is None else total.add(c.bias))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class Conv3x3Constants:
|
|
149
|
+
"""W^T of a k x k convolution in (ky, kx, c) row order, [k*k*Cin, Cout]; the bias with
|
|
150
|
+
rows repeated (None: bias-free conv).
|
|
151
|
+
|
|
152
|
+
weight: NumPy [Cout, Cin, k, k] or a ServerArray [Cout, Cin * k * k] in PyTorch's
|
|
153
|
+
flatten order (c, ky, kx) (the asset format); the permutation runs on the server.
|
|
154
|
+
``rows``: the layout the bias serves (default ``WindowLayout(7)``: 49 rows) or its
|
|
155
|
+
row count; recorded as ``rows``."""
|
|
156
|
+
|
|
157
|
+
def __init__(self, ops, weight, bias=None, rows=WINDOW_LAYOUT, k: int = KERNEL):
|
|
158
|
+
ee = ops.ee
|
|
159
|
+
if isinstance(weight, ServerArray):
|
|
160
|
+
cout, flat = weight.shape
|
|
161
|
+
if flat % (k * k):
|
|
162
|
+
raise ValueError(f"a [Cout, Cin * {k * k}] weight cannot have {flat} columns (kernel {k})")
|
|
163
|
+
cin = flat // (k * k)
|
|
164
|
+
w = (weight.array.reshape([cout, cin, k * k]).transpose(1, 2) # [Cout, kk, Cin]
|
|
165
|
+
.reshape([cout, k * k * cin]).transpose()) # [kk Cin, Cout]
|
|
166
|
+
else:
|
|
167
|
+
weight = np.asarray(weight, dtype=np.float32)
|
|
168
|
+
cout, cin = weight.shape[:2]
|
|
169
|
+
w = constant_value(ee, weight.transpose(2, 3, 1, 0).reshape(k * k * cin, cout))
|
|
170
|
+
self.k, self.cin, self.cout = k, cin, cout
|
|
171
|
+
self.rows = builder_rows(rows)
|
|
172
|
+
self.weight_t = ops.wrap(w)
|
|
173
|
+
self.bias = None if bias is None else ops.constant(bias, repeat_rows=self.rows)
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def embedded_values(self) -> int:
|
|
177
|
+
bias_rows = 0 if self.bias is None else self.cout
|
|
178
|
+
return self.k * self.k * self.cin * self.cout + bias_rows
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _finish(ops, product, c: Conv3x3Constants):
|
|
182
|
+
return ops.to_float(product if c.bias is None else product.add(c.bias))
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _check_kernel(c: Conv3x3Constants) -> None:
|
|
186
|
+
"""The window convolution reads a one-token ring (``halo``) and taps at offsets -1, 0
|
|
187
|
+
and 1 (``taps``): its kernel is 3 x 3, the block's identity. Constants built for another
|
|
188
|
+
kernel are refused (client-side; the graph is unchanged)."""
|
|
189
|
+
if c.k != KERNEL:
|
|
190
|
+
raise ValueError(f"the window convolution is {KERNEL} x {KERNEL}; constants were built for k = {c.k}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def conv3x3_window(ops, x, c: Conv3x3Constants, coarse, *, window: int = WINDOW,
|
|
194
|
+
policy: str = DOUBLE_ROUND_ONCE):
|
|
195
|
+
"""Window layout [w^2, Cin] on the window grid `coarse` -> [w^2, Cout] (zero padding at
|
|
196
|
+
the raster edge, exactly PyTorch's). Constants built for another row count than
|
|
197
|
+
``window ** 2`` are refused, and so are constants for another kernel than 3 x 3, the
|
|
198
|
+
block's identity. Validated at windows 5, 7, 8 and 12."""
|
|
199
|
+
validate_policy(policy, allowed=DOUBLE_ROUND_ONCE)
|
|
200
|
+
_check_kernel(c)
|
|
201
|
+
check_rows(c, WindowLayout(window))
|
|
202
|
+
ee = ops.ee
|
|
203
|
+
halo3 = halo(ee, x, coarse, c.cin, window=window)
|
|
204
|
+
return _finish(ops, ops.matmul_double(im2col(ee, halo3, c.cin, c.k, window=window), c.weight_t), c)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def group_weight(weight, g: int, groups: int = GROUPS):
|
|
208
|
+
"""W_g, input-channel group g of a [Cout, Cin, 3, 3] weight: channels
|
|
209
|
+
[g Cin / groups, (g + 1) Cin / groups), the g-th of ``groups`` contiguous groups in
|
|
210
|
+
channel order. As an asset ServerArray [Cout, Cin * 9] in PyTorch's flatten order
|
|
211
|
+
(c, ky, kx), group g is the contiguous column slice [g Cin * 9 / groups,
|
|
212
|
+
(g + 1) Cin * 9 / groups).
|
|
213
|
+
|
|
214
|
+
``groups`` must divide Cin and 0 <= g < groups; otherwise the slice would drop or share
|
|
215
|
+
channels, so it is refused (client-side; the graph is unchanged)."""
|
|
216
|
+
groups, g = operator.index(groups), operator.index(g)
|
|
217
|
+
if groups < 1 or not 0 <= g < groups:
|
|
218
|
+
raise ValueError(f"group {g} of {groups} groups does not exist")
|
|
219
|
+
if isinstance(weight, ServerArray):
|
|
220
|
+
cout, flat = weight.shape
|
|
221
|
+
if flat % (KERNEL * KERNEL) or (flat // (KERNEL * KERNEL)) % groups:
|
|
222
|
+
raise ValueError(f"{groups} groups do not split the {flat} columns of a "
|
|
223
|
+
f"[Cout, Cin * {KERNEL * KERNEL}] weight into whole channels")
|
|
224
|
+
width = flat // groups
|
|
225
|
+
return ServerArray(weight.array.slice(1, g * width, (g + 1) * width), (cout, width))
|
|
226
|
+
w = np.asarray(weight, np.float32)
|
|
227
|
+
if w.ndim != 4 or w.shape[2:] != (KERNEL, KERNEL):
|
|
228
|
+
raise ValueError(f"expected a [Cout, Cin, {KERNEL}, {KERNEL}] weight; received {w.shape}")
|
|
229
|
+
if w.shape[1] % groups:
|
|
230
|
+
raise ValueError(f"{groups} groups do not split {w.shape[1]} input channels evenly")
|
|
231
|
+
width = w.shape[1] // groups
|
|
232
|
+
return w[:, g * width:(g + 1) * width]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def group_product(ee, part, w: Conv3x3Constants, *, policy: str = DOUBLE_ROUND_ONCE):
|
|
236
|
+
"""X_g's [w^2, 9 C_g] im2col times W_g^T, left in double (no rounding).
|
|
237
|
+
|
|
238
|
+
``part`` is a W tensor (the decoder's ``Feature``); its window gives the halo and
|
|
239
|
+
the rows the constants must have. Policy ``double_round_once`` (the default) or its
|
|
240
|
+
``grouped_double`` variant: the caller rounds once, after summing the groups. Validated
|
|
241
|
+
at 1, 2, 4 and 8 groups (``group_weight``) at windows 7 and 12, and at 4 groups at
|
|
242
|
+
window 8, with the partials summed in double in group order and rounded once."""
|
|
243
|
+
validate_policy(policy, allowed=(DOUBLE_ROUND_ONCE, GROUPED_DOUBLE))
|
|
244
|
+
_check_kernel(w)
|
|
245
|
+
check_rows(w, part)
|
|
246
|
+
img = WindowOps(ee)
|
|
247
|
+
halo3 = halo(ee, part.image, part.grid.coarse(part.window), w.cin, window=part.window)
|
|
248
|
+
return img.matmul_double(im2col(ee, halo3, w.cin, w.k, window=part.window), w.weight_t)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
# Cost functions (contract section 9)
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
def conv3x3_values(cin: int, cout: int, *, k: int = KERNEL, bias: bool = False, layout=WINDOW_LAYOUT) -> int:
|
|
255
|
+
"""Values of the constants: W^T [k*k*Cin, Cout], plus the bias rows [rows, Cout] (the
|
|
256
|
+
research budgets' convention; ``Conv3x3Constants.embedded_values`` counts a bias as
|
|
257
|
+
Cout values, before the server-side repeat)."""
|
|
258
|
+
return k * k * cin * cout + (builder_rows(layout) * cout if bias else 0)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def conv3x3_arrays(*, bias: bool = False) -> int:
|
|
262
|
+
"""Server arrays read for ServerArray weights: the weight, and the bias if any."""
|
|
263
|
+
return 2 if bias else 1
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def group_product_values(cin: int, cout: int, *, k: int = KERNEL) -> int:
|
|
267
|
+
"""Values of one group's W_g^T [k*k*C_g, Cout] (C_g = Cin / groups): the groups
|
|
268
|
+
together embed the weight once, as column slices."""
|
|
269
|
+
return k * k * cin * cout
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def group_product_arrays() -> int:
|
|
273
|
+
"""Server arrays read: the group's column slice of the one weight array."""
|
|
274
|
+
return 1
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def depthwise_conv3x3_values(channels: int, *, bias: bool = True, layout=WINDOW_LAYOUT) -> int:
|
|
278
|
+
"""Evaluated repeated weights [rows,9,D] and optional bias [rows,D]."""
|
|
279
|
+
return builder_rows(layout) * channels * (9 + int(bias))
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def depthwise_conv3x3_arrays(*, bias: bool = True) -> int:
|
|
283
|
+
"""Source ServerArrays: one weight and optional one bias; repeats are expressions."""
|
|
284
|
+
return 2 if bias else 1
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
METADATA = validate_metadata({
|
|
288
|
+
"depthwise_conv3x3_window": BlockMetadata(
|
|
289
|
+
name="depthwise_conv3x3_window", layout_in="W", layout_out="W",
|
|
290
|
+
locality=Locality.WINDOW_RING, radius=1,
|
|
291
|
+
policy=DOUBLE_ROUND_ONCE, embedded_constants=depthwise_conv3x3_values,
|
|
292
|
+
asset_arrays=depthwise_conv3x3_arrays,
|
|
293
|
+
status="Ready — 3 × 3, stride1, padding1, dilation1, multiplier1; window7; D4 optional bias, D1024 biased; D16 biased in C4/D16 Mix-FFN",
|
|
294
|
+
evidence=("docs/evidence/segformer_b0-offline.json", "docs/evidence/segformer_b0-live.json"),
|
|
295
|
+
# Validated: the status's form at window 7, with the channel counts (D) and bias it names.
|
|
296
|
+
validated=Validated(tuple(
|
|
297
|
+
{"k": (3,), "stride": (1,), "padding": (1,), "dilation": (1,), "multiplier": (1,), "window": (7,),
|
|
298
|
+
"channels": channels, "bias": bias}
|
|
299
|
+
for channels, bias in (((4,), (True, False)), ((16, 1024), (True,)))),
|
|
300
|
+
sources=(DepthwiseConv3x3Constants,))),
|
|
301
|
+
"conv3x3_window": BlockMetadata(
|
|
302
|
+
name="conv3x3_window", layout_in="W", layout_out="W", locality=Locality.WINDOW_RING, radius=1,
|
|
303
|
+
policy=DOUBLE_ROUND_ONCE, embedded_constants=conv3x3_values, asset_arrays=conv3x3_arrays,
|
|
304
|
+
status="Ready — 3 × 3 kernel, stride 1; windows 5, 7, 8, 12",
|
|
305
|
+
evidence=("docs/history/building-blocks-2026-10-11.md#33-convolution", "docs/history/upernet/notes/milestone7_findings.md",
|
|
306
|
+
"docs/evidence/p01-offline.json",
|
|
307
|
+
"docs/evidence/live-fixtures/20261013T074301Z-p01a.json", "docs/evidence/live-fixtures/20261013T074330Z-p01b.json", "docs/evidence/live-fixtures/20261013T075542Z-window-relative_position_bias-w12.json",
|
|
308
|
+
"docs/evidence/live-fixtures/20261013T115558Z-p04.json"), # decoder-setting validation live record
|
|
309
|
+
# The 3 x 3 kernel is the block's identity (k, refused otherwise); stride 1 is its only stride.
|
|
310
|
+
validated=Validated(({"k": (KERNEL,), "window": (5, 7, 8, 12)},), sources=(Conv3x3Constants,))),
|
|
311
|
+
"group_product": BlockMetadata(
|
|
312
|
+
name="group_product", layout_in="W", layout_out="W", locality=Locality.WINDOW_RING, radius=1,
|
|
313
|
+
policy=DOUBLE_ROUND_ONCE, embedded_constants=group_product_values, asset_arrays=group_product_arrays,
|
|
314
|
+
status="Ready — 1, 2, 4, 8 groups at windows 7, 12; 4 groups at window 8",
|
|
315
|
+
evidence=("docs/history/building-blocks-2026-10-11.md#33-convolution", "variant: grouped_double",
|
|
316
|
+
"docs/history/upernet/notes/milestone10_findings.md", "docs/evidence/p04-offline.json",
|
|
317
|
+
"docs/evidence/live-fixtures/20261013T115558Z-p04.json", "docs/evidence/c0b-offline.json",
|
|
318
|
+
"docs/evidence/live-fixtures/20261013T154410Z-c0b.json"),
|
|
319
|
+
# groups is group_weight's; the 3 x 3 kernel (k) never varied.
|
|
320
|
+
validated=Validated(({"groups": (1, 2, 4, 8), "k": (KERNEL,), "window": (7, 12)},
|
|
321
|
+
{"groups": (4,), "k": (KERNEL,), "window": (8,)}),
|
|
322
|
+
sources=(group_weight, Conv3x3Constants))),
|
|
323
|
+
})
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""The linear layer y = x W^T + b (torch.nn.Linear) per token, and the 1 x 1 convolution.
|
|
2
|
+
|
|
3
|
+
Moved from ``swin_block/earth_engine/linear.py`` (``LinearConstants``, ``linear``)
|
|
4
|
+
and ``upernet/earth_engine/conv2d.py`` (``conv1x1_constants``, ``conv1x1``), research
|
|
5
|
+
code archived at the tag ``research-archive-2026-10-12`` (docs/history/README.md).
|
|
6
|
+
|
|
7
|
+
One primitive serves every projection of the Swin block (QKV, attention output,
|
|
8
|
+
MLP fc1/fc2), Patch Merging's reduction, the patch-embedding projection and the
|
|
9
|
+
decoder's 1 x 1 convolutions. In window layout a whole window is one matrix
|
|
10
|
+
product, [N, in] x [in, out] per pixel, against a constant W^T; on a value (V) it
|
|
11
|
+
is the same product on an ee.Array.
|
|
12
|
+
|
|
13
|
+
**Policy** ``double_round_once``: matrixMultiply accumulates the float32 operands
|
|
14
|
+
in double (window-attention validation: its results rounded to float32 equal the correctly rounded
|
|
15
|
+
float64 dot products); the bias is added in double and the sum is rounded to
|
|
16
|
+
float32 once.
|
|
17
|
+
|
|
18
|
+
**Rows.** The bias is pre-repeated to the rows of the layout it serves (EE arrays
|
|
19
|
+
do not broadcast): ``WindowLayout(7)`` -> 49, ``WindowLayout(1)`` -> 1, a V value
|
|
20
|
+
-> its row count. Builders take that layout (or tensor), as the reference-model composites pass
|
|
21
|
+
it, or a row count, the builders' original call form (still accepted), and record
|
|
22
|
+
``rows``. A block given ``layout=`` refuses constants built for another one
|
|
23
|
+
(client-side only).
|
|
24
|
+
Windows 5, 7, 8 and 12 (and one-row dense constants) are validated live (including the window-size checks).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import numpy as np
|
|
30
|
+
|
|
31
|
+
from ...core.metadata import ANY_SETTINGS, BlockMetadata, Locality, validate_metadata
|
|
32
|
+
from ...core.precision import DOUBLE_ROUND_ONCE, validate_policy
|
|
33
|
+
from ...weights.constants import ServerArray, ValueLayout, WindowLayout, check_rows, constant_rows
|
|
34
|
+
|
|
35
|
+
WINDOW_LAYOUT = WindowLayout()
|
|
36
|
+
"""Today's layout: window 7, 49 rows per constant (the only one validated live)."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def as_layout(layout):
|
|
40
|
+
"""``layout`` itself (a ``WindowLayout``, ``ValueLayout``, ``WindowTensor`` or 2-D V
|
|
41
|
+
value), or a positive ``int`` row count, the builders' original call form (still
|
|
42
|
+
accepted; the reference-model composites pass layouts), as the ``ValueLayout`` with that many rows:
|
|
43
|
+
only the count matters to a per-channel constant."""
|
|
44
|
+
if isinstance(layout, (int, np.integer)) and not isinstance(layout, bool):
|
|
45
|
+
return ValueLayout(int(layout))
|
|
46
|
+
return layout
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def builder_rows(layout) -> int:
|
|
50
|
+
"""Rows of a per-channel constant for ``layout`` (see ``as_layout``):
|
|
51
|
+
``tensor_ee.weights.constants.constant_rows``."""
|
|
52
|
+
return constant_rows(as_layout(layout))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def no_constants(**parameters) -> int:
|
|
56
|
+
"""Cost function of a block that embeds and reads no constants."""
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class LinearConstants:
|
|
61
|
+
"""W^T [in, out] and the bias with rows repeated to [rows, out].
|
|
62
|
+
|
|
63
|
+
weight and bias are NumPy arrays or ServerArrays. bias=None is a layer without
|
|
64
|
+
bias (torch.nn.Linear(bias=False), e.g. Patch Merging's reduction): no addition
|
|
65
|
+
at all, not the addition of zeros. ``tokens`` is the layout the bias serves
|
|
66
|
+
(or its row count; see ``builder_rows``); ``rows`` records it.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, ops, weight, bias, tokens):
|
|
70
|
+
if not isinstance(weight, ServerArray):
|
|
71
|
+
weight = np.asarray(weight, dtype=np.float32)
|
|
72
|
+
self.in_features, self.out_features = weight.shape[1], weight.shape[0]
|
|
73
|
+
self.rows = builder_rows(tokens)
|
|
74
|
+
self.weight_t = ops.constant(weight.T)
|
|
75
|
+
self.bias = None if bias is None else ops.constant(bias, repeat_rows=self.rows)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def linear(ops, x, layer: LinearConstants, *, policy: str = DOUBLE_ROUND_ONCE, layout=None):
|
|
79
|
+
"""x: float32 [N, in] -> float32 [N, out].
|
|
80
|
+
|
|
81
|
+
``layout``, when given (the layout x is in: a ``WindowTensor``, ``WindowLayout``
|
|
82
|
+
or ``ValueLayout``), refuses constants built for another row count."""
|
|
83
|
+
validate_policy(policy, allowed=DOUBLE_ROUND_ONCE)
|
|
84
|
+
if layout is not None:
|
|
85
|
+
check_rows(layer, as_layout(layout))
|
|
86
|
+
product = ops.matmul_double(x, layer.weight_t)
|
|
87
|
+
return ops.to_float(product if layer.bias is None else product.add(layer.bias))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def conv1x1_constants(ops, weight, bias, rows) -> LinearConstants:
|
|
91
|
+
"""Conv2d 1 x 1 weight [Cout, Cin, 1, 1] (or [Cout, Cin]) -> the Swin block's linear.
|
|
92
|
+
``rows``: the layout the bias serves, or its row count."""
|
|
93
|
+
if not isinstance(weight, ServerArray):
|
|
94
|
+
weight = np.asarray(weight, dtype=np.float32).reshape(np.shape(weight)[0], -1)
|
|
95
|
+
return LinearConstants(ops, weight, bias, rows)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def conv1x1(ops, x, layer: LinearConstants, *, policy: str = DOUBLE_ROUND_ONCE, layout=None):
|
|
99
|
+
"""x: float32 [rows, Cin] arrays -> [rows, Cout] (exactly ``linear``)."""
|
|
100
|
+
return linear(ops, x, layer, policy=policy, layout=layout)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# Cost functions (contract section 9)
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
def linear_values(in_features: int, out_features: int, *, bias: bool = True, layout=WINDOW_LAYOUT) -> int:
|
|
107
|
+
"""Values embedded for NumPy weights: W^T [in, out], plus the bias rows [rows, out]."""
|
|
108
|
+
return in_features * out_features + (builder_rows(layout) * out_features if bias else 0)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def linear_arrays(*, bias: bool = True) -> int:
|
|
112
|
+
"""Server arrays read for ServerArray weights: the weight, and the bias if any."""
|
|
113
|
+
return 2 if bias else 1
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
METADATA = validate_metadata({
|
|
117
|
+
"linear": BlockMetadata(
|
|
118
|
+
name="linear", layout_in="W", layout_out="W", locality=Locality.POINTWISE,
|
|
119
|
+
policy=DOUBLE_ROUND_ONCE, embedded_constants=linear_values, asset_arrays=linear_arrays,
|
|
120
|
+
status="Ready",
|
|
121
|
+
evidence=("docs/history/building-blocks-2026-10-11.md#36-linear-layers-and-attention", "docs/history/swin_block/notes/findings.md"),
|
|
122
|
+
validated=ANY_SETTINGS),
|
|
123
|
+
"conv1x1": BlockMetadata(
|
|
124
|
+
name="conv1x1", layout_in="W", layout_out="W", locality=Locality.POINTWISE,
|
|
125
|
+
policy=DOUBLE_ROUND_ONCE, embedded_constants=linear_values, asset_arrays=linear_arrays,
|
|
126
|
+
status="Ready",
|
|
127
|
+
evidence=("docs/history/building-blocks-2026-10-11.md#33-convolution", "docs/history/upernet/notes/milestone7_findings.md"),
|
|
128
|
+
validated=ANY_SETTINGS),
|
|
129
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""W merges: the FPN's addition and the channel concatenation.
|
|
2
|
+
|
|
3
|
+
Moved from ``upernet/earth_engine/feature_ops.py`` (``add``, ``concat``), research code
|
|
4
|
+
archived at the tag ``research-archive-2026-10-12`` (docs/history/README.md). The
|
|
5
|
+
operands are W tensors (the decoder's ``Feature``)
|
|
6
|
+
carried with their grid and layout, so operands that do not share a grid, layout or
|
|
7
|
+
window are refused client-side (``same_grid``). Earth Engine itself would not refuse
|
|
8
|
+
them: binary operations on images of different grids are evaluated in the request's
|
|
9
|
+
projection with each input resampled (nearest), which silently replicates coarse values.
|
|
10
|
+
|
|
11
|
+
- ``add``: float32 + float32 -> float32; EE rounds the exact sum once, as IEEE / PyTorch
|
|
12
|
+
do (policy ``double_round_once``: one rounding at the operation boundary).
|
|
13
|
+
- ``concat``: torch.cat(dim=1) order (first operand's channels first); window layout
|
|
14
|
+
``arrayCat`` on axis 1, dense bands renamed and stacked. Values are moved, never
|
|
15
|
+
rounded: no arithmetic, no cast, no policy.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from ...core.errors import GridMismatch
|
|
21
|
+
from ...core.metadata import ANY_SETTINGS, NO_ARITHMETIC, BlockMetadata, Locality, validate_metadata
|
|
22
|
+
from ...core.precision import DOUBLE_ROUND_ONCE, validate_policy
|
|
23
|
+
from ...core.tensor import _layout, band_names, same_grid
|
|
24
|
+
from .linear import no_constants
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def add(a, b, *, policy: str = DOUBLE_ROUND_ONCE):
|
|
28
|
+
"""a + b of two W tensors on the same grid, layout and window, with equal channels."""
|
|
29
|
+
validate_policy(policy, allowed=DOUBLE_ROUND_ONCE)
|
|
30
|
+
same_grid(a, b)
|
|
31
|
+
if a.channels != b.channels:
|
|
32
|
+
raise GridMismatch(f"{a.channels} vs {b.channels} channels")
|
|
33
|
+
return a.like(a.image.add(b.image).toFloat())
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def concat(parts: list):
|
|
37
|
+
"""torch.cat(parts, dim=1) of W tensors: the decoder's ``Feature`` or a bare
|
|
38
|
+
``WindowTensor`` (window layout)."""
|
|
39
|
+
same_grid(*parts)
|
|
40
|
+
total = sum(p.channels for p in parts)
|
|
41
|
+
if _layout(parts[0]) == "dense_bands":
|
|
42
|
+
renamed = [p.image.select(band_names(p.channels), band_names(p.channels, f"p{i}_"))
|
|
43
|
+
for i, p in enumerate(parts)]
|
|
44
|
+
image = renamed[0]
|
|
45
|
+
for part in renamed[1:]:
|
|
46
|
+
image = image.addBands(part)
|
|
47
|
+
return parts[0].like(image.rename(band_names(total)), total)
|
|
48
|
+
image = parts[0].image
|
|
49
|
+
for p in parts[1:]:
|
|
50
|
+
image = image.arrayCat(p.image, 1)
|
|
51
|
+
return parts[0].like(image, total)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
METADATA = validate_metadata({
|
|
55
|
+
"add": BlockMetadata(
|
|
56
|
+
name="add", layout_in="W", layout_out="W", locality=Locality.POINTWISE,
|
|
57
|
+
policy=DOUBLE_ROUND_ONCE, embedded_constants=no_constants, asset_arrays=no_constants,
|
|
58
|
+
status="Ready",
|
|
59
|
+
evidence=("docs/history/building-blocks-2026-10-11.md#35-activations-merges-and-outputs", "docs/history/upernet/notes/milestone9_findings.md"),
|
|
60
|
+
validated=ANY_SETTINGS),
|
|
61
|
+
"concat": BlockMetadata(
|
|
62
|
+
name="concat", layout_in="W", layout_out="W", locality=Locality.POINTWISE,
|
|
63
|
+
policy=NO_ARITHMETIC, embedded_constants=no_constants, asset_arrays=no_constants,
|
|
64
|
+
status="Ready",
|
|
65
|
+
evidence=("docs/history/building-blocks-2026-10-11.md#35-activations-merges-and-outputs", "docs/history/upernet/notes/milestone8_findings.md"),
|
|
66
|
+
validated=ANY_SETTINGS),
|
|
67
|
+
})
|