NVIDIA Modulus Sym (Latest Release)
Sym (Latest Release)

deeplearning/modulus/modulus-sym/_modules/modulus/sym/models/fourier_net.html

Source code for modulus.sym.models.fourier_net

# SPDX-FileCopyrightText: Copyright (c) 2023 - 2024 NVIDIA CORPORATION & AFFILIATES.
# SPDX-FileCopyrightText: All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import torch
from torch import Tensor
from typing import Dict, List, Tuple

import modulus.sym.models.fully_connected as fully_connected
from modulus.models.layers import FourierLayer
from modulus.sym.models.activation import Activation
from modulus.sym.models.arch import Arch
from modulus.sym.key import Key


[docs]class FourierNetArch(Arch): """Fourier encoding fully-connected neural network. This network is a fully-connected neural network that encodes the input features into Fourier space using sinesoidal activation functions. This helps reduce spectal bias during training. Parameters ---------- input_keys : List[Key] Input key list. output_keys : List[Key] Output key list. detach_keys : List[Key], optional List of keys to detach gradients, by default [] frequencies : Tuple, optional A tuple that describes the Fourier encodings to use any inputs in the list `['x', 'y', 'z', 't']`. The first element describes the type of frequency encoding with options, `'gaussian', 'full', 'axis', 'diagonal'`, by default ("axis", [i for i in range(10)]) :obj:`'gaussian'` samples frequency of Fourier series from Gaussian. :obj:`'axis'` samples along axis of spectral space with the given list range of frequencies. :obj:`'diagonal'` samples along diagonal of spectral space with the given list range of frequencies. :obj:`'full'` samples along entire spectral space for all combinations of frequencies in given list. frequencies_params : Tuple, optional Same as `frequencies` used for encodings of any inputs not in the list `['x', 'y', 'z', 't']`. By default ("axis", [i for i in range(10)]) activation_fn : Activation, optional Activation function, by default :obj:`Activation.SILU` layer_size : int, optional Layer size for every hidden layer of the model, by default 512 nr_layers : int, optional Number of hidden layers of the model, by default 6 skip_connections : bool, optional Apply skip connections every 2 hidden layers, by default False weight_norm : bool, optional Use weight norm on fully connected layers, by default True adaptive_activations : bool, optional Use an adaptive activation functions, by default False Variable Shape -------------- - Input variable tensor shape: :math:`[N, size]` - Output variable tensor shape: :math:`[N, size]` Example ------- Gaussian frequencies >>> std = 1.0; num_freq = 10 >>> model = .fourier_net.FourierNetArch( >>> [Key("x", size=2)], >>> [Key("y", size=2)], >>> frequencies=("gaussian", std, num_freq)) Diagonal frequencies >>> frequencies = [1.0, 2.0, 3.0, 4.0] >>> model = .fourier_net.FourierNetArch( >>> [Key("x", size=2)], >>> [Key("y", size=2)], >>> frequencies=("diagonal", frequencies)) Full frequencies >>> frequencies = [1.0, 2.0, 3.0, 4.0] >>> model = .fourier_net.FourierNetArch( >>> [Key("x", size=2)], >>> [Key("y", size=2)], >>> frequencies=("full", frequencies)) Note ---- For information regarding adaptive activations please refer to https://arxiv.org/abs/1906.01170. """ def __init__( self, input_keys: List[Key], output_keys: List[Key], detach_keys: List[Key] = [], frequencies: Tuple = ("axis", [i for i in range(10)]), frequencies_params: Tuple = ("axis", [i for i in range(10)]), activation_fn: Activation = Activation.SILU, layer_size: int = 512, nr_layers: int = 6, skip_connections: bool = False, weight_norm: bool = True, adaptive_activations: bool = False, ) -> None: super().__init__( input_keys=input_keys, output_keys=output_keys, detach_keys=detach_keys ) if frequencies_params is None: frequencies_params = frequencies self.xyzt_var = [x for x in self.input_key_dict if x in ["x", "y", "z", "t"]] # Prepare slice index xyzt_slice_index = self.prepare_slice_index(self.input_key_dict, self.xyzt_var) self.register_buffer("xyzt_slice_index", xyzt_slice_index, persistent=False) self.params_var = [ x for x in self.input_key_dict if x not in ["x", "y", "z", "t"] ] params_slice_index = self.prepare_slice_index( self.input_key_dict, self.params_var ) self.register_buffer("params_slice_index", params_slice_index, persistent=False) in_features_xyzt = sum( (v for k, v in self.input_key_dict.items() if k in self.xyzt_var) ) in_features_params = sum( (v for k, v in self.input_key_dict.items() if k in self.params_var) ) in_features = in_features_xyzt + in_features_params out_features = sum(self.output_key_dict.values()) if in_features_xyzt > 0: self.fourier_layer_xyzt = FourierLayer( in_features=in_features_xyzt, frequencies=frequencies ) in_features += self.fourier_layer_xyzt.out_features() else: self.fourier_layer_xyzt = None if in_features_params > 0: self.fourier_layer_params = FourierLayer( in_features=in_features_params, frequencies=frequencies_params ) in_features += self.fourier_layer_params.out_features() else: self.fourier_layer_params = None self.fc = fully_connected.FullyConnectedArchCore( in_features=in_features, layer_size=layer_size, out_features=out_features, nr_layers=nr_layers, skip_connections=skip_connections, activation_fn=activation_fn, adaptive_activations=adaptive_activations, weight_norm=weight_norm, ) def _tensor_forward(self, x: Tensor) -> Tensor: x = self.process_input( x, self.input_scales_tensor, input_dict=self.input_key_dict, dim=-1 ) if self.fourier_layer_xyzt is not None: in_xyzt_var = self.slice_input(x, self.xyzt_slice_index, dim=-1) fourier_xyzt = self.fourier_layer_xyzt(in_xyzt_var) x = torch.cat((x, fourier_xyzt), dim=-1) if self.fourier_layer_params is not None: in_params_var = self.slice_input(x, self.params_slice_index, dim=-1) fourier_params = self.fourier_layer_params(in_params_var) x = torch.cat((x, fourier_params), dim=-1) x = self.fc(x) x = self.process_output(x, self.output_scales_tensor) return x
[docs] def forward(self, in_vars: Dict[str, Tensor]) -> Dict[str, Tensor]: x = self.concat_input( in_vars, self.input_key_dict.keys(), detach_dict=self.detach_key_dict, dim=-1, ) y = self._tensor_forward(x) return self.split_output(y, self.output_key_dict, dim=-1)

def _dict_forward(self, in_vars: Dict[str, Tensor]) -> Dict[str, Tensor]: """ This is the original forward function, left here for the correctness test. """ x = self.prepare_input( in_vars, self.input_key_dict.keys(), detach_dict=self.detach_key_dict, dim=-1, input_scales=self.input_scales, ) if self.fourier_layer_xyzt is not None: in_xyzt_var = self.prepare_input( in_vars, self.xyzt_var, detach_dict=self.detach_key_dict, dim=-1, input_scales=self.input_scales, ) fourier_xyzt = self.fourier_layer_xyzt(in_xyzt_var) x = torch.cat((x, fourier_xyzt), dim=-1) if self.fourier_layer_params is not None: in_params_var = self.prepare_input( in_vars, self.params_var, detach_dict=self.detach_key_dict, dim=-1, input_scales=self.input_scales, ) fourier_params = self.fourier_layer_params(in_params_var) x = torch.cat((x, fourier_params), dim=-1) x = self.fc(x) return self.prepare_output( x, self.output_key_dict, dim=-1, output_scales=self.output_scales )

© Copyright 2023, NVIDIA Modulus Team. Last updated on Nov 27, 2024.