| 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: |
|
|
| |
| layer_sizes = [input_size] + hidden_layer_sizes |
| if output_size: |
| layer_sizes.append(output_size) |
|
|
| |
| nlayers = len(layer_sizes) - 1 |
|
|
| |
| |
| act = [activation for i in range(nlayers)] |
| act[-1] = output_activation |
|
|
| |
| 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, |
| nedge_in_features: int, |
| nedge_out_features: int, |
| nmlp_layers: int, |
| mlp_hidden_dim: int, |
| activation:nn.Module,): |
| |
| super(Encoder, self).__init__() |
| |
| 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)]) |
| |
| 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 |
| ): |
|
|
| |
| super(InteractionNetwork, self).__init__(aggr='mean') |
| |
| 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)]) |
|
|
| |
| 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: |
|
|
| |
|
|
| 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 |
| ): |
| |
| |
| 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') |
| |
| 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 |
|
|