| |
| import torch |
| from typing import Final, Callable, Optional, List |
|
|
|
|
| |
| |
| _MODEL_MODIFIER_PERSISTENT_ATTR_NAME: Final[str] = ( |
| "_nequip_model_modifier_is_persistent" |
| ) |
| _MODEL_MODIFIER_PRIVATE_ATTR_NAME: Final[str] = "_nequip_model_modifier_is_private" |
|
|
| |
| _MODEL_MODIFIER_UNSUPPORTED_DEVICES_ATTR_NAME: Final[str] = ( |
| "_nequip_model_modifier_unsupported_devices" |
| ) |
| _MODEL_MODIFIER_SUPPORTED_COMPILE_MODES_ATTR_NAME: Final[str] = ( |
| "_nequip_model_modifier_supported_compile_modes" |
| ) |
|
|
|
|
| def model_modifier( |
| persistent: bool, |
| private: Optional[bool] = None, |
| unsupported_devices: List[str] = [], |
| supported_compile_modes: Optional[List[str]] = None, |
| ): |
| """ |
| Mark a ``@classmethod`` of an ``nn.Module`` as a "model modifier" that can be applied by the user to modify a packaged or other loaded model on-the-fly. Model modifiers must be a ``@classmethod`` of one of the ``nn.Module`` objects in the model. |
| |
| Args: |
| persistent (bool): Whether the modifier should be applied when building the model for packaging. |
| private (bool, optional): Whether the modifier is private and should not be exposed in public interfaces. Defaults to None. |
| unsupported_devices (List[str], optional): List of device types that this modifier does not support. Defaults to []. |
| supported_compile_modes (List[str], optional): List of compile modes that this modifier supports. Defaults to None. |
| """ |
|
|
| def decorator(func): |
| assert isinstance(func, classmethod), ( |
| "@model_modifier must be applied after @classmethod" |
| ) |
| assert not hasattr(func.__func__, _MODEL_MODIFIER_PERSISTENT_ATTR_NAME) |
|
|
| setattr(func.__func__, _MODEL_MODIFIER_PERSISTENT_ATTR_NAME, persistent) |
|
|
| if private is not None: |
| setattr(func.__func__, _MODEL_MODIFIER_PRIVATE_ATTR_NAME, private) |
|
|
| setattr( |
| func.__func__, |
| _MODEL_MODIFIER_UNSUPPORTED_DEVICES_ATTR_NAME, |
| unsupported_devices, |
| ) |
|
|
| setattr( |
| func.__func__, |
| _MODEL_MODIFIER_SUPPORTED_COMPILE_MODES_ATTR_NAME, |
| supported_compile_modes, |
| ) |
|
|
| return func |
|
|
| return decorator |
|
|
|
|
| def is_model_modifier(func: callable) -> bool: |
| |
| return hasattr(func, _MODEL_MODIFIER_PERSISTENT_ATTR_NAME) |
|
|
|
|
| def is_persistent_model_modifier(func: callable) -> bool: |
| return getattr(func, _MODEL_MODIFIER_PERSISTENT_ATTR_NAME) |
|
|
|
|
| def is_private_model_modifier(func: callable) -> Optional[bool]: |
| |
| |
| |
| return getattr(func, _MODEL_MODIFIER_PRIVATE_ATTR_NAME, False) |
|
|
|
|
| def get_model_modifier_unsupported_devices(func: callable) -> List[str]: |
| """Get the list of unsupported devices for a model modifier. Returns empty list for backwards compatibility.""" |
| return getattr(func, _MODEL_MODIFIER_UNSUPPORTED_DEVICES_ATTR_NAME, []) |
|
|
|
|
| def get_model_modifier_supported_compile_modes(func: callable) -> Optional[List[str]]: |
| """Get the list of supported compile modes for a model modifier. Returns None if not set.""" |
| return getattr(func, _MODEL_MODIFIER_SUPPORTED_COMPILE_MODES_ATTR_NAME, None) |
|
|
|
|
| def replace_submodules( |
| model: torch.nn.Module, |
| target_cls: type, |
| factory: Callable[[torch.nn.Module], torch.nn.Module], |
| ) -> torch.nn.Module: |
| """ |
| Recursively walk the children of ``model``, and whenever we see an instance of ``target_cls``, replace it (in-place) with ``factory(old_module)`` by mutating ``model._modules[name]``. |
| """ |
| for name, child in list(model.named_children()): |
| if isinstance(child, target_cls): |
| |
| model._modules[name] = factory(child) |
| else: |
| |
| replace_submodules(child, target_cls, factory) |
| return model |
|
|