From a63196fa59422ef08bbd2ed8c9c5ca4844377245 Mon Sep 17 00:00:00 2001 From: Jeremy McGibbon Date: Thu, 12 Feb 2026 17:24:37 +0000 Subject: [PATCH 1/7] revert change to weight shape with 1 group --- .../models/conditional_sfno/s2convolutions.py | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/fme/core/models/conditional_sfno/s2convolutions.py b/fme/core/models/conditional_sfno/s2convolutions.py index b138dd442..15ec43ac9 100644 --- a/fme/core/models/conditional_sfno/s2convolutions.py +++ b/fme/core/models/conditional_sfno/s2convolutions.py @@ -183,12 +183,21 @@ def __init__( # seemingly the first weight is not really complex, so we need to account for that scale[0, :] *= math.sqrt(2.0) - weight_shape = [ - num_groups, - in_channels // num_groups, - out_channels // num_groups, - self.modes_lat_local, - ] + if num_groups == 1: + # for backwards compatibility with existing checkpoints, + # we keep the weight shape the same when num_groups=1 + weight_shape = [ + in_channels, + out_channels, + self.modes_lat_local, + ] + else: + weight_shape = [ + num_groups, + in_channels // num_groups, + out_channels // num_groups, + self.modes_lat_local, + ] assert factorization == "ComplexDense" self.weight = nn.Parameter(scale * torch.randn(*weight_shape, 2)) @@ -254,10 +263,14 @@ def forward(self, x, timer: Timer = NullTimer()): # pragma: no cover lora_update = 0.0 with timer.child("dhconv"): + if self.num_groups == 1: + weight = self.weight[None, ...] # expand group dim + else: + weight = self.weight xp = torch.zeros_like(x) xp[..., : self.modes_lat_local, : self.modes_lon_local] = _contract_dhconv( x[..., : self.modes_lat_local, : self.modes_lon_local], - self.weight, + weight, ) xp = xp + self.lora_scaling * lora_update xp = xp.reshape(B, self.out_channels, H, W) From d4f0014206af5a0c43277d07ddd254e3bb12a26e Mon Sep 17 00:00:00 2001 From: Jeremy McGibbon Date: Thu, 12 Feb 2026 17:32:55 +0000 Subject: [PATCH 2/7] fix using load state dict pre hook --- .../models/conditional_sfno/s2convolutions.py | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/fme/core/models/conditional_sfno/s2convolutions.py b/fme/core/models/conditional_sfno/s2convolutions.py index 15ec43ac9..22fad38d1 100644 --- a/fme/core/models/conditional_sfno/s2convolutions.py +++ b/fme/core/models/conditional_sfno/s2convolutions.py @@ -183,21 +183,12 @@ def __init__( # seemingly the first weight is not really complex, so we need to account for that scale[0, :] *= math.sqrt(2.0) - if num_groups == 1: - # for backwards compatibility with existing checkpoints, - # we keep the weight shape the same when num_groups=1 - weight_shape = [ - in_channels, - out_channels, - self.modes_lat_local, - ] - else: - weight_shape = [ - num_groups, - in_channels // num_groups, - out_channels // num_groups, - self.modes_lat_local, - ] + weight_shape = [ + num_groups, + in_channels // num_groups, + out_channels // num_groups, + self.modes_lat_local, + ] assert factorization == "ComplexDense" self.weight = nn.Parameter(scale * torch.randn(*weight_shape, 2)) @@ -232,8 +223,38 @@ def __init__( if bias: self.bias = nn.Parameter(torch.zeros(1, out_channels, 1, 1)) + self.in_channels = in_channels self.out_channels = out_channels + # rewrite old checkpoints on load + self._register_load_state_dict_pre_hook(self._pre_load_hook, with_module=True) + + @staticmethod + def _pre_load_hook( + module: "SpectralConvS2", + state_dict: dict[str, torch.Tensor], + prefix: str, + local_metadata: dict, + strict: bool, + missing_keys: list[str], + unexpected_keys: list[str], + error_msgs: list[str], + ) -> None: + key = prefix + "weight" + if key not in state_dict: + return + + weight = state_dict[key] + + if weight.ndim == 3 and weight.shape == ( + module.in_channels, + module.out_channels, + module.modes_lat_local, + ): + state_dict[key] = weight.view( + 1, module.in_channels, module.out_channels, module.modes_lat_local + ) + def forward(self, x, timer: Timer = NullTimer()): # pragma: no cover dtype = x.dtype residual = x @@ -263,14 +284,10 @@ def forward(self, x, timer: Timer = NullTimer()): # pragma: no cover lora_update = 0.0 with timer.child("dhconv"): - if self.num_groups == 1: - weight = self.weight[None, ...] # expand group dim - else: - weight = self.weight xp = torch.zeros_like(x) xp[..., : self.modes_lat_local, : self.modes_lon_local] = _contract_dhconv( x[..., : self.modes_lat_local, : self.modes_lon_local], - weight, + self.weight, ) xp = xp + self.lora_scaling * lora_update xp = xp.reshape(B, self.out_channels, H, W) From 8449cc21cc828265dd01ea34e3da02cbc615e8cc Mon Sep 17 00:00:00 2001 From: Jeremy McGibbon Date: Thu, 12 Feb 2026 17:33:59 +0000 Subject: [PATCH 3/7] remove redundant check --- fme/core/models/conditional_sfno/s2convolutions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fme/core/models/conditional_sfno/s2convolutions.py b/fme/core/models/conditional_sfno/s2convolutions.py index 22fad38d1..3c0a80b76 100644 --- a/fme/core/models/conditional_sfno/s2convolutions.py +++ b/fme/core/models/conditional_sfno/s2convolutions.py @@ -246,7 +246,7 @@ def _pre_load_hook( weight = state_dict[key] - if weight.ndim == 3 and weight.shape == ( + if weight.shape == ( module.in_channels, module.out_channels, module.modes_lat_local, From f6a2e0181818069eec0d6af7ccd5d313a9565265 Mon Sep 17 00:00:00 2001 From: Jeremy McGibbon Date: Thu, 12 Feb 2026 17:37:08 +0000 Subject: [PATCH 4/7] avoid using unnecessary private version of method --- fme/core/models/conditional_sfno/s2convolutions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fme/core/models/conditional_sfno/s2convolutions.py b/fme/core/models/conditional_sfno/s2convolutions.py index 3c0a80b76..5c61e0bf7 100644 --- a/fme/core/models/conditional_sfno/s2convolutions.py +++ b/fme/core/models/conditional_sfno/s2convolutions.py @@ -227,7 +227,7 @@ def __init__( self.out_channels = out_channels # rewrite old checkpoints on load - self._register_load_state_dict_pre_hook(self._pre_load_hook, with_module=True) + self.register_load_state_dict_pre_hook(self._pre_load_hook) @staticmethod def _pre_load_hook( From b399e71b3dd58dc91a7dc0adb4fae3c3631d6ab3 Mon Sep 17 00:00:00 2001 From: Jeremy McGibbon Date: Thu, 12 Feb 2026 17:42:41 +0000 Subject: [PATCH 5/7] name the pre-load hook --- fme/core/models/conditional_sfno/s2convolutions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fme/core/models/conditional_sfno/s2convolutions.py b/fme/core/models/conditional_sfno/s2convolutions.py index 5c61e0bf7..88c309867 100644 --- a/fme/core/models/conditional_sfno/s2convolutions.py +++ b/fme/core/models/conditional_sfno/s2convolutions.py @@ -227,10 +227,10 @@ def __init__( self.out_channels = out_channels # rewrite old checkpoints on load - self.register_load_state_dict_pre_hook(self._pre_load_hook) + self.register_load_state_dict_pre_hook(self._add_singleton_group_dim) @staticmethod - def _pre_load_hook( + def _add_singleton_group_dim( module: "SpectralConvS2", state_dict: dict[str, torch.Tensor], prefix: str, From 7cfa7db273d428a16676c73d6fe57e4ae49c79b2 Mon Sep 17 00:00:00 2001 From: Jeremy McGibbon Date: Thu, 12 Feb 2026 19:37:12 +0000 Subject: [PATCH 6/7] add regression test, fix code --- .../models/conditional_sfno/s2convolutions.py | 11 +-- fme/core/registry/test_module_registry.py | 84 ++++++++++++++++++ .../NoiseConditionedSFNO_state_dict.pt | Bin 0 -> 69609 bytes 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 fme/core/registry/testdata/NoiseConditionedSFNO_state_dict.pt diff --git a/fme/core/models/conditional_sfno/s2convolutions.py b/fme/core/models/conditional_sfno/s2convolutions.py index 88c309867..e2789d515 100644 --- a/fme/core/models/conditional_sfno/s2convolutions.py +++ b/fme/core/models/conditional_sfno/s2convolutions.py @@ -246,14 +246,15 @@ def _add_singleton_group_dim( weight = state_dict[key] - if weight.shape == ( + ungrouped_shape = ( module.in_channels, module.out_channels, module.modes_lat_local, - ): - state_dict[key] = weight.view( - 1, module.in_channels, module.out_channels, module.modes_lat_local - ) + 2, + ) + + if weight.shape == ungrouped_shape: + state_dict[key] = weight.view(1, *ungrouped_shape) def forward(self, x, timer: Timer = NullTimer()): # pragma: no cover dtype = x.dtype diff --git a/fme/core/registry/test_module_registry.py b/fme/core/registry/test_module_registry.py index 8015648b8..20b885cc0 100644 --- a/fme/core/registry/test_module_registry.py +++ b/fme/core/registry/test_module_registry.py @@ -1,16 +1,23 @@ import dataclasses +import datetime +import pathlib from collections.abc import Iterable import dacite import pytest import torch +import fme +from fme.core.coordinates import HybridSigmaPressureCoordinate, LatLonCoordinates from fme.core.dataset_info import DatasetInfo +from fme.core.dicts import to_flat_dict from fme.core.labels import LabelEncoding from fme.core.registry.module import Module from .module import CONDITIONAL_BUILDERS, ModuleConfig, ModuleSelector +DATA_DIR = pathlib.Path(__file__).parent / "testdata" + class MockModule(torch.nn.Module): def __init__(self, param_shapes: Iterable[tuple[int, ...]]): @@ -71,3 +78,80 @@ def test_build_conditional(): def test_module_selector_raises_with_bad_config(): with pytest.raises(dacite.UnexpectedDataError): ModuleSelector(type="mock", config={"non_existent_key": 1}) + + +def get_noise_conditioned_sfno_module_selector() -> ModuleSelector: + return ModuleSelector( + type="NoiseConditionedSFNO", + config={ + "embed_dim": 8, + "noise_embed_dim": 4, + "noise_type": "isotropic", + "filter_type": "linear", + "use_mlp": True, + "num_layers": 4, + "operator_type": "dhconv", + "affine_norms": True, + "spectral_transform": "sht", + }, + ) + + +def load_or_cache_state(selector_name: str, module: Module) -> dict[str, torch.Tensor]: + state_dict_path = DATA_DIR / f"{selector_name}_state_dict.pt" + if state_dict_path.exists(): + return torch.load(state_dict_path) + else: + state_dict = module.get_state() + torch.save(state_dict, state_dict_path) + raise RuntimeError( + f"State dict for {selector_name} not found. " + f"Created a new one at {state_dict_path}. " + "Please commit it to the repo and run the test again." + ) + + +SELECTORS = { + "NoiseConditionedSFNO": get_noise_conditioned_sfno_module_selector(), +} + + +@pytest.mark.parametrize( + "selector_name", + SELECTORS.keys(), +) +def test_module_backwards_compatibility(selector_name: str): + torch.manual_seed(0) + img_shape = (9, 18) + n_in_channels = 5 + n_out_channels = 6 + all_labels = {"a", "b"} + timestep = datetime.timedelta(hours=6) + device = fme.get_device() + horizontal_coordinate = LatLonCoordinates( + lat=torch.zeros(img_shape[0], device=device), + lon=torch.zeros(img_shape[1], device=device), + ) + vertical_coordinate = HybridSigmaPressureCoordinate( + ak=torch.arange(7, device=device), bk=torch.arange(7, device=device) + ) + dataset_info = DatasetInfo( + horizontal_coordinates=horizontal_coordinate, + vertical_coordinate=vertical_coordinate, + timestep=timestep, + all_labels=all_labels, + ) + module = SELECTORS[selector_name].build( + n_in_channels=n_in_channels, + n_out_channels=n_out_channels, + dataset_info=dataset_info, + ) + loaded_state_dict = load_or_cache_state(selector_name, module) + new_keys = set(to_flat_dict(module.get_state()).keys()).difference( + to_flat_dict(loaded_state_dict).keys() + ) + module.load_state(loaded_state_dict) + assert not new_keys, ( + f"New keys added to state dict: {new_keys}. " + "Please delete and re-generate the regression target." + ) diff --git a/fme/core/registry/testdata/NoiseConditionedSFNO_state_dict.pt b/fme/core/registry/testdata/NoiseConditionedSFNO_state_dict.pt new file mode 100644 index 0000000000000000000000000000000000000000..1ededd76f5cdd2ee2eb08ca2e70443b9d76d8ffd GIT binary patch literal 69609 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 Q*;o3wK>QHq)$ literal 0 HcmV?d00001 From 5e6736e4e3fce8b320eea0f1e414d76ed5635304 Mon Sep 17 00:00:00 2001 From: Jeremy McGibbon Date: Thu, 12 Feb 2026 20:20:40 +0000 Subject: [PATCH 7/7] remove assertion --- fme/core/registry/test_module_registry.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fme/core/registry/test_module_registry.py b/fme/core/registry/test_module_registry.py index 20b885cc0..d2dce58e9 100644 --- a/fme/core/registry/test_module_registry.py +++ b/fme/core/registry/test_module_registry.py @@ -10,7 +10,6 @@ import fme from fme.core.coordinates import HybridSigmaPressureCoordinate, LatLonCoordinates from fme.core.dataset_info import DatasetInfo -from fme.core.dicts import to_flat_dict from fme.core.labels import LabelEncoding from fme.core.registry.module import Module @@ -147,11 +146,4 @@ def test_module_backwards_compatibility(selector_name: str): dataset_info=dataset_info, ) loaded_state_dict = load_or_cache_state(selector_name, module) - new_keys = set(to_flat_dict(module.get_state()).keys()).difference( - to_flat_dict(loaded_state_dict).keys() - ) module.load_state(loaded_state_dict) - assert not new_keys, ( - f"New keys added to state dict: {new_keys}. " - "Please delete and re-generate the regression target." - )