BENO / model /BE_MPNN.py
OneScience's picture
Upload folder using huggingface_hub
7087ec2 verified
Raw
History Blame Contribute Delete
10.4 kB
from typing import List
import torch
import torch.nn as nn
from torch_geometric.nn import MessagePassing
from .transformer import *
def build_mlp(
input_size: int,
hidden_layer_sizes: List[int],
output_size: int = None,
output_activation: nn.Module = nn.Identity,
activation: nn.Module = nn.ReLU) -> nn.Module:
# Size of each layer
layer_sizes = [input_size] + hidden_layer_sizes
if output_size:
layer_sizes.append(output_size)
# Number of layers
nlayers = len(layer_sizes) - 1
# Create a list of activation functions and
# set the last element to output activation function
act = [activation for i in range(nlayers)]
act[-1] = output_activation
# Create a torch sequential container
mlp = nn.Sequential()
for i in range(nlayers):
mlp.add_module("NN-" + str(i), nn.Linear(layer_sizes[i],
layer_sizes[i + 1]))
mlp.add_module("Act-" + str(i), act[i]())
return mlp
class Encoder(nn.Module):
def __init__(
self,
nnode_in_features: int,
nnode_out_features: int, #latent dim=128
nedge_in_features: int,
nedge_out_features: int,
nmlp_layers: int,
mlp_hidden_dim: int,
activation:nn.Module,):
super(Encoder, self).__init__()
# Encode node features as an MLP
self.node_fn = nn.Sequential(*[build_mlp(nnode_in_features,
[mlp_hidden_dim
for _ in range(nmlp_layers)],
nnode_out_features,activation=activation),
nn.LayerNorm(nnode_out_features)])
# Encode edge features as an MLP
self.edge_fn = nn.Sequential(*[build_mlp(nedge_in_features,
[mlp_hidden_dim
for _ in range(nmlp_layers)],
nedge_out_features,activation=activation),
nn.LayerNorm(nedge_out_features)])
self.node_fn_inbd = nn.Sequential(*[build_mlp(nnode_in_features,
[mlp_hidden_dim
for _ in range(nmlp_layers)],
nnode_out_features,activation=activation),
nn.LayerNorm(nnode_out_features)])
self.edge_fn_inbd = nn.Sequential(*[build_mlp(nedge_in_features,
[mlp_hidden_dim
for _ in range(nmlp_layers)],
nedge_out_features,activation=activation),
nn.LayerNorm(nedge_out_features)])
def forward(
self,
x: torch.tensor,
edge_features: torch.tensor,
x_inbd:torch.tensor,
edge_inbd_features: torch.tensor):
return self.node_fn(x), self.edge_fn(edge_features), self.node_fn_inbd(x_inbd), self.edge_fn_inbd(edge_inbd_features)
class InteractionNetwork(MessagePassing):
def __init__(
self,
nnode_in: int,
nnode_out: int,
nedge_in: int,
nedge_out: int,
nmlp_layers: int,
mlp_hidden_dim: int,
boundary_dim: int,
trans_layer:int,
activation: nn.Module = nn.ReLU
):
# Aggregate features from neighbors
super(InteractionNetwork, self).__init__(aggr='mean')
# Node MLP
self.node_fn = nn.Sequential(*[build_mlp(nnode_in + nedge_out + boundary_dim,
[mlp_hidden_dim
for _ in range(nmlp_layers)],
nnode_out,activation=activation),
nn.LayerNorm(nnode_out)])
self.edge_fn = nn.Sequential(*[build_mlp(nnode_in + nnode_in + nedge_in,
[mlp_hidden_dim
for _ in range(nmlp_layers)],
nedge_out,activation=activation),
nn.LayerNorm(nedge_out)])
#boundary
self.boundary_fn = Transformer(enc_in = 3, d_model = boundary_dim, n_heads = 2, enc_layers = trans_layer)
def forward(self,
x: torch.tensor,
edge_index: torch.tensor,
edge_features: torch.tensor,
boundary: torch.tensor,
):
boundary = boundary.unsqueeze(0).float()
boundary = self.boundary_fn(boundary)
x_residual = x.clone()
edge_features_residual = edge_features.clone()
x, edge_features = self.propagate(
edge_index=edge_index, x=x, edge_features=edge_features, boundary=boundary)
return x + x_residual, edge_features + edge_features_residual
def message(self,
x_i: torch.tensor,
x_j: torch.tensor,
edge_features: torch.tensor,
boundary: torch.tensor
) -> torch.tensor:
# Concat edge features with a final shape of [nedges, latent_dim*3]
edge_features = torch.cat([x_i, x_j, edge_features], dim=-1)
edge_features = self.edge_fn(edge_features)
return edge_features
def update(self,
x_updated: torch.tensor,
x: torch.tensor,
edge_features: torch.tensor,
boundary: torch.tensor
):
# Concat node features with a final shape of
boundary_all = boundary.repeat(x.shape[0], 1)
x_updated = torch.cat([x_updated, x,boundary_all], dim=-1)
x_updated = self.node_fn(x_updated)
return x_updated, edge_features
class Processor(MessagePassing):
def __init__(
self,
nnode_in: int,
nnode_out: int,
nedge_in: int,
nedge_out: int,
nmessage_passing_steps: int,
nmlp_layers: int,
mlp_hidden_dim: int,
boundary_dim: int,
trans_layer:int,
activation:nn.Module
):
super(Processor, self).__init__(aggr='mean')
# Create a stack of M Graph Networks GNs.
self.gnn_stacks = nn.ModuleList([
InteractionNetwork(
nnode_in=nnode_in,
nnode_out=nnode_out,
nedge_in=nedge_in,
nedge_out=nedge_out,
nmlp_layers=nmlp_layers,
mlp_hidden_dim=mlp_hidden_dim,
boundary_dim=boundary_dim,
trans_layer=trans_layer,
activation=activation
) for _ in range(nmessage_passing_steps)])
self.gnn_stacks_inbd = nn.ModuleList([
InteractionNetwork(
nnode_in=nnode_in,
nnode_out=nnode_out,
nedge_in=nedge_in,
nedge_out=nedge_out,
nmlp_layers=nmlp_layers,
mlp_hidden_dim=mlp_hidden_dim,
boundary_dim=boundary_dim,
trans_layer=trans_layer,
activation=activation,
) for _ in range(nmessage_passing_steps)])
def forward(self,
x: torch.tensor,
edge_index: torch.tensor,
edge_features: torch.tensor,
boundary:torch.tensor,
x_inbd: torch.tensor,
edge_inbd_index: torch.tensor,
edge_inbd_features: torch.tensor,
boundary_inbd:torch.tensor):
for gnn in self.gnn_stacks:
x, edge_features= gnn(x, edge_index, edge_features,boundary )
for gnn in self.gnn_stacks_inbd:
x_inbd, edge_inbd_features = gnn(x_inbd, edge_inbd_index, edge_inbd_features,boundary_inbd)
return x, edge_features, x_inbd, edge_inbd_features
class Decoder(nn.Module):
def __init__(
self,
nnode_in: int,
nnode_out: int,
nmlp_layers: int,
mlp_hidden_dim: int,
activation:nn.Module):
super(Decoder, self).__init__()
self.node_fn = build_mlp(
nnode_in, [mlp_hidden_dim for _ in range(nmlp_layers)], nnode_out,activation=activation)
self.node_fn_inbd = build_mlp(
nnode_in, [mlp_hidden_dim for _ in range(nmlp_layers)], nnode_out,activation=activation)
def forward(self,
x: torch.tensor,
x_inbd: torch.tensor):
u1=self.node_fn(x)
u2=self.node_fn_inbd(x_inbd)
u=u1+u2
return u
class HeteroGNS(nn.Module):
def __init__(
self,
nnode_in_features: int,
nnode_out_features: int,
nedge_in_features: int,
latent_dim: int = 128,
nmessage_passing_steps: int = 10,
nmlp_layers: int = 2,
mlp_hidden_dim: int = 128,
activation: nn.Module = nn.ELU,
boundary_dim: int=128,
trans_layer: int =3,
):
super(HeteroGNS, self).__init__()
self._encoder = Encoder(
nnode_in_features=nnode_in_features,
nnode_out_features=latent_dim,
nedge_in_features=nedge_in_features,
nedge_out_features=latent_dim,
nmlp_layers=nmlp_layers,
mlp_hidden_dim=mlp_hidden_dim,
activation=activation,
)
self._processor = Processor(
nnode_in=latent_dim,
nnode_out=latent_dim,
nedge_in=latent_dim,
nedge_out=latent_dim,
nmessage_passing_steps=nmessage_passing_steps,
nmlp_layers=nmlp_layers,
mlp_hidden_dim=mlp_hidden_dim,
boundary_dim = boundary_dim,
trans_layer = trans_layer,
activation=activation,
)
self._decoder = Decoder(
nnode_in=latent_dim,
nnode_out=nnode_out_features,
nmlp_layers=nmlp_layers,
mlp_hidden_dim=mlp_hidden_dim,
activation=activation,
)
def forward(self,data):
x, edge_index, edge_features,boundary = data['G1'].x, data['G1'].edge_index, data['G1'].edge_features,data['G1'].boundary
x_inbd, edge_inbd_index, edge_inbd_features,boundary_inbd = data['G2'].x, data['G2'].edge_index,data['G2'].edge_features,data['G2'].boundary
x, edge_features,x_inbd,edge_inbd_features = self._encoder(x, edge_features,x_inbd,edge_inbd_features)
x, edge_features, x_inbd, edge_inbd_features = self._processor(x, edge_index, edge_features, boundary,x_inbd,edge_inbd_index, edge_inbd_features,boundary_inbd)
u = self._decoder(x,x_inbd)
return u