PDENNEval / model /deeponet.py
OneScience's picture
Upload folder using huggingface_hub
e2703dc verified
Raw
History Blame Contribute Delete
10.8 kB
# coding=utf-8
import torch
import torch.nn as nn
from onescience.utils.pdenneval.deeponet_utils import _get_act, _get_initializer
class MLP(nn.Module):
"""Fully-connected neural network."""
def __init__(self, layer_sizes, activation, kernel_initializer):
super().__init__()
self.activation = _get_act(activation)
initializer = _get_initializer(kernel_initializer)
initializer_zero = _get_initializer("zeros")
self.linears = torch.nn.ModuleList()
for i in range(1, len(layer_sizes)):
self.linears.append(
torch.nn.Linear(
layer_sizes[i - 1], layer_sizes[i], dtype=torch.float32
)
)
initializer(self.linears[-1].weight)
initializer_zero(self.linears[-1].bias)
def forward(self, inputs):
x = inputs
for linear in self.linears[:-1]:
x = self.activation(linear(x))
x = self.linears[-1](x)
return x
class Modified_MLP(nn.Module):
def __init__(self, layer_sizes, activation, kernel_initializer) -> None:
super().__init__()
self.activation = _get_act(activation)
initializer = _get_initializer(kernel_initializer)
initializer_zero = _get_initializer("zeros")
self.linears = torch.nn.ModuleList()
for i in range(1, len(layer_sizes)):
self.linears.append(
torch.nn.Linear(
layer_sizes[i - 1], layer_sizes[i], dtype=torch.float32
)
)
initializer(self.linears[-1].weight)
initializer_zero(self.linears[-1].bias)
self.linear1=torch.nn.Linear(layer_sizes[0], layer_sizes[1], dtype=torch.float32)
self.linear2=torch.nn.Linear(layer_sizes[0], layer_sizes[1], dtype=torch.float32)
initializer(self.linear1.weight),initializer(self.linear2.weight)
initializer_zero(self.linear1.bias),initializer_zero(self.linear2.bias)
def forward(self, inputs):
U = self.activation(self.linear1(inputs))
V = self.activation(self.linear2(inputs))
for linear in self.linears[:-1]:
outputs=torch.sigmoid(linear(inputs))
inputs= outputs*U + (1-outputs)* V
outputs = self.linears[-1](inputs)
return outputs
class DeepONet(nn.Module):
"""Deep operator network.
Args:
layer_sizes_branch: A list of integers as the width of a fully connected network,
or `(dim, f)` where `dim` is the input dimension and `f` is a network
function. The width of the last layer in the branch and trunk net should be
equal.
layer_sizes_trunk (list): A list of integers as the width of a fully connected
network.
activation: If `activation` is a ``string``, then the same activation is used in
both trunk and branch nets. If `activation` is a ``dict``, then the trunk
net uses the activation `activation["trunk"]`, and the branch net uses
`activation["branch"]`.
"""
def __init__(
self,
layer_sizes_branch,
layer_sizes_trunk,
activation,
kernel_initializer,
):
super().__init__()
if isinstance(activation, dict):
activation_branch = _get_act(activation["branch"])
self.activation_trunk = _get_act(activation["trunk"])
else:
activation_branch = self.activation_trunk = _get_act(activation)
if callable(layer_sizes_branch[0]):
# User-defined network
self.branch = layer_sizes_branch[0]
else:
# Fully connected network
self.branch = MLP(layer_sizes_branch, activation_branch, kernel_initializer)
self.trunk = MLP(layer_sizes_trunk, self.activation_trunk, kernel_initializer)
self.b = torch.nn.parameter.Parameter(torch.tensor(0.0))
def forward(self, inputs):
x_func = inputs[0]
x_loc = inputs[1]
# Branch net to encode the input function
x_func = self.branch(x_func)
# Trunk net to encode the domain of the output function
x_loc = self.activation_trunk(self.trunk(x_loc))
# Dot product
if x_func.shape[-1] != x_loc.shape[-1]:
raise AssertionError(
"Output sizes of branch net and trunk net do not match."
)
x = torch.einsum("bi,bi->b", x_func, x_loc)
x = torch.unsqueeze(x, 1)
# Add bias
x += self.b
return x
class DeepONetCartesianProd(nn.Module):
"""Deep operator network for dataset in the format of Cartesian product.
Args:
layer_sizes_branch: A list of integers as the width of a fully connected network,
or `(dim, f)` where `dim` is the input dimension and `f` is a network
function. The width of the last layer in the branch and trunk net should be
equal.
layer_sizes_trunk (list): A list of integers as the width of a fully connected
network.
activation: If `activation` is a ``string``, then the same activation is used in
both trunk and branch nets. If `activation` is a ``dict``, then the trunk
net uses the activation `activation["trunk"]`, and the branch net uses
`activation["branch"]`.
"""
def __init__(
self,
layer_sizes_branch,
layer_sizes_trunk,
activation,
kernel_initializer,
base_model = "MLP" # or Modified_MLP
):
super().__init__()
if isinstance(activation, dict):
activation_branch = _get_act(activation["branch"])
self.activation_trunk = _get_act(activation["trunk"])
else:
activation_branch = self.activation_trunk = _get_act(activation)
base_model= MLP if base_model=="MLP" else Modified_MLP
if callable(layer_sizes_branch[0]):
# User-defined network
self.branch = layer_sizes_branch[0]
else:
self.branch = base_model(layer_sizes_branch, activation_branch, kernel_initializer)
self.trunk = base_model(layer_sizes_trunk, self.activation_trunk, kernel_initializer)
self.b = torch.nn.parameter.Parameter(torch.tensor(0.0))
def forward(self, inputs):
x_func = inputs[0]
x_loc = inputs[1]
# Branch net to encode the input function
x_func = self.branch(x_func)
# Trunk net to encode the domain of the output function
x_loc = self.activation_trunk(self.trunk(x_loc))
# Dot product
if x_func.shape[-1] != x_loc.shape[-1]:
raise AssertionError(
"Output sizes of branch net and trunk net do not match."
)
x = torch.einsum("bi,ni->bn", x_func, x_loc)
# Add bias
x += self.b
return x
class DeepONetCartesianProd2D(DeepONetCartesianProd):
# For multiple outputs, we choose the second approach mentioned in "https://arxiv.org/abs/2111.05512", i.e. split
# the output of both the branch and the trunk into n groups, and the k-th groups outputs the k-th solution.
def __init__(self,
size: int,
in_channel_branch: int,
query_dim: int ,
out_channel: int,
activation: str = "relu",
kernel_initializer: str = "Glorot normal",
base_model = "MLP"):
layer_sizes_branch = [in_channel_branch*size**2]+[128]*4+[128*out_channel]
layer_sizes_trunk= [query_dim]+[128]*4+[128*out_channel]
super().__init__(layer_sizes_branch,layer_sizes_trunk,activation,kernel_initializer,base_model)
self.out_channel = out_channel
self.query_dim=query_dim
self.b = torch.nn.parameter.Parameter(torch.zeros(out_channel,dtype=torch.float32))
def forward(self, inputs):
x_func = inputs[0]
x_loc = inputs[1]
batchsize=x_func.shape[0]
x_func = x_func.reshape([batchsize,-1])
grid_shape = x_loc.shape[:-1]
x_loc = x_loc.reshape([-1,self.query_dim]) #(num_point, query_dim)
num_points=x_loc.shape[0]
# Branch net to encode the input function
x_func = self.branch(x_func.reshape([batchsize,-1]))
# Trunk net to encode the domain of the output function
x_loc = self.activation_trunk(self.trunk(x_loc))
# Dot product
if x_func.shape[-1] != x_loc.shape[-1]:
raise AssertionError(
"Output sizes of branch net and trunk net do not match."
)
x_func = x_func.reshape([batchsize,self.out_channel,-1])
x_loc = x_loc.reshape([num_points,self.out_channel,-1])
x = torch.einsum("bci,nci->bnc", x_func, x_loc)
# Add bias
x += self.b
return x.reshape([-1,*grid_shape,self.out_channel])
class DeepONetCartesianProd1D(DeepONetCartesianProd):
# For multiple outputs, we choose the second approach mentioned in "https://arxiv.org/abs/2111.05512", i.e. split
# the output of both the branch and the trunk into n groups, and the k-th groups outputs the k-th solution.
def __init__(self,
size :int,
in_channel_branch: int,
query_dim: int ,
out_channel: int,
activation: str = "relu",
kernel_initializer: str = "Glorot normal",
base_model="MLP"):
layer_sizes_branch= [in_channel_branch*size]+[128]*4+[128*out_channel]
layer_sizes_trunk= [query_dim]+[128]*3+[128*out_channel]
super().__init__(layer_sizes_branch,layer_sizes_trunk,activation,kernel_initializer,base_model)
self.out_channel = out_channel
self.b = torch.nn.parameter.Parameter(torch.zeros(out_channel,dtype=torch.float32))
self.query_dim=query_dim
def forward(self, inputs):
x_func = inputs[0]
x_loc = inputs[1]
grid_shape = x_loc.shape[:-1]
x_loc = x_loc.reshape([-1,self.query_dim]) #(num_point, query_dim)
num_points=x_loc.shape[0]
batchsize=x_func.shape[0]
x_func = x_func.reshape([batchsize,-1])
# Branch net to encode the input function
x_func = self.branch(x_func)
# Trunk net to encode the domain of the output function
x_loc = self.activation_trunk(self.trunk(x_loc))
# Dot product
if x_func.shape[-1] != x_loc.shape[-1]:
raise AssertionError(
"Output sizes of branch net and trunk net do not match."
)
x_func = x_func.reshape([batchsize,self.out_channel,-1])
x_loc = x_loc.reshape([num_points,self.out_channel,-1])
x = torch.einsum("bci,nci->bnc", x_func, x_loc)
# Add bias
x += self.b
return x.reshape([-1,*grid_shape,self.out_channel])