chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:36:30 +08:00
commit 55ab4e4a73
473 changed files with 72932 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
from .dhcf import DHCF
from .hgnn import HGNN
from .hgnnp import HGNNP
from .hnhn import HNHN
from .hwnn import HWNN
from .hypergcn import HyperGCN
from .setgnn import SetGNN
from .unignn import UniGAT
from .unignn import UniGCN
from .unignn import UniGIN
from .unignn import UniSAGE
+95
View File
@@ -0,0 +1,95 @@
from typing import Tuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from easygraph.classes import Hypergraph
class DHCF(nn.Module):
r"""The DHCF model proposed in `Dual Channel Hypergraph Collaborative Filtering <https://dl.acm.org/doi/10.1145/3394486.3403253>`_ paper (KDD 2020).
.. note::
The user and item embeddings and trainable parameters are initialized with xavier_uniform distribution.
Parameters:
``num_users`` (``int``): The Number of users.
``num_items`` (``int``): The Number of items.
``emb_dim`` (``int``): Embedding dimension.
``num_layers`` (``int``): The Number of layers. Defaults to ``3``.
``drop_rate`` (``float``): The dropout probability. Defaults to ``0.5``.
"""
def __init__(
self,
num_users: int,
num_items: int,
emb_dim: int,
num_layers: int = 3,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.num_users, self.num_items = num_users, num_items
self.num_layers = num_layers
self.drop_rate = drop_rate
self.u_embedding = nn.Embedding(num_users, emb_dim)
self.i_embedding = nn.Embedding(num_items, emb_dim)
self.W_gc, self.W_bi = nn.ModuleList(), nn.ModuleList()
for _ in range(self.num_layers):
self.W_gc.append(nn.Linear(emb_dim, emb_dim))
self.W_bi.append(nn.Linear(emb_dim, emb_dim))
self.reset_parameters()
def reset_parameters(self):
r"""Initialize learnable parameters."""
nn.init.xavier_uniform_(self.u_embedding.weight)
nn.init.xavier_uniform_(self.i_embedding.weight)
for W_gc, W_bi in zip(self.W_gc, self.W_bi):
nn.init.xavier_uniform_(W_gc.weight)
nn.init.xavier_uniform_(W_bi.weight)
nn.init.constant_(W_gc.bias, 0)
nn.init.constant_(W_bi.bias, 0)
def forward(
self, hg_ui: Hypergraph, hg_iu: Hypergraph
) -> Tuple[torch.Tensor, torch.Tensor]:
r"""The forward function.
Parameters:
``hg_ui`` (``eg.Hypergraph``): The hypergraph structure that users as vertices.
``hg_iu`` (``eg.Hypergraph``): The hypergraph structure that items as vertices.
"""
u_embs = self.u_embedding.weight
i_embs = self.i_embedding.weight
all_embs = torch.cat([u_embs, i_embs], dim=0)
embs_list = [all_embs]
for _idx in range(self.num_layers):
u_embs, i_embs = torch.split(
all_embs, [self.num_users, self.num_items], dim=0
)
# ==========================================================================================
# Two JHConv Layers for users and items, respectively.
u_embs = hg_ui.smoothing_with_HGNN(u_embs)
i_embs = hg_iu.smoothing_with_HGNN(i_embs)
g_embs = torch.cat([u_embs, i_embs], dim=0)
sum_embs = F.leaky_relu(
self.W_gc[_idx](g_embs) + g_embs, negative_slope=0.2
)
# ==========================================================================================
bi_embs = all_embs * g_embs
bi_embs = F.leaky_relu(self.W_bi[_idx](bi_embs), negative_slope=0.2)
all_embs = sum_embs + bi_embs
all_embs = F.dropout(all_embs, p=self.drop_rate, training=self.training)
all_embs = F.normalize(all_embs, p=2, dim=1)
embs_list.append(all_embs)
embs = torch.stack(embs_list, dim=1)
embs = torch.mean(embs, dim=1)
u_embs, i_embs = torch.split(embs, [self.num_users, self.num_items], dim=0)
return u_embs, i_embs
+70
View File
@@ -0,0 +1,70 @@
import torch
import torch.nn as nn
class DHNE(nn.Module):
r"""The DHNE model proposed in `Structural Deep Embedding for Hyper-Networks <https://arxiv.org/abs/1711.10146>`_ paper (AAAI 2018).
Parameters:
``dim_feature`` (``int``): : feature dimension list ( len = 3)
``embedding_size`` (``int``): :The embedding dimension size
``hidden_size`` (``int``): The hidden full connected layer size.
"""
def __init__(self, dim_feature, embedding_size, hidden_size):
super(DHNE, self).__init__()
self.dim_feature = dim_feature
self.embedding_size = embedding_size
self.hidden_size = hidden_size
self.encode0 = nn.Sequential(
nn.Linear(
in_features=self.dim_feature[0], out_features=self.embedding_size[0]
)
)
self.encode1 = nn.Sequential(
nn.Linear(
in_features=self.dim_feature[1], out_features=self.embedding_size[1]
)
)
self.encode2 = nn.Sequential(
nn.Linear(
in_features=self.dim_feature[2], out_features=self.embedding_size[2]
)
)
self.decode_layer0 = nn.Linear(
in_features=self.embedding_size[0], out_features=self.dim_feature[0]
)
self.decode_layer1 = nn.Linear(
in_features=self.embedding_size[1], out_features=self.dim_feature[1]
)
self.decode_layer2 = nn.Linear(
in_features=self.embedding_size[2], out_features=self.dim_feature[2]
)
self.hidden_layer = nn.Linear(
in_features=sum(self.embedding_size), out_features=self.hidden_size
)
self.ouput_layer = nn.Linear(in_features=self.hidden_size, out_features=1)
def forward(self, input0, input1, input2):
input0 = self.encode0(input0)
input0 = torch.tanh(input0)
decode0 = self.decode_layer0(input0)
decode0 = torch.sigmoid(decode0)
input1 = self.encode1(input1)
input1 = torch.tanh(input1)
decode1 = self.decode_layer1(input1)
decode1 = torch.sigmoid(decode1)
input2 = self.encode2(input2)
input2 = torch.tanh(input2)
decode2 = self.decode_layer2(input2)
decode2 = torch.sigmoid(decode2)
merged = torch.tanh(torch.cat((input0, input1, input2), dim=1))
merged = self.hidden_layer(merged)
merged = self.ouput_layer(merged)
merged = torch.sigmoid(merged)
return [decode0, decode1, decode2, merged]
+45
View File
@@ -0,0 +1,45 @@
import torch
import torch.nn as nn
from easygraph.nn import HGNNConv
class HGNN(nn.Module):
r"""The HGNN model proposed in `Hypergraph Neural Networks <https://arxiv.org/pdf/1809.09401>`_ paper (AAAI 2019).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to 0.5.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
use_bn: bool = False,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.layers = nn.ModuleList()
self.layers.append(
HGNNConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
)
self.layers.append(
HGNNConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
)
def forward(self, X: torch.Tensor, hg: "Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
for layer in self.layers:
X = layer(X, hg)
return X
+44
View File
@@ -0,0 +1,44 @@
import torch
import torch.nn as nn
from easygraph.nn import HGNNPConv
class HGNNP(nn.Module):
r"""The HGNN :sup:`+` model proposed in `HGNN+: General Hypergraph Neural Networks <https://ieeexplore.ieee.org/document/9795251>`_ paper (IEEE T-PAMI 2022).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
use_bn: bool = False,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.layers = nn.ModuleList()
self.layers.append(
HGNNPConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
)
self.layers.append(
HGNNPConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
)
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
for layer in self.layers:
X = layer(X, hg)
return X
+44
View File
@@ -0,0 +1,44 @@
import torch
import torch.nn as nn
from easygraph.nn import HNHNConv
class HNHN(nn.Module):
r"""The HNHN model proposed in `HNHN: Hypergraph Networks with Hyperedge Neurons <https://arxiv.org/pdf/2006.12278.pdf>`_ paper (ICML 2020).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
use_bn: bool = False,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.layers = nn.ModuleList()
self.layers.append(
HNHNConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
)
self.layers.append(
HNHNConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
)
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
for layer in self.layers:
X = layer(X, hg)
return X
+60
View File
@@ -0,0 +1,60 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from easygraph.nn import HWNNConv
class HWNN(nn.Module):
r"""The HWNN model proposed in `Heterogeneous Hypergraph Embedding for Graph Classification <https://arxiv.org/abs/2010.10728>`_ paper (WSDM 2021).
Parameters:
``in_channels`` (``int``): Number of input feature channels. :math:`C_{in}` is the dimension of input features.
``num_classes`` (``int``): Number of target classes for classification.
``ncount`` (``int``): Total number of nodes in the hypergraph.
``hyper_snapshot_num`` (``int``, optional): number of sementic snapshots for the given heterogeneous hypergraph.
``hid_channels`` (``int``, optional): Number of hidden units. :math:`C_{hid}` is the dimension of hidden representations. Defaults to 128.
``drop_rate`` (``float``, optional): Dropout probability for regularization. Defaults to 0.01.
"""
def __init__(
self,
in_channels: int,
num_classes: int,
ncount: int,
hyper_snapshot_num: int = 1,
hid_channels: int = 128,
drop_rate: float = 0.01,
) -> None:
super().__init__()
self.drop_rate = drop_rate
self.convolution_1 = HWNNConv(
in_channels, hid_channels, ncount, K1=3, K2=3, approx=True
)
self.convolution_2 = HWNNConv(
hid_channels, num_classes, ncount, K1=3, K2=3, approx=True
)
self.par = torch.nn.Parameter(torch.Tensor(hyper_snapshot_num))
torch.nn.init.uniform_(self.par, 0, 0.99)
def forward(self, X: torch.Tensor, hgs: list) -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
``hgs`` (``list`` of ``Hypergraph``): A list of hypergraph structures whcih stands for snapshots.
"""
channel = []
hyper_snapshot_num = len(hgs)
for snap_index in range(hyper_snapshot_num):
hg = hgs[snap_index]
Y = F.relu(self.convolution_1(X, hg))
Y = F.dropout(Y, self.drop_rate)
Y = self.convolution_2(Y, hg)
Y = F.log_softmax(Y, dim=1)
channel.append(Y)
X = torch.zeros_like(channel[0])
for ind in range(hyper_snapshot_num):
X = X + self.par[ind] * channel[ind]
return X
+67
View File
@@ -0,0 +1,67 @@
import torch
import torch.nn as nn
from easygraph.classes import Hypergraph
from easygraph.nn import HyperGCNConv
class HyperGCN(nn.Module):
r"""The HyperGCN model proposed in `HyperGCN: A New Method of Training Graph Convolutional Networks on Hypergraphs <https://papers.nips.cc/paper/2019/file/1efa39bcaec6f3900149160693694536-Paper.pdf>`_ paper (NeurIPS 2019).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``use_mediator`` (``str``): Whether to use mediator to transform the hyperedges to edges in the graph. Defaults to ``False``.
``fast`` (``bool``): If set to ``True``, the transformed graph structure will be computed once from the input hypergraph and vertex features, and cached for future use. Defaults to ``True``.
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to 0.5.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
use_mediator: bool = False,
use_bn: bool = False,
fast: bool = True,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.fast = fast
self.cached_g = None
self.with_mediator = use_mediator
self.layers = nn.ModuleList()
self.layers.append(
HyperGCNConv(
in_channels,
hid_channels,
use_mediator,
use_bn=use_bn,
drop_rate=drop_rate,
)
)
self.layers.append(
HyperGCNConv(
hid_channels, num_classes, use_mediator, use_bn=use_bn, is_last=True
)
)
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
if self.fast:
if self.cached_g is None:
self.cached_g = Hypergraph.from_hypergraph_hypergcn(
hg, X, self.with_mediator
)
for layer in self.layers:
X = layer(X, hg, self.cached_g)
else:
for layer in self.layers:
X = layer(X, hg)
return X
+289
View File
@@ -0,0 +1,289 @@
from collections import Counter
import torch
import torch.nn as nn
import torch.nn.functional as F
from easygraph.nn.convs.common import MLP
from easygraph.nn.convs.hypergraphs.halfnlh_conv import HalfNLHconv
from torch.nn import Linear
__all__ = ["SetGNN"]
class SetGNN(nn.Module):
r"""The SetGNN model proposed in `YOU ARE ALLSET: A MULTISET LEARNING FRAMEWORK FOR HYPERGRAPH NEURAL NETWORKS <https://openreview.net/pdf?id=hpBTIv2uy_E>`_ paper (ICLR 2022).
Parameters:
``num_features`` (``int``): : The dimension of node features.
``num_classes`` (``int``): The Number of class of the classification task.
``Classifier_hidden`` (``int``): Decoder hidden units.
``Classifier_num_layers`` (``int``): Layers of decoder.
``MLP_hidden`` (``int``): Encoder hidden units.
``MLP_num_layers`` (``int``): Layers of encoder.
``dropout`` (``float``, optional): Dropout ratio. Defaults to 0.5.
``aggregate`` (``str``): The aggregation method. Defaults to ``add``
``normalization`` (``str``): The normalization method. Defaults to ``ln``
``deepset_input_norm`` (``bool``): Defaults to True.
``heads`` (``int``): Defaults to 1
`PMA`` (``bool``): Defaults to True
`GPR`` (``bool``): Defaults to False
`LearnMask`` (``bool``): Defaults to False
`norm`` (``Tensor``): The weight for edges in bipartite graphs, correspond to data.edge_index
"""
def __init__(
self,
num_features,
num_classes,
Classifier_hidden=64,
Classifier_num_layers=2,
MLP_hidden=64,
MLP_num_layers=2,
All_num_layers=2,
dropout=0.5,
aggregate="mean",
normalization="ln",
deepset_input_norm=True,
heads=1,
PMA=True,
GPR=False,
LearnMask=False,
norm=None,
self_loop=True,
):
super(SetGNN, self).__init__()
"""
args should contain the following:
V_in_dim, V_enc_hid_dim, V_dec_hid_dim, V_out_dim, V_enc_num_layers, V_dec_num_layers
E_in_dim, E_enc_hid_dim, E_dec_hid_dim, E_out_dim, E_enc_num_layers, E_dec_num_layers
All_num_layers,dropout
!!! V_in_dim should be the dimension of node features
!!! E_out_dim should be the number of classes (for classification)
"""
# Now set all dropout the same, but can be different
self.All_num_layers = All_num_layers
self.dropout = dropout
self.aggr = aggregate
self.NormLayer = normalization
self.InputNorm = deepset_input_norm
self.GPR = GPR
self.LearnMask = LearnMask
# Now define V2EConvs[i], V2EConvs[i] for ith layers
# Currently we assume there's no hyperedge features, which means V_out_dim = E_in_dim
# If there's hyperedge features, concat with Vpart decoder output features [V_feat||E_feat]
self.V2EConvs = nn.ModuleList()
self.E2VConvs = nn.ModuleList()
self.bnV2Es = nn.ModuleList()
self.bnE2Vs = nn.ModuleList()
self.edge_index = None
self.self_loop = self_loop
if self.LearnMask:
self.Importance = nn.Parameter(torch.ones(norm.size()))
if self.All_num_layers == 0:
self.classifier = MLP(
in_channels=num_features,
hidden_channels=Classifier_hidden,
out_channels=num_classes,
num_layers=Classifier_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=False,
)
else:
self.V2EConvs.append(
HalfNLHconv(
in_dim=num_features,
hid_dim=MLP_hidden,
out_dim=MLP_hidden,
num_layers=MLP_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=self.InputNorm,
heads=heads,
attention=PMA,
)
)
self.bnV2Es.append(nn.BatchNorm1d(MLP_hidden))
self.E2VConvs.append(
HalfNLHconv(
in_dim=MLP_hidden,
hid_dim=MLP_hidden,
out_dim=MLP_hidden,
num_layers=MLP_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=self.InputNorm,
heads=heads,
attention=PMA,
)
)
self.bnE2Vs.append(nn.BatchNorm1d(MLP_hidden))
for _ in range(self.All_num_layers - 1):
self.V2EConvs.append(
HalfNLHconv(
in_dim=MLP_hidden,
hid_dim=MLP_hidden,
out_dim=MLP_hidden,
num_layers=MLP_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=self.InputNorm,
heads=heads,
attention=PMA,
)
)
self.bnV2Es.append(nn.BatchNorm1d(MLP_hidden))
self.E2VConvs.append(
HalfNLHconv(
in_dim=MLP_hidden,
hid_dim=MLP_hidden,
out_dim=MLP_hidden,
num_layers=MLP_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=self.InputNorm,
heads=heads,
attention=PMA,
)
)
self.bnE2Vs.append(nn.BatchNorm1d(MLP_hidden))
if self.GPR:
self.MLP = MLP(
in_channels=num_features,
hidden_channels=MLP_hidden,
out_channels=MLP_hidden,
num_layers=MLP_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=False,
)
self.GPRweights = Linear(self.All_num_layers + 1, 1, bias=False)
self.classifier = MLP(
in_channels=MLP_hidden,
hidden_channels=Classifier_hidden,
out_channels=num_classes,
num_layers=Classifier_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=False,
)
else:
self.classifier = MLP(
in_channels=MLP_hidden,
hidden_channels=Classifier_hidden,
out_channels=num_classes,
num_layers=Classifier_num_layers,
dropout=self.dropout,
normalization=self.NormLayer,
InputNorm=False,
)
def generate_edge_index(self, dataset, self_loop=False):
edge_list = dataset["edge_list"]
e_ind = 0
edge_index = [[], []]
for e in edge_list:
for n in e:
edge_index[0].append(n)
edge_index[1].append(e_ind)
e_ind += 1
edge_index = torch.tensor(edge_index).type(torch.LongTensor)
if self_loop:
hyperedge_appear_fre = Counter(edge_index[1].numpy())
skip_node_lst = []
for edge in hyperedge_appear_fre:
if hyperedge_appear_fre[edge] == 1:
skip_node = edge_index[0][torch.where(edge_index[1] == edge)[0]]
skip_node_lst.append(skip_node)
num_nodes = dataset["num_vertices"]
new_edge_idx = len(edge_index[1]) + 1
new_edges = torch.zeros(
(2, num_nodes - len(skip_node_lst)), dtype=edge_index.dtype
)
tmp_count = 0
for i in range(num_nodes):
if i not in skip_node_lst:
new_edges[0][tmp_count] = i
new_edges[1][tmp_count] = new_edge_idx
new_edge_idx += 1
tmp_count += 1
edge_index = torch.Tensor(edge_index).type(torch.LongTensor)
edge_index = torch.cat((edge_index, new_edges), dim=1)
_, sorted_idx = torch.sort(edge_index[0])
edge_index = torch.Tensor(edge_index[:, sorted_idx]).type(torch.LongTensor)
return edge_index
def reset_parameters(self):
for layer in self.V2EConvs:
layer.reset_parameters()
for layer in self.E2VConvs:
layer.reset_parameters()
for layer in self.bnV2Es:
layer.reset_parameters()
for layer in self.bnE2Vs:
layer.reset_parameters()
self.classifier.reset_parameters()
if self.GPR:
self.MLP.reset_parameters()
self.GPRweights.reset_parameters()
if self.LearnMask:
nn.init.ones_(self.Importance)
def forward(self, data):
"""
The data should contain the follows
data.x: node features
data.edge_index: edge list (of size (2,|E|)) where data.edge_index[0] contains nodes and data.edge_index[1] contains hyperedges
!!! Note that self loop should be assigned to a new (hyper)edge id!!!
!!! Also note that the (hyper)edge id should start at 0 (akin to node id)
data.norm: The weight for edges in bipartite graphs, correspond to data.edge_index
!!! Note that we output final node representation. Loss should be defined outside.
"""
if self.edge_index is None:
self.edge_index = self.generate_edge_index(data, self.self_loop)
# print("generate_edge_index:", self.edge_index.shape)
x, edge_index = data["features"], self.edge_index
if data["weight"] == None:
norm = torch.ones(edge_index.size()[1])
else:
norm = data["weight"]
if self.LearnMask:
norm = self.Importance * norm
reversed_edge_index = torch.stack([edge_index[1], edge_index[0]], dim=0)
if self.GPR:
xs = []
xs.append(F.relu(self.MLP(x)))
for i, _ in enumerate(self.V2EConvs):
x = F.relu(self.V2EConvs[i](x, edge_index, norm, self.aggr))
# x = self.bnV2Es[i](x)
x = F.dropout(x, p=self.dropout, training=self.training)
x = self.E2VConvs[i](x, reversed_edge_index, norm, self.aggr)
x = F.relu(x)
xs.append(x)
# x = self.bnE2Vs[i](x)
x = F.dropout(x, p=self.dropout, training=self.training)
x = torch.stack(xs, dim=-1)
x = self.GPRweights(x).squeeze()
x = self.classifier(x)
else:
x = F.dropout(x, p=0.2, training=self.training) # Input dropout
for i, _ in enumerate(self.V2EConvs):
x = F.relu(self.V2EConvs[i](x, edge_index, norm, self.aggr))
# x = self.bnV2Es[i](x)
x = F.dropout(x, p=self.dropout, training=self.training)
x = F.relu(self.E2VConvs[i](x, reversed_edge_index, norm, self.aggr))
# x = self.bnE2Vs[i](x)
x = F.dropout(x, p=self.dropout, training=self.training)
x = self.classifier(x)
return x
+214
View File
@@ -0,0 +1,214 @@
import torch
import torch.nn as nn
from easygraph.nn import MultiHeadWrapper
from easygraph.nn import UniGATConv
from easygraph.nn import UniGCNConv
from easygraph.nn import UniGINConv
from easygraph.nn import UniSAGEConv
__all__ = [
"UniGCN",
"UniGAT",
"UniSAGE",
"UniGIN",
]
class UniGCN(nn.Module):
r"""The UniGCN model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
use_bn: bool = False,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.layers = nn.ModuleList()
self.layers.append(
UniGCNConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
)
self.layers.append(
UniGCNConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
)
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
for layer in self.layers:
X = layer(X, hg)
return X
class UniGAT(nn.Module):
r"""The UniGAT model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``num_heads`` (``int``): The Number of attention head in each layer.
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
``drop_rate`` (``float``): The dropout probability. Defaults to ``0.5``.
``atten_neg_slope`` (``float``): Hyper-parameter of the ``LeakyReLU`` activation of edge attention. Defaults to 0.2.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
num_heads: int,
use_bn: bool = False,
drop_rate: float = 0.5,
atten_neg_slope: float = 0.2,
) -> None:
super().__init__()
self.drop_layer = nn.Dropout(drop_rate)
self.multi_head_layer = MultiHeadWrapper(
num_heads,
"concat",
UniGATConv,
in_channels=in_channels,
out_channels=hid_channels,
use_bn=use_bn,
drop_rate=drop_rate,
atten_neg_slope=atten_neg_slope,
)
# The original implementation has applied activation layer after the final layer.
# Thus, we donot set ``is_last`` to ``True``.
self.out_layer = UniGATConv(
hid_channels * num_heads,
num_classes,
use_bn=use_bn,
drop_rate=drop_rate,
atten_neg_slope=atten_neg_slope,
is_last=False,
)
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
X = self.drop_layer(X)
X = self.multi_head_layer(X=X, hg=hg)
X = self.drop_layer(X)
X = self.out_layer(X, hg)
return X
class UniSAGE(nn.Module):
r"""The UniSAGE model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
use_bn: bool = False,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.layers = nn.ModuleList()
self.layers.append(
UniSAGEConv(in_channels, hid_channels, use_bn=use_bn, drop_rate=drop_rate)
)
self.layers.append(
UniSAGEConv(hid_channels, num_classes, use_bn=use_bn, is_last=True)
)
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
for layer in self.layers:
X = layer(X, hg)
return X
class UniGIN(nn.Module):
r"""The UniGIN model proposed in `UniGNN: a Unified Framework for Graph and Hypergraph Neural Networks <https://arxiv.org/pdf/2105.00956.pdf>`_ paper (IJCAI 2021).
Parameters:
``in_channels`` (``int``): :math:`C_{in}` is the number of input channels.
``hid_channels`` (``int``): :math:`C_{hid}` is the number of hidden channels.
``num_classes`` (``int``): The Number of class of the classification task.
``eps`` (``float``): The epsilon value. Defaults to ``0.0``.
``train_eps`` (``bool``): If set to ``True``, the epsilon value will be trainable. Defaults to ``False``.
``use_bn`` (``bool``): If set to ``True``, use batch normalization. Defaults to ``False``.
``drop_rate`` (``float``, optional): Dropout ratio. Defaults to ``0.5``.
"""
def __init__(
self,
in_channels: int,
hid_channels: int,
num_classes: int,
eps: float = 0.0,
train_eps: bool = False,
use_bn: bool = False,
drop_rate: float = 0.5,
) -> None:
super().__init__()
self.layers = nn.ModuleList()
self.layers.append(
UniGINConv(
in_channels,
hid_channels,
eps=eps,
train_eps=train_eps,
use_bn=use_bn,
drop_rate=drop_rate,
)
)
self.layers.append(
UniGINConv(
hid_channels,
num_classes,
eps=eps,
train_eps=train_eps,
use_bn=use_bn,
is_last=True,
)
)
def forward(self, X: torch.Tensor, hg: "eg.Hypergraph") -> torch.Tensor:
r"""The forward function.
Parameters:
``X`` (``torch.Tensor``): Input vertex feature matrix. Size :math:`(N, C_{in})`.
``hg`` (``eg.Hypergraph``): The hypergraph structure that contains :math:`N` vertices.
"""
for layer in self.layers:
X = layer(X, hg)
return X