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
tensor_ee/__init__.py ADDED
@@ -0,0 +1,28 @@
1
+ """Experimental PyTorch-compatible building blocks for native Earth Engine."""
2
+
3
+ from importlib.metadata import PackageNotFoundError as _PackageNotFoundError, version as _distribution_version
4
+
5
+ try:
6
+ __version__ = _distribution_version("tensor-ee")
7
+ except _PackageNotFoundError:
8
+ __version__ = "0.1.0" # Source checkouts need no installed distribution metadata.
9
+
10
+ from .comparison import PixelTensor, compare_segmentation
11
+ from .compatibility import CompatibilityReport, check_compatibility
12
+ from .compiler import CompiledModel, Grid, Raster, UnsupportedOperation, WeightProvider, compile_model
13
+ from .primitives import (concatenate, conv2d, eval_dropout, from_image, gelu, leaky_relu, nearest_upsample,
14
+ pool2d, relu, residual_add, sigmoid, silu, softmax, tanh)
15
+ from .outputs import SegmentationOutputs, binary_threshold, segmentation_outputs
16
+ from .weight_assets import FeatureCollectionWeightProvider, effective_convolutions, export_weights
17
+ from .weight_mapping import MatchedWeightProvider
18
+
19
+ __all__ = [
20
+ "PixelTensor", "compare_segmentation",
21
+ "CompatibilityReport", "check_compatibility",
22
+ "CompiledModel", "Grid", "Raster", "UnsupportedOperation", "WeightProvider",
23
+ "compile_model", "from_image", "conv2d", "pool2d", "nearest_upsample",
24
+ "concatenate", "residual_add", "relu", "sigmoid", "leaky_relu", "silu", "gelu", "tanh", "eval_dropout", "softmax",
25
+ "SegmentationOutputs", "segmentation_outputs", "binary_threshold",
26
+ "FeatureCollectionWeightProvider", "MatchedWeightProvider",
27
+ "effective_convolutions", "export_weights",
28
+ ]
@@ -0,0 +1,304 @@
1
+ """Local accounting and exact asset record mapping for the Swin-UPerNet composites.
2
+
3
+ The fixed Swin-T224 adapter (``tensor_ee.swin``) and workflow (``tensor_ee.swin_workflow``)
4
+ use ``SwinTParameters`` and the loaders here, at Swin-T224's settings. The schema, the
5
+ checkpoint mapping, the parameter checks and the weight records also take a
6
+ ``SwinUPerNetConfig`` (``tensor_ee.models.swin_t224.configuration``; default ``SWIN_T224``):
7
+ ``parameter_records`` yields the ``ConvolutionWeights`` records of any configuration, under
8
+ the timm and SMP names the composites' loader (``with_manifest``) reads, in the v2 manifest
9
+ format (``native_ee_featurecollection_weights_v2``) that ``export_weights`` writes.
10
+ Swin-T224's 189 records are unchanged."""
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+ import hashlib
15
+ import io
16
+ import json
17
+ from pathlib import Path
18
+ import re
19
+ from typing import Any, Literal, Mapping
20
+
21
+ import numpy as np
22
+ import torch
23
+
24
+ from .core.errors import UnsupportedOperation
25
+ from .models.swin_t224.configuration import DEPTHS, SWIN_T224, SwinUPerNetConfig, block_shifts
26
+ from .weights.assets import ConvolutionWeights, FeatureCollectionWeightProvider
27
+ from .weights.constants import ServerArray
28
+
29
+ ENC_PREFIX = "encoder.model."
30
+ PE_PREFIX = ENC_PREFIX + "patch_embed."
31
+ ENCODER_NAME = "tu-swin_tiny_patch4_window7_224"
32
+
33
+
34
+ def parameter_shapes(config: SwinUPerNetConfig = SWIN_T224):
35
+ """Complete inference schema in the historical three-map layouts (encoder groups, patch
36
+ embedding, decoder, BatchNorm counters): ``config.parameter_shapes()``; Swin-T224's by
37
+ default."""
38
+ return config.parameter_shapes()
39
+
40
+
41
+ def encoder_prefix(group):
42
+ if group.startswith("pm"):
43
+ return f"layers.{int(group[2:])}.downsample."
44
+ s, b = group[1:].split("b")
45
+ return f"layers.{int(s)-1}.blocks.{b}."
46
+
47
+
48
+ def checkpoint_shapes(config: SwinUPerNetConfig = SWIN_T224):
49
+ """The SMP UPerNet checkpoint's tensor shapes (``encoder.model.layers_{s}...``,
50
+ ``encoder.model.patch_embed.*`` with the Conv2d weight [embed_dim, bands, patch, patch],
51
+ the decoder and head, and the BatchNorm counters)."""
52
+ enc, pe, dec, counters = parameter_shapes(config)
53
+ out = {ENC_PREFIX + encoder_prefix(g).replace("layers.", "layers_", 1) + n: shape
54
+ for g, params in enc.items() for n, shape in params.items()}
55
+ conv = (config.embed_dim, config.in_chans, config.patch, config.patch)
56
+ out.update({PE_PREFIX+n: (conv if n == "proj.weight" else shape) for n, shape in pe.items()})
57
+ out.update(dec)
58
+ out.update(counters)
59
+ return out
60
+
61
+
62
+ def _sha(value, name):
63
+ if not isinstance(value, str) or re.fullmatch(r"[0-9a-f]{64}", value) is None:
64
+ raise UnsupportedOperation(f"{name} must be a lowercase SHA-256 digest")
65
+
66
+
67
+ def _model(config: SwinUPerNetConfig) -> str:
68
+ """The model's name in messages: "fixed Swin-T" (Swin-T224, the historical messages) or
69
+ "configured Swin-UPerNet"."""
70
+ return "fixed Swin-T" if config == SWIN_T224 else "configured Swin-UPerNet"
71
+
72
+
73
+ def validate_state(state, config: SwinUPerNetConfig = SWIN_T224):
74
+ shapes = checkpoint_shapes(config)
75
+ if not isinstance(state, Mapping) or set(state) != set(shapes):
76
+ got = set(state) if isinstance(state, Mapping) else set()
77
+ raise UnsupportedOperation(f"{_model(config)} checkpoint keys differ: missing={sorted(set(shapes)-got)}, extra={sorted(got-set(shapes))}")
78
+ for k, shape in shapes.items():
79
+ v = state[k]
80
+ if not isinstance(v, torch.Tensor) or tuple(v.shape) != shape:
81
+ raise UnsupportedOperation(f"{k}: expected tensor shape {shape}")
82
+ if k.endswith("num_batches_tracked"):
83
+ if v.dtype != torch.int64 or v.item() < 0:
84
+ raise UnsupportedOperation(f"{k}: expected a nonnegative int64 BatchNorm counter")
85
+ elif v.dtype != torch.float32 or not torch.isfinite(v).all():
86
+ raise UnsupportedOperation(f"{k}: expected finite float32 coefficients")
87
+
88
+
89
+ def read_state(checkpoint: Path, expected_checkpoint_sha256: str):
90
+ _sha(expected_checkpoint_sha256, "expected_checkpoint_sha256")
91
+ # An open file can still be changed in place. Hash and deserialize one
92
+ # immutable snapshot so the verified digest binds the returned coefficients.
93
+ source_data = Path(checkpoint).read_bytes()
94
+ if hashlib.sha256(source_data).hexdigest() != expected_checkpoint_sha256:
95
+ raise UnsupportedOperation("checkpoint SHA-256 differs from expected_checkpoint_sha256")
96
+ checkpoint_data = torch.load(io.BytesIO(source_data), map_location="cpu", weights_only=True)
97
+ if not isinstance(checkpoint_data, Mapping) or "model_state_dict" not in checkpoint_data:
98
+ raise UnsupportedOperation("expected checkpoint with model_state_dict")
99
+ state = checkpoint_data["model_state_dict"]
100
+ validate_state(state)
101
+ return state
102
+
103
+
104
+ def parameters_from_state(state, config: SwinUPerNetConfig = SWIN_T224):
105
+ """Re-key only, preserving all coefficient values and original flatten order. The
106
+ patch embedding's Conv2d weight becomes [embed_dim, bands x patch^2], flattened
107
+ (c, ky, kx)."""
108
+ validate_state(state, config)
109
+ enc_shapes, _, _, _ = parameter_shapes(config)
110
+ enc, pe, dec = {g: {} for g in enc_shapes}, {}, {}
111
+ for k, tensor in state.items():
112
+ if k.endswith("num_batches_tracked"):
113
+ continue
114
+ v = tensor.detach().cpu().numpy().copy()
115
+ if k.startswith(PE_PREFIX):
116
+ n = k[len(PE_PREFIX):]
117
+ pe[n] = v.reshape(config.embed_dim, config.patch_columns) if n == "proj.weight" else v
118
+ elif k.startswith(ENC_PREFIX):
119
+ stage, rest = k[len(ENC_PREFIX+"layers_"):].split(".", 1)
120
+ if rest.startswith("blocks."):
121
+ b, n = rest[len("blocks."):].split(".", 1)
122
+ enc[f"s{int(stage)+1}b{b}"][n] = v
123
+ else:
124
+ enc[f"pm{int(stage)}"][rest[len("downsample."):]] = v
125
+ else:
126
+ dec[k] = v
127
+ return enc, pe, dec
128
+
129
+
130
+ def _validate_map(values, shapes, name, model="fixed Swin-T"):
131
+ if not isinstance(values, Mapping) or set(values) != set(shapes):
132
+ raise UnsupportedOperation(f"{name}: {model} parameter names differ")
133
+ for k, shape in shapes.items():
134
+ value = values[k]
135
+ if isinstance(value, ServerArray):
136
+ # Decoder asset convolutions use the historical flattened [Cout,Cin*k*k] record.
137
+ accepted = ((shape[0], int(np.prod(shape[1:]))),) if len(shape) == 4 else (shape,)
138
+ if tuple(value.shape) not in accepted:
139
+ raise UnsupportedOperation(f"{name}.{k}: expected shape {shape}")
140
+ elif not isinstance(value, np.ndarray) or value.shape != shape or value.dtype != np.float32 or not np.isfinite(value).all():
141
+ raise UnsupportedOperation(f"{name}.{k}: expected finite float32 array of shape {shape}")
142
+
143
+
144
+ def validate_parameters(encoder, patch, decoder, config: SwinUPerNetConfig = SWIN_T224) -> None:
145
+ """The parameter checks: the three maps hold exactly the configuration's names (the
146
+ encoder groups without a final norm), each a finite float32 NumPy array of its schema
147
+ shape or a ``ServerArray`` of that shape (a convolution as [Cout, Cin k k])."""
148
+ enc, pe, dec, _ = parameter_shapes(config)
149
+ model = _model(config)
150
+ if not isinstance(encoder, Mapping) or set(encoder) != set(enc):
151
+ raise UnsupportedOperation(f"encoder: {model} groups differ (raw C4 has no final norm)")
152
+ for g in enc:
153
+ _validate_map(encoder[g], enc[g], g, model)
154
+ _validate_map(patch, pe, "patch_embed", model)
155
+ _validate_map(decoder, dec, "decoder", model)
156
+
157
+
158
+ @dataclass(frozen=True)
159
+ class SwinTParameters:
160
+ """Validated Swin-T224 parameters: the fixed adapter's and workflow's input."""
161
+
162
+ encoder: Mapping[str, Mapping[str, np.ndarray | ServerArray]]
163
+ patch_embed: Mapping[str, np.ndarray | ServerArray]
164
+ decoder: Mapping[str, np.ndarray | ServerArray]
165
+ checkpoint_sha256: str
166
+ manifest_sha256: str | None = None
167
+
168
+ def __post_init__(self):
169
+ self.validate()
170
+
171
+ def validate(self):
172
+ _sha(self.checkpoint_sha256, "checkpoint_sha256")
173
+ if self.manifest_sha256 is not None:
174
+ _sha(self.manifest_sha256, "manifest_sha256")
175
+ validate_parameters(self.encoder, self.patch_embed, self.decoder, SWIN_T224)
176
+
177
+
178
+ def records_and_locations(encoder, patch, decoder):
179
+ """The existing row/chunk/record format, with each source tensor's read location."""
180
+ records, locations = {}, {}
181
+ groups = [(g, p, encoder_prefix(g)) for g, p in encoder.items()]
182
+ groups += [("patch_embed", patch, "patch_embed."), ("decoder", decoder, "")]
183
+ for group, params, prefix in groups:
184
+ for n, value in params.items():
185
+ base = n.removesuffix(".bias")
186
+ weight_name = base + ".weight"
187
+ if n.endswith(".bias") and weight_name in params and params[weight_name].ndim >= 2:
188
+ key, part = prefix+base, "bias"
189
+ elif value.ndim == 1:
190
+ key, part = prefix+n, "vector"
191
+ elif n.endswith(".weight"):
192
+ key, part = prefix+n.removesuffix(".weight"), "weight"
193
+ else:
194
+ key, part = prefix+n, "matrix"
195
+ locations[(group,n)] = (key, part, value.shape)
196
+ if part == "bias":
197
+ continue
198
+ matrix = value.reshape(1,-1) if part == "vector" else value.reshape(value.shape[0],-1)
199
+ bias = params.get(n.removesuffix(".weight")+".bias") if part == "weight" else None
200
+ if key in records:
201
+ raise UnsupportedOperation(f"duplicate parameter record {key}")
202
+ records[key] = ConvolutionWeights(key, matrix.copy(), None if bias is None else bias.copy(), "float32", prefix+n)
203
+ return records, locations
204
+
205
+
206
+ def parameter_records(encoder, patch, decoder, config: SwinUPerNetConfig = SWIN_T224) -> list[ConvolutionWeights]:
207
+ """The ``ConvolutionWeights`` records of a configuration's local parameters, for
208
+ ``export_weights`` (a v2 manifest) and ``with_manifest`` (the composites' loader).
209
+
210
+ The three maps are the historical layouts (``parameters_from_state``): encoder groups
211
+ ``s{s}b{b}`` / ``pm{n}`` with timm names, the patch embedding, and the decoder and head
212
+ with SMP names. They must pass ``validate_parameters`` for ``config`` and be NumPy arrays
213
+ (server arrays cannot be re-exported). The records are ``records_and_locations``'s, in
214
+ the maps' order: for Swin-T224 exactly ``swin_workflow.weight_records``'s 189. No
215
+ requests occur."""
216
+ validate_parameters(encoder, patch, decoder, config)
217
+ maps = [*encoder.values(), patch, decoder]
218
+ if any(not isinstance(value, np.ndarray) for values in maps for value in values.values()):
219
+ raise UnsupportedOperation("parameter_records requires local NumPy parameters, without a manifest")
220
+ records, _ = records_and_locations(encoder, patch, decoder)
221
+ return list(records.values())
222
+
223
+
224
+ def with_manifest(encoder, patch, decoder, manifest, checkpoint_sha256):
225
+ if not isinstance(manifest, Mapping) or manifest.get("source_checkpoint_sha256") != checkpoint_sha256:
226
+ raise UnsupportedOperation("manifest source_checkpoint_sha256 differs from the checkpoint")
227
+ if manifest.get("format") != "native_ee_featurecollection_weights_v2" or manifest.get("status") != "ready":
228
+ raise UnsupportedOperation("a completed native_ee_featurecollection_weights_v2 manifest is required")
229
+ records, locations = records_and_locations(encoder, patch, decoder)
230
+ layers = manifest.get("layers")
231
+ if not isinstance(layers, Mapping) or set(layers) != set(records):
232
+ raise UnsupportedOperation("manifest must account for every fixed Swin-T record exactly once")
233
+ for key, record in records.items():
234
+ meta = layers[key]
235
+ if not isinstance(meta, Mapping) or (meta.get("shape") != list(record.shape) or meta.get("dtype") != "float32"
236
+ or meta.get("bias") is not (record.bias is not None) or meta.get("sha256") != record.sha256):
237
+ raise UnsupportedOperation(f"manifest record {key}: shape/dtype/bias/fingerprint differs from checkpoint")
238
+ assets = meta.get("assets")
239
+ if not isinstance(assets, list) or not assets or any(not isinstance(v,str) or not v for v in assets) or len(set(assets)) != len(assets):
240
+ raise UnsupportedOperation(f"manifest record {key}: explicit unique asset IDs are required")
241
+ listing = json.dumps(sorted((key, r.sha256) for key,r in records.items()), separators=(",",":"))
242
+ if manifest.get("manifest_hash") != hashlib.sha256(listing.encode()).hexdigest():
243
+ raise UnsupportedOperation("manifest_hash differs from the exact parameter record listing")
244
+ provider = FeatureCollectionWeightProvider(manifest)
245
+ arrays = {k: provider.arrays(k) for k in records}
246
+ output = {g:{} for g in encoder}
247
+ output.update(patch_embed={},decoder={})
248
+ for (g,n),(key,part,shape) in locations.items():
249
+ weight, bias = arrays[key]
250
+ if part == "bias":
251
+ array = bias.reshape(list(shape))
252
+ elif part == "vector":
253
+ array = weight.reshape(list(shape))
254
+ else:
255
+ array = weight
256
+ shape = tuple(records[key].shape)
257
+ output[g][n] = ServerArray(array,shape)
258
+ sha = hashlib.sha256(json.dumps(manifest,sort_keys=True,separators=(",",":"),allow_nan=False).encode()).hexdigest()
259
+ return {g:output[g] for g in encoder},output['patch_embed'],output['decoder'],sha
260
+
261
+
262
+ def load_swin_t224_parameters(checkpoint: Path, *, expected_checkpoint_sha256: str,
263
+ manifest: Mapping[str, Any] | None = None) -> SwinTParameters:
264
+ state = read_state(checkpoint, expected_checkpoint_sha256)
265
+ enc, pe, dec = parameters_from_state(state)
266
+ manifest_sha = None
267
+ if manifest is not None:
268
+ enc, pe, dec, manifest_sha = with_manifest(enc, pe, dec, manifest, expected_checkpoint_sha256)
269
+ return SwinTParameters(enc, pe, dec, expected_checkpoint_sha256, manifest_sha)
270
+
271
+
272
+ def build_model(state, attention="default"):
273
+ """Strict local reference model; no download, EE initialization, or forward pass."""
274
+ if attention not in ("default","explicit"):
275
+ raise UnsupportedOperation("attention must be 'default' or 'explicit'")
276
+ validate_state(state)
277
+ import segmentation_models_pytorch as smp
278
+ # Construction must not round the checkpoint through an ambient half dtype
279
+ # or place the reference on an ambient accelerator default device.
280
+ with torch.device("cpu"):
281
+ model = smp.UPerNet(encoder_name=ENCODER_NAME, encoder_weights=None, in_channels=2, classes=2).float()
282
+ model.load_state_dict(state, strict=True)
283
+ model = model.eval()
284
+ shifts = block_shifts(8,8)
285
+ for s in range(4):
286
+ stage = getattr(model.encoder.model,f"layers_{s}")
287
+ if len(stage.blocks) != DEPTHS[s]:
288
+ raise UnsupportedOperation("installed model does not implement the fixed Swin-T depths")
289
+ for block, shift in zip(stage.blocks,shifts[s]):
290
+ if block.shift_size != (shift,shift) or block.window_size != (7,7):
291
+ raise UnsupportedOperation("installed model window/shift differs from fixed Swin-T224")
292
+ if (not isinstance(block.mlp.act,torch.nn.GELU) or block.mlp.act.approximate != 'none'
293
+ or block.norm1.eps != 1e-5 or block.norm2.eps != 1e-5):
294
+ raise UnsupportedOperation("installed model GELU/LayerNorm differs from fixed Swin-T224")
295
+ if attention == "explicit":
296
+ block.attn.fused_attn = False
297
+ return model
298
+
299
+
300
+ def load_swin_t224_model(checkpoint: Path, *, expected_checkpoint_sha256: str,
301
+ attention: Literal["default", "explicit"] = "default") -> torch.nn.Module:
302
+ if attention not in ("default", "explicit"):
303
+ raise UnsupportedOperation("attention must be 'default' or 'explicit'")
304
+ return build_model(read_state(checkpoint,expected_checkpoint_sha256),attention)
File without changes
@@ -0,0 +1,36 @@
1
+ """Raster-layout (R) blocks of the native EE library: the CNN path's compute blocks.
2
+
3
+ - ``conv``: ``conv2d``;
4
+ - ``pooling``: ``pool2d``;
5
+ - ``resample``: ``nearest_upsample``;
6
+ - ``merge``: ``concatenate``, ``residual_add``;
7
+ - ``activations``: ``relu``, ``sigmoid``, ``leaky_relu``, ``silu``, ``gelu``, ``tanh``,
8
+ ``eval_dropout``, ``common_score_mask``, ``softmax``;
9
+ - ``outputs``: ``segmentation_outputs``, ``binary_threshold``.
10
+
11
+ Each module declares ``METADATA`` (locality, policy, cost and status per block,
12
+ docs/library-contract.md section 9). Every block takes a keyword-only
13
+ ``policy=`` that defaults to, and only accepts, ``float32_operands`` (section 7).
14
+
15
+ These are the blocks themselves. ``compile_model`` materializes each node with
16
+ ``.toFloat()``, and the public ``tensor_ee`` primitives wrap the same blocks
17
+ with that materialization; neither cast is inside a block.
18
+
19
+ Modules here import only the standard library, ``ee``, NumPy, torch (the blocks
20
+ take PyTorch modules and options), ``tensor_ee.core``, ``tensor_ee.layouts``
21
+ and ``tensor_ee.weights``. Nothing here imports a research folder, the
22
+ converter (``compiler``, ``semantics``, ...) or the models.
23
+ """
24
+
25
+ from .conv import conv2d
26
+ from .pooling import pool2d
27
+ from .resample import nearest_upsample
28
+ from .merge import concatenate, residual_add
29
+ from .activations import (common_score_mask, eval_dropout, gelu, leaky_relu, relu, sigmoid, silu,
30
+ softmax, tanh)
31
+ from .outputs import SegmentationOutputs, binary_threshold, segmentation_outputs
32
+
33
+ __all__ = ["conv2d", "pool2d", "nearest_upsample", "concatenate", "residual_add",
34
+ "relu", "sigmoid", "leaky_relu", "silu", "gelu", "tanh", "eval_dropout",
35
+ "common_score_mask", "softmax",
36
+ "SegmentationOutputs", "segmentation_outputs", "binary_threshold"]
@@ -0,0 +1,183 @@
1
+ """R pointwise activations, the common score mask and channel softmax.
2
+
3
+ Moved whole from ``tensor_ee.compiler``, where they were private:
4
+ ``_relu``, ``_sigmoid``, ``_leaky_relu``, ``_silu``, ``_gelu``, ``_tanh``,
5
+ ``_eval_dropout``, ``_common_score_mask`` and ``_softmax`` are here ``relu``,
6
+ ``sigmoid``, ``leaky_relu``, ``silu``, ``gelu``, ``tanh``, ``eval_dropout``,
7
+ ``common_score_mask`` and ``softmax``. Their helpers moved with them:
8
+ ``_sigmoid_image``, ``_real_option``, ``_FLOAT32_MAX``, ``_exact_gelu`` and
9
+ ``_dropout_probability``. The old names are re-exports of these objects.
10
+
11
+ Precision policy ``float32_operands`` (contract section 7): scalars are exact
12
+ binary64 (``_ee_scalar``); softmax keeps its ``toDouble()`` score widening; the
13
+ node output is materialized with ``.toFloat()`` by the caller
14
+ (``compile_model`` or the ``tensor_ee.primitives`` facade), not here.
15
+
16
+ ``_finish`` and ``_ee_scalar`` are module-level bindings that these functions
17
+ look up (tests/test_pointwise_operators.py patches them here).
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import math
23
+ from numbers import Real
24
+ from typing import Any
25
+
26
+ import ee
27
+ import torch
28
+
29
+ from ...core.errors import UnsupportedOperation
30
+ from ...core.grid import Grid
31
+ from ...core.metadata import ANY_SETTINGS, BlockMetadata, Locality, Validated, validate_metadata
32
+ from ...core.precision import FLOAT32_OPERANDS, validate_policy
33
+ from ...core.precision import exact_scalar as _ee_scalar
34
+ from ...core.tensor import Raster
35
+ from ...layouts.raster import finish as _finish
36
+ from .conv import _no_coefficients
37
+
38
+
39
+ def relu(raster: Raster, *, clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
40
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
41
+ return _finish(raster.image.max(0), raster.bands, raster.grid, clip_footprints)
42
+
43
+
44
+ def _sigmoid_image(image: ee.Image) -> ee.Image:
45
+ # Stable algebraic form avoids exp overflow for very negative logits.
46
+ magnitude = image.abs().multiply(-1).exp()
47
+ positive = magnitude.add(1).pow(-1)
48
+ negative = magnitude.divide(magnitude.add(1))
49
+ return positive.where(image.lt(0), negative)
50
+
51
+
52
+ def sigmoid(raster: Raster, *, clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
53
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
54
+ return _finish(_sigmoid_image(raster.image), raster.bands, raster.grid, clip_footprints)
55
+
56
+
57
+ _FLOAT32_MAX = float(torch.finfo(torch.float32).max)
58
+
59
+
60
+ def _real_option(value: Any, name: str, *, float32: bool = False) -> float:
61
+ if isinstance(value, bool) or not isinstance(value, Real):
62
+ raise UnsupportedOperation(f"{name} must be a static finite real scalar; received {value!r}")
63
+ try:
64
+ number = float(value)
65
+ except (OverflowError, ValueError) as error:
66
+ raise UnsupportedOperation(f"{name} must be finite; received {value!r}") from error
67
+ if not math.isfinite(number):
68
+ raise UnsupportedOperation(f"{name} must be finite; received {value!r}")
69
+ if float32:
70
+ if abs(value) > _FLOAT32_MAX:
71
+ raise UnsupportedOperation(f"{name} must be representable in float32; received {value!r}")
72
+ rounded = torch.tensor(number, dtype=torch.float32).item()
73
+ if value != 0 and rounded == 0:
74
+ raise UnsupportedOperation(f"{name} must be representable in float32; received {value!r}")
75
+ number = rounded
76
+ return number
77
+
78
+
79
+ def _exact_gelu(approximate: Any) -> None:
80
+ if not isinstance(approximate, str) or approximate != "none":
81
+ raise UnsupportedOperation(f"approximate must be 'none'; received {approximate!r}")
82
+
83
+
84
+ def _dropout_probability(p: Any) -> float:
85
+ probability = _real_option(p, "p")
86
+ if not 0 <= p <= 1:
87
+ raise UnsupportedOperation(f"p must satisfy 0 <= p <= 1; received {p!r}")
88
+ return probability
89
+
90
+
91
+ def leaky_relu(raster: Raster, negative_slope: float = 0.01,
92
+ *, clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
93
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
94
+ slope = _real_option(negative_slope, "negative_slope", float32=True)
95
+ image = raster.image.where(raster.image.lt(0), raster.image.multiply(_ee_scalar(slope)))
96
+ return _finish(image, raster.bands, raster.grid, clip_footprints)
97
+
98
+
99
+ def silu(raster: Raster, *, clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
100
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
101
+ # Both sigmoid branches use exp(-abs(x)); no positive exponent can overflow.
102
+ sigmoid = _sigmoid_image(raster.image)
103
+ return _finish(raster.image.multiply(sigmoid), raster.bands, raster.grid, clip_footprints)
104
+
105
+
106
+ def gelu(raster: Raster, *, approximate: str = "none",
107
+ clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
108
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
109
+ _exact_gelu(approximate)
110
+ # Established Swin expression: double work, one float32 materialization at
111
+ # the public/compiler boundary. No intermediate x*x or positive exponent.
112
+ image = raster.image.multiply(0.5).multiply(
113
+ raster.image.multiply(math.sqrt(0.5)).erf().add(1))
114
+ return _finish(image, raster.bands, raster.grid, clip_footprints)
115
+
116
+
117
+ def tanh(raster: Raster, *, clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
118
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
119
+ return _finish(raster.image.tanh(), raster.bands, raster.grid, clip_footprints)
120
+
121
+
122
+ def eval_dropout(raster: Raster, *, p: float = 0.5,
123
+ clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
124
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
125
+ _dropout_probability(p)
126
+ return _finish(raster.image, raster.bands, raster.grid, clip_footprints)
127
+
128
+
129
+ def common_score_mask(image: ee.Image, grid: Grid, *, policy: str = FLOAT32_OPERANDS) -> ee.Image:
130
+ """Intersect every score mask and finite float32 predicate before reduction.
131
+
132
+ This is the existing _valid_logits predicate in a recipe-free core path.
133
+ Parsing FLOAT32_MAX avoids EE's integral-valued literal clipping.
134
+ """
135
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
136
+ masks = image.mask().reduce(ee.Reducer.min()).gt(0)
137
+ finite = image.eq(image).And(image.abs().lte(_ee_scalar(_FLOAT32_MAX))).reduce(ee.Reducer.min())
138
+ return masks.And(finite).unmask(0, sameFootprint=False).And(grid.mask())
139
+
140
+
141
+ def softmax(raster: Raster, *, clip_footprints: bool = False, policy: str = FLOAT32_OPERANDS) -> Raster:
142
+ validate_policy(policy, allowed=FLOAT32_OPERANDS)
143
+ if (not raster.bands or any(not isinstance(name, str) or not name for name in raster.bands)
144
+ or len(set(raster.bands)) != len(raster.bands)):
145
+ raise UnsupportedOperation("softmax requires nonempty unique channel names")
146
+ image = raster.image.select(list(raster.bands))
147
+ image = image.updateMask(common_score_mask(image, raster.grid)).toDouble()
148
+ maximum = image.reduce(ee.Reducer.max())
149
+ exponential = image.subtract(maximum).exp()
150
+ normalized = exponential.divide(exponential.reduce(ee.Reducer.sum()))
151
+ return _finish(normalized, raster.bands, raster.grid, clip_footprints)
152
+
153
+
154
+ # ---------------------------------------------------------------------------
155
+ # Locality and cost metadata (contract section 9)
156
+ # ---------------------------------------------------------------------------
157
+ _SWEEPS = ("docs/evidence/t02-operators.json", "docs/evidence/t05-primary.json")
158
+
159
+ _UNCLIPPED = Validated(({"clip_footprints": (False,)},))
160
+ """The blocks only the CNN operator validation, segmentation output validation and trained-model preservation sweeps cover: their slopes, probabilities and inputs
161
+ varied, but ``clip_footprints`` never left False. The ResNet18 runs, which the other R blocks
162
+ cite, used True (resnet18_full64_float32_policy.json)."""
163
+
164
+
165
+ def _pointwise(name: str, evidence: tuple[str, ...], validated: Validated = ANY_SETTINGS) -> BlockMetadata:
166
+ return BlockMetadata(name=name, layout_in="R", layout_out="R", locality=Locality.POINTWISE,
167
+ policy=FLOAT32_OPERANDS, embedded_constants=_no_coefficients,
168
+ asset_arrays=_no_coefficients, status="Ready", evidence=evidence, validated=validated)
169
+
170
+
171
+ METADATA = validate_metadata({
172
+ "relu": _pointwise("relu", ("docs/history/cnn/results/parity.json", "docs/history/cnn/results/resnet18_full64_float32_policy.json",
173
+ "docs/history/cnn/results/segwater_js_parity_1x1.json")),
174
+ "sigmoid": _pointwise("sigmoid", ("docs/history/cnn/results/parity.json", "docs/history/cnn/results/resnet18_full64_float32_policy.json")),
175
+ "leaky_relu": _pointwise("leaky_relu", _SWEEPS, _UNCLIPPED),
176
+ "silu": _pointwise("silu", _SWEEPS, _UNCLIPPED),
177
+ "gelu": _pointwise("gelu", _SWEEPS, _UNCLIPPED),
178
+ "tanh": _pointwise("tanh", _SWEEPS, _UNCLIPPED),
179
+ "eval_dropout": _pointwise("eval_dropout", _SWEEPS, _UNCLIPPED),
180
+ # A single-band 0/1 validity mask on the raster's grid.
181
+ "common_score_mask": _pointwise("common_score_mask", ("docs/evidence/t03-outputs.json",)),
182
+ "softmax": _pointwise("softmax", ("docs/evidence/t03-outputs.json", "docs/evidence/t05-primary.json"), _UNCLIPPED),
183
+ })