| import torch |
| from typing import Union, Sequence, Dict |
| from math import sqrt |
| from onescience.datapipes.materials.nequip import AtomicDataDict |
|
|
|
|
| class AvgNumNeighborsNorm(torch.nn.Module): |
| def __init__( |
| self, |
| type_names: Sequence[str], |
| avg_num_neighbors: Union[float, Dict[str, float]], |
| ) -> None: |
| """ |
| Module to normalize features during training using per type edge sum normalization. |
| |
| Args: |
| type_names (Sequence[str]): list of atom type names |
| avg_num_neighbors (float/Dict[str, float]): used to normalize edge sums for better numerics |
| """ |
| super().__init__() |
| assert avg_num_neighbors is not None, "avg_num_neighbors must be specified" |
|
|
| self.in_field = self.out_field = AtomicDataDict.NODE_FEATURES_KEY |
| self.norm_key = AtomicDataDict.FEATURE_NORM_FACTOR_KEY |
|
|
| |
| if isinstance(avg_num_neighbors, (float, int)): |
| avg_num_neighbors = [avg_num_neighbors] |
| elif isinstance(avg_num_neighbors, dict): |
| assert set(type_names) == set(avg_num_neighbors.keys()) |
| avg_num_neighbors = [avg_num_neighbors[k] for k in type_names] |
| else: |
| raise RuntimeError( |
| "Unrecognized format for `avg_num_neighbors`, only floats or dicts allowed." |
| ) |
| assert isinstance(avg_num_neighbors, list) |
|
|
| |
| norm_const = torch.tensor([(1.0 / sqrt(N)) for N in avg_num_neighbors]) |
| norm_const = norm_const.reshape(-1, 1) |
| |
| |
| self.register_buffer("norm_const", norm_const, persistent=False) |
|
|
| |
| self.norm_shortcut = self.norm_const.numel() == 1 |
|
|
| def forward(self, data: AtomicDataDict.Type) -> AtomicDataDict.Type: |
| features = data[self.in_field] |
| norm_size = features.size(0) |
|
|
| if self.norm_key in data and data[self.norm_key].size(0) == norm_size: |
| norm_factor = data[self.norm_key] |
| else: |
| |
| if self.norm_shortcut: |
| |
| norm_factor = self.norm_const.expand(norm_size, -1) |
| else: |
| |
| norm_factor = torch.nn.functional.embedding( |
| data[AtomicDataDict.ATOM_TYPE_KEY][:norm_size], |
| self.norm_const, |
| ) |
| data[self.norm_key] = norm_factor |
|
|
| data[self.out_field] = norm_factor * features |
| return data |
|
|