From b1f7f38f6bd4180a522e582ecc32232b0e206574 Mon Sep 17 00:00:00 2001 From: Giovanni Semioli Date: Fri, 3 Oct 2025 14:46:34 +0200 Subject: [PATCH 1/6] add models --- hyperlink_prediction/__init__.py | 13 ---------- .../hyperlink_prediction_base.py | 24 ------------------- hyperlink_prediction/models/__init__.py | 9 +++++++ .../hyperlink_prediction_algorithm.py | 6 ++--- .../models/hyperlink_prediction_base.py | 18 ++++++++++++++ .../hyperlink_prediction_result.py | 0 6 files changed, 30 insertions(+), 40 deletions(-) delete mode 100644 hyperlink_prediction/__init__.py delete mode 100644 hyperlink_prediction/hyperlink_prediction_base.py create mode 100644 hyperlink_prediction/models/__init__.py rename hyperlink_prediction/{ => models}/hyperlink_prediction_algorithm.py (82%) create mode 100644 hyperlink_prediction/models/hyperlink_prediction_base.py rename hyperlink_prediction/{ => models}/hyperlink_prediction_result.py (100%) diff --git a/hyperlink_prediction/__init__.py b/hyperlink_prediction/__init__.py deleted file mode 100644 index 05f24e4..0000000 --- a/hyperlink_prediction/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -import hyperlink_prediction.datasets -from .hyperlink_prediction_base import HypergraphSampler -from .hyperlink_prediction_algorithm import CommonNeighbors -from .hyperlink_prediction_result import HyperlinkPredictionResult - -__all__ = [ - 'hyperlink_prediction.datasets', -] -data_classes = [ - 'HypergraphSampler', - 'CommonNeighbors', - 'HyperlinkPredictionResult' -] \ No newline at end of file diff --git a/hyperlink_prediction/hyperlink_prediction_base.py b/hyperlink_prediction/hyperlink_prediction_base.py deleted file mode 100644 index d2f7d14..0000000 --- a/hyperlink_prediction/hyperlink_prediction_base.py +++ /dev/null @@ -1,24 +0,0 @@ -import torch -import numpy as np -from abc import abstractmethod - -class HypergraphSampler(): - - def __init__(self, num_node: int, device: torch.device = torch.device('cpu')): - self.num_node = num_node - self.device = device - - @abstractmethod - def fit(self, *args, **kwargs): - pass - - @abstractmethod - def generate(self, edge_index: torch.Tensor): - pass - - @abstractmethod - def transform(self, edge_index: np.ndarray): - pass - - def trasform(self, edge_index: torch.Tensor): - return self.generate(edge_index) diff --git a/hyperlink_prediction/models/__init__.py b/hyperlink_prediction/models/__init__.py new file mode 100644 index 0000000..33a2ab4 --- /dev/null +++ b/hyperlink_prediction/models/__init__.py @@ -0,0 +1,9 @@ +from .hyperlink_prediction_base import HyperlinkPredictor +from .hyperlink_prediction_algorithm import CommonNeighbors +from .hyperlink_prediction_result import HyperlinkPredictionResult + +__all__ = data_classes = [ + 'HyperlinkPredictor', + 'CommonNeighbors', + 'HyperlinkPredictionResult' +] \ No newline at end of file diff --git a/hyperlink_prediction/hyperlink_prediction_algorithm.py b/hyperlink_prediction/models/hyperlink_prediction_algorithm.py similarity index 82% rename from hyperlink_prediction/hyperlink_prediction_algorithm.py rename to hyperlink_prediction/models/hyperlink_prediction_algorithm.py index c87aab0..7fc2f80 100644 --- a/hyperlink_prediction/hyperlink_prediction_algorithm.py +++ b/hyperlink_prediction/models/hyperlink_prediction_algorithm.py @@ -1,14 +1,14 @@ import torch from torch import Tensor -from .hyperlink_prediction_base import HypergraphSampler +from .hyperlink_prediction_base import HyperlinkPredictor from .hyperlink_prediction_result import HyperlinkPredictionResult -class CommonNeighbors(HypergraphSampler): +class CommonNeighbors(HyperlinkPredictor): def score_CN(self, H, u, v): return torch.dot(H[u], H[v]).item() - def generate(self, edge_index: Tensor): + def predict(self, edge_index: Tensor): sparse = torch.sparse_coo_tensor( edge_index, torch.ones(edge_index.shape[1], device=self.device), diff --git a/hyperlink_prediction/models/hyperlink_prediction_base.py b/hyperlink_prediction/models/hyperlink_prediction_base.py new file mode 100644 index 0000000..b5566d0 --- /dev/null +++ b/hyperlink_prediction/models/hyperlink_prediction_base.py @@ -0,0 +1,18 @@ +import torch +import numpy as np +from abc import abstractmethod +from hyperlink_prediction_result import HyperlinkPredictionResult +class HyperlinkPredictor(): + + def __init__(self, num_node: int, device: torch.device = torch.device('cpu')): + self.num_node = num_node + self.device = device + + @abstractmethod + def fit(self, X, y, *args, **kwargs): + pass + + @abstractmethod + def predict(self, X, edge_index: torch.Tensor) -> HyperlinkPredictionResult: + pass + diff --git a/hyperlink_prediction/hyperlink_prediction_result.py b/hyperlink_prediction/models/hyperlink_prediction_result.py similarity index 100% rename from hyperlink_prediction/hyperlink_prediction_result.py rename to hyperlink_prediction/models/hyperlink_prediction_result.py From 887caadb0549f48eb9b0e700862c4d4bd2a92efb Mon Sep 17 00:00:00 2001 From: Giovanni Semioli Date: Wed, 8 Oct 2025 19:16:25 +0200 Subject: [PATCH 2/6] Unify subpackages --- hyperbench/__init__.py | 11 +++++++++++ hyperbench/hyperlink_prediction/__init__.py | 10 ++++++++++ .../hyperlink_prediction}/datasets/__init__.py | 0 .../hyperlink_prediction}/datasets/arb_dataset.py | 0 .../datasets/dataset_hypergraph.py | 0 .../hyperlink_prediction}/datasets/imdb_dataset.py | 0 .../hyperlink_prediction}/loader/__init__.py | 0 .../hyperlink_prediction}/loader/dataloader.py | 0 .../hyperlink_prediction}/models/__init__.py | 0 .../models/hyperlink_prediction_algorithm.py | 0 .../models/hyperlink_prediction_base.py | 0 .../models/hyperlink_prediction_result.py | 0 .../negative_sampling}/__init__.py | 0 .../hypergraph_negative_sampling.py | 0 .../hypergraph_negative_sampling_algorithm.py | 0 .../hypergraph_negative_sampling_result.py | 0 {pipelines => hyperbench/pipelines}/__init__.py | 0 {pipelines => hyperbench/pipelines}/pipeline.py | 4 ++-- {utils => hyperbench/utils}/__init__.py | 0 .../utils}/hyperlink_train_test_split.py | 0 .../utils}/set_negative_samplig_method.py | 0 21 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 hyperbench/__init__.py create mode 100644 hyperbench/hyperlink_prediction/__init__.py rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/datasets/__init__.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/datasets/arb_dataset.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/datasets/dataset_hypergraph.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/datasets/imdb_dataset.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/loader/__init__.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/loader/dataloader.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/models/__init__.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/models/hyperlink_prediction_algorithm.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/models/hyperlink_prediction_base.py (100%) rename {hyperlink_prediction => hyperbench/hyperlink_prediction}/models/hyperlink_prediction_result.py (100%) rename {negative_sampling => hyperbench/negative_sampling}/__init__.py (100%) rename {negative_sampling => hyperbench/negative_sampling}/hypergraph_negative_sampling.py (100%) rename {negative_sampling => hyperbench/negative_sampling}/hypergraph_negative_sampling_algorithm.py (100%) rename {negative_sampling => hyperbench/negative_sampling}/hypergraph_negative_sampling_result.py (100%) rename {pipelines => hyperbench/pipelines}/__init__.py (100%) rename {pipelines => hyperbench/pipelines}/pipeline.py (98%) rename {utils => hyperbench/utils}/__init__.py (100%) rename {utils => hyperbench/utils}/hyperlink_train_test_split.py (100%) rename {utils => hyperbench/utils}/set_negative_samplig_method.py (100%) diff --git a/hyperbench/__init__.py b/hyperbench/__init__.py new file mode 100644 index 0000000..66d65e8 --- /dev/null +++ b/hyperbench/__init__.py @@ -0,0 +1,11 @@ +import hyperbench.hyperlink_prediction +import hyperbench.negative_sampling +import hyperbench.pipelines +import hyperbench.utils + +__all__ = [ + "hyperlink_prediction", + "negative_sampling", + "pipelines", + "utils" +] \ No newline at end of file diff --git a/hyperbench/hyperlink_prediction/__init__.py b/hyperbench/hyperlink_prediction/__init__.py new file mode 100644 index 0000000..3c88578 --- /dev/null +++ b/hyperbench/hyperlink_prediction/__init__.py @@ -0,0 +1,10 @@ +import hyperlink_prediction +import hyperlink_prediction.datasets +import hyperlink_prediction.loader +import hyperlink_prediction.models + +__all__ = [ + "datasets", + "loader", + "models" +] \ No newline at end of file diff --git a/hyperlink_prediction/datasets/__init__.py b/hyperbench/hyperlink_prediction/datasets/__init__.py similarity index 100% rename from hyperlink_prediction/datasets/__init__.py rename to hyperbench/hyperlink_prediction/datasets/__init__.py diff --git a/hyperlink_prediction/datasets/arb_dataset.py b/hyperbench/hyperlink_prediction/datasets/arb_dataset.py similarity index 100% rename from hyperlink_prediction/datasets/arb_dataset.py rename to hyperbench/hyperlink_prediction/datasets/arb_dataset.py diff --git a/hyperlink_prediction/datasets/dataset_hypergraph.py b/hyperbench/hyperlink_prediction/datasets/dataset_hypergraph.py similarity index 100% rename from hyperlink_prediction/datasets/dataset_hypergraph.py rename to hyperbench/hyperlink_prediction/datasets/dataset_hypergraph.py diff --git a/hyperlink_prediction/datasets/imdb_dataset.py b/hyperbench/hyperlink_prediction/datasets/imdb_dataset.py similarity index 100% rename from hyperlink_prediction/datasets/imdb_dataset.py rename to hyperbench/hyperlink_prediction/datasets/imdb_dataset.py diff --git a/hyperlink_prediction/loader/__init__.py b/hyperbench/hyperlink_prediction/loader/__init__.py similarity index 100% rename from hyperlink_prediction/loader/__init__.py rename to hyperbench/hyperlink_prediction/loader/__init__.py diff --git a/hyperlink_prediction/loader/dataloader.py b/hyperbench/hyperlink_prediction/loader/dataloader.py similarity index 100% rename from hyperlink_prediction/loader/dataloader.py rename to hyperbench/hyperlink_prediction/loader/dataloader.py diff --git a/hyperlink_prediction/models/__init__.py b/hyperbench/hyperlink_prediction/models/__init__.py similarity index 100% rename from hyperlink_prediction/models/__init__.py rename to hyperbench/hyperlink_prediction/models/__init__.py diff --git a/hyperlink_prediction/models/hyperlink_prediction_algorithm.py b/hyperbench/hyperlink_prediction/models/hyperlink_prediction_algorithm.py similarity index 100% rename from hyperlink_prediction/models/hyperlink_prediction_algorithm.py rename to hyperbench/hyperlink_prediction/models/hyperlink_prediction_algorithm.py diff --git a/hyperlink_prediction/models/hyperlink_prediction_base.py b/hyperbench/hyperlink_prediction/models/hyperlink_prediction_base.py similarity index 100% rename from hyperlink_prediction/models/hyperlink_prediction_base.py rename to hyperbench/hyperlink_prediction/models/hyperlink_prediction_base.py diff --git a/hyperlink_prediction/models/hyperlink_prediction_result.py b/hyperbench/hyperlink_prediction/models/hyperlink_prediction_result.py similarity index 100% rename from hyperlink_prediction/models/hyperlink_prediction_result.py rename to hyperbench/hyperlink_prediction/models/hyperlink_prediction_result.py diff --git a/negative_sampling/__init__.py b/hyperbench/negative_sampling/__init__.py similarity index 100% rename from negative_sampling/__init__.py rename to hyperbench/negative_sampling/__init__.py diff --git a/negative_sampling/hypergraph_negative_sampling.py b/hyperbench/negative_sampling/hypergraph_negative_sampling.py similarity index 100% rename from negative_sampling/hypergraph_negative_sampling.py rename to hyperbench/negative_sampling/hypergraph_negative_sampling.py diff --git a/negative_sampling/hypergraph_negative_sampling_algorithm.py b/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py similarity index 100% rename from negative_sampling/hypergraph_negative_sampling_algorithm.py rename to hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py diff --git a/negative_sampling/hypergraph_negative_sampling_result.py b/hyperbench/negative_sampling/hypergraph_negative_sampling_result.py similarity index 100% rename from negative_sampling/hypergraph_negative_sampling_result.py rename to hyperbench/negative_sampling/hypergraph_negative_sampling_result.py diff --git a/pipelines/__init__.py b/hyperbench/pipelines/__init__.py similarity index 100% rename from pipelines/__init__.py rename to hyperbench/pipelines/__init__.py diff --git a/pipelines/pipeline.py b/hyperbench/pipelines/pipeline.py similarity index 98% rename from pipelines/pipeline.py rename to hyperbench/pipelines/pipeline.py index 047d152..96f5991 100644 --- a/pipelines/pipeline.py +++ b/hyperbench/pipelines/pipeline.py @@ -22,7 +22,7 @@ def execute(): import time from random import randint, seed from hyperlink_prediction.loader.dataloader import DatasetLoader - from hyperlink_prediction.hyperlink_prediction_algorithm import CommonNeighbors + from hyperlink_prediction.models.hyperlink_prediction_algorithm import CommonNeighbors from hyperlink_prediction.datasets.imdb_dataset import CHLPBaseDataset, IMDBHypergraphDataset, ARXIVHypergraphDataset, COURSERAHypergraphDataset from utils.set_negative_samplig_method import setNegativeSamplingAlgorithm from utils.hyperlink_train_test_split import train_test_split @@ -170,7 +170,7 @@ def forward(self, x, x_e, edge_index): negative_test = negative_sampler.generate(h.edge_index) hlp_method = CommonNeighbors(h.num_nodes) - hlp_result = hlp_method.generate(negative_test.edge_index) + hlp_result = hlp_method.predict(negative_test.edge_index) y_pos = torch.ones(hlp_result.edge_index.size(1), 1) y_neg = torch.zeros(negative_test.edge_index.size(1), 1) diff --git a/utils/__init__.py b/hyperbench/utils/__init__.py similarity index 100% rename from utils/__init__.py rename to hyperbench/utils/__init__.py diff --git a/utils/hyperlink_train_test_split.py b/hyperbench/utils/hyperlink_train_test_split.py similarity index 100% rename from utils/hyperlink_train_test_split.py rename to hyperbench/utils/hyperlink_train_test_split.py diff --git a/utils/set_negative_samplig_method.py b/hyperbench/utils/set_negative_samplig_method.py similarity index 100% rename from utils/set_negative_samplig_method.py rename to hyperbench/utils/set_negative_samplig_method.py From 4d84041d99e10f960c08500be00e536c5ac2167c Mon Sep 17 00:00:00 2001 From: Giovanni Semioli Date: Thu, 9 Oct 2025 20:09:03 +0200 Subject: [PATCH 3/6] fix import --- hyperbench/__init__.py | 8 ++++---- hyperbench/hyperlink_prediction/__init__.py | 5 +---- .../hyperlink_prediction/datasets/arb_dataset.py | 2 +- .../hyperlink_prediction/datasets/imdb_dataset.py | 2 +- hyperbench/hyperlink_prediction/loader/dataloader.py | 6 +++--- hyperbench/negative_sampling/__init__.py | 1 - .../hypergraph_negative_sampling_algorithm.py | 4 ++-- .../hypergraph_negative_sampling_result.py | 2 +- hyperbench/pipelines/__init__.py | 3 +-- hyperbench/pipelines/pipeline.py | 10 +++++----- hyperbench/utils/__init__.py | 5 ++--- hyperbench/utils/set_negative_samplig_method.py | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 14 files changed, 24 insertions(+), 30 deletions(-) diff --git a/hyperbench/__init__.py b/hyperbench/__init__.py index 66d65e8..cd87b24 100644 --- a/hyperbench/__init__.py +++ b/hyperbench/__init__.py @@ -1,7 +1,7 @@ -import hyperbench.hyperlink_prediction -import hyperbench.negative_sampling -import hyperbench.pipelines -import hyperbench.utils +from . import hyperlink_prediction +from . import negative_sampling +from . import pipelines +from . import utils __all__ = [ "hyperlink_prediction", diff --git a/hyperbench/hyperlink_prediction/__init__.py b/hyperbench/hyperlink_prediction/__init__.py index 3c88578..bf0ab41 100644 --- a/hyperbench/hyperlink_prediction/__init__.py +++ b/hyperbench/hyperlink_prediction/__init__.py @@ -1,7 +1,4 @@ -import hyperlink_prediction -import hyperlink_prediction.datasets -import hyperlink_prediction.loader -import hyperlink_prediction.models +from . import datasets, loader, models __all__ = [ "datasets", diff --git a/hyperbench/hyperlink_prediction/datasets/arb_dataset.py b/hyperbench/hyperlink_prediction/datasets/arb_dataset.py index 28a0684..749f1b7 100644 --- a/hyperbench/hyperlink_prediction/datasets/arb_dataset.py +++ b/hyperbench/hyperlink_prediction/datasets/arb_dataset.py @@ -2,7 +2,7 @@ import tarfile import torch import torch.nn.functional as F -from hyperlink_prediction.datasets.dataset_hypergraph import DatasetHyperGraph +from .dataset_hypergraph import DatasetHyperGraph from torch_geometric.data.hypergraph_data import HyperGraphData from os import remove, listdir diff --git a/hyperbench/hyperlink_prediction/datasets/imdb_dataset.py b/hyperbench/hyperlink_prediction/datasets/imdb_dataset.py index 74e9819..c532518 100644 --- a/hyperbench/hyperlink_prediction/datasets/imdb_dataset.py +++ b/hyperbench/hyperlink_prediction/datasets/imdb_dataset.py @@ -1,7 +1,7 @@ import torch import pickle from abc import ABC -from hyperlink_prediction.datasets.dataset_hypergraph import DatasetHyperGraph +from .dataset_hypergraph import DatasetHyperGraph from torch_geometric.data.hypergraph_data import HyperGraphData diff --git a/hyperbench/hyperlink_prediction/loader/dataloader.py b/hyperbench/hyperlink_prediction/loader/dataloader.py index 6da46e2..1b2aca0 100644 --- a/hyperbench/hyperlink_prediction/loader/dataloader.py +++ b/hyperbench/hyperlink_prediction/loader/dataloader.py @@ -1,10 +1,10 @@ import torch from typing import List, Any from torch.utils.data import DataLoader -from utils.set_negative_samplig_method import setNegativeSamplingAlgorithm -from negative_sampling.hypergraph_negative_sampling_algorithm import HypergraphNegativeSampler, MotifHypergraphNegativeSampler +from ...utils.set_negative_samplig_method import setNegativeSamplingAlgorithm +from ...negative_sampling.hypergraph_negative_sampling_algorithm import HypergraphNegativeSampler, MotifHypergraphNegativeSampler from torch_geometric.data.hypergraph_data import HyperGraphData -from hyperlink_prediction.datasets.dataset_hypergraph import DatasetHyperGraph +from ..datasets.dataset_hypergraph import DatasetHyperGraph class DatasetLoader(DataLoader): diff --git a/hyperbench/negative_sampling/__init__.py b/hyperbench/negative_sampling/__init__.py index e851cd2..2b2f408 100644 --- a/hyperbench/negative_sampling/__init__.py +++ b/hyperbench/negative_sampling/__init__.py @@ -1,4 +1,3 @@ -import negative_sampling from .hypergraph_negative_sampling import HypergraphNegativeSampler from .hypergraph_negative_sampling_result import HypergraphNegativeSamplerResult, ABSizedHypergraphNegativeSamplerResult from .hypergraph_negative_sampling_algorithm import ABSizedHypergraphNegativeSampler,SizedHypergraphNegativeSampler, MotifHypergraphNegativeSampler, CliqueHypergraphNegativeSampler diff --git a/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py b/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py index b54116b..4a88919 100644 --- a/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py +++ b/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py @@ -2,8 +2,8 @@ import torch_geometric.nn.aggr as aggr from enum import Enum from torch import Tensor -from negative_sampling.hypergraph_negative_sampling import HypergraphNegativeSampler -from negative_sampling.hypergraph_negative_sampling_result import HypergraphNegativeSamplerResult, ABSizedHypergraphNegativeSamplerResult +from .hypergraph_negative_sampling import HypergraphNegativeSampler +from .hypergraph_negative_sampling_result import HypergraphNegativeSamplerResult, ABSizedHypergraphNegativeSamplerResult import warnings warnings.filterwarnings('ignore', category=UserWarning) diff --git a/hyperbench/negative_sampling/hypergraph_negative_sampling_result.py b/hyperbench/negative_sampling/hypergraph_negative_sampling_result.py index de7f452..e03e1d5 100644 --- a/hyperbench/negative_sampling/hypergraph_negative_sampling_result.py +++ b/hyperbench/negative_sampling/hypergraph_negative_sampling_result.py @@ -2,7 +2,7 @@ import torch_geometric.nn.aggr as aggr from abc import ABC from torch import Tensor -from negative_sampling.hypergraph_negative_sampling import HypergraphNegativeSampler +from .hypergraph_negative_sampling import HypergraphNegativeSampler class HypergraphNegativeSamplerResult(ABC): """ A class Result which rapresents the hypergraph generated by diff --git a/hyperbench/pipelines/__init__.py b/hyperbench/pipelines/__init__.py index f7981c3..f458b4d 100644 --- a/hyperbench/pipelines/__init__.py +++ b/hyperbench/pipelines/__init__.py @@ -1,5 +1,4 @@ -import pipelines -from pipelines import pipeline +from . import pipeline __all__ = [ pipeline diff --git a/hyperbench/pipelines/pipeline.py b/hyperbench/pipelines/pipeline.py index 96f5991..86c14a4 100644 --- a/hyperbench/pipelines/pipeline.py +++ b/hyperbench/pipelines/pipeline.py @@ -21,11 +21,11 @@ def execute(): import matplotlib.pyplot as plt import time from random import randint, seed - from hyperlink_prediction.loader.dataloader import DatasetLoader - from hyperlink_prediction.models.hyperlink_prediction_algorithm import CommonNeighbors - from hyperlink_prediction.datasets.imdb_dataset import CHLPBaseDataset, IMDBHypergraphDataset, ARXIVHypergraphDataset, COURSERAHypergraphDataset - from utils.set_negative_samplig_method import setNegativeSamplingAlgorithm - from utils.hyperlink_train_test_split import train_test_split + from ..hyperlink_prediction.loader.dataloader import DatasetLoader + from ..hyperlink_prediction.models.hyperlink_prediction_algorithm import CommonNeighbors + from ..hyperlink_prediction.datasets.imdb_dataset import CHLPBaseDataset, IMDBHypergraphDataset, ARXIVHypergraphDataset, COURSERAHypergraphDataset + from ..utils.set_negative_samplig_method import setNegativeSamplingAlgorithm + from ..utils.hyperlink_train_test_split import train_test_split from torch_geometric.nn import HypergraphConv from tqdm.auto import trange, tqdm from torch_geometric.data.hypergraph_data import HyperGraphData diff --git a/hyperbench/utils/__init__.py b/hyperbench/utils/__init__.py index 09fd158..4c4c060 100644 --- a/hyperbench/utils/__init__.py +++ b/hyperbench/utils/__init__.py @@ -1,6 +1,5 @@ -import utils -from utils.hyperlink_train_test_split import train_test_split -from utils.set_negative_samplig_method import setNegativeSamplingAlgorithm +from .hyperlink_train_test_split import train_test_split +from .set_negative_samplig_method import setNegativeSamplingAlgorithm __all__ = [ 'train_test_split', 'setNegativeSamplingAlgorithm' diff --git a/hyperbench/utils/set_negative_samplig_method.py b/hyperbench/utils/set_negative_samplig_method.py index d986722..4d61ef8 100644 --- a/hyperbench/utils/set_negative_samplig_method.py +++ b/hyperbench/utils/set_negative_samplig_method.py @@ -1,4 +1,4 @@ -from negative_sampling.hypergraph_negative_sampling_algorithm import SizedHypergraphNegativeSampler, MotifHypergraphNegativeSampler, CliqueHypergraphNegativeSampler, HypergraphNegativeSampler +from ..negative_sampling.hypergraph_negative_sampling_algorithm import SizedHypergraphNegativeSampler, MotifHypergraphNegativeSampler, CliqueHypergraphNegativeSampler, HypergraphNegativeSampler def setNegativeSamplingAlgorithm(ns_algorithm: str, num_node: int): ns_method : HypergraphNegativeSampler diff --git a/pyproject.toml b/pyproject.toml index c330c6f..b487f5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ dependencies = [ requires-python = ">=3.9" [project.scripts] -pipeline = "pipelines.pipeline:execute" +pipeline = "hyperbench.pipelines.pipeline:execute" [project.urls] Homepage = "https://www.isislab.it/" diff --git a/setup.py b/setup.py index d704634..917ad1c 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ include_package_data=True, entry_points={ 'console_scripts': [ - 'pipeline=pipelines.pipeline:execute', + 'pipeline=hyperbench.pipelines.pipeline:execute', ], }, install_requires=[], From 74c1eecd70ec35d46de3a987d4ef9867b1db472a Mon Sep 17 00:00:00 2001 From: Giovanni Semioli Date: Thu, 9 Oct 2025 20:09:24 +0200 Subject: [PATCH 4/6] implemented fit method --- .../models/hyperlink_prediction_algorithm.py | 28 +++++++++++++++---- .../models/hyperlink_prediction_base.py | 4 +-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/hyperbench/hyperlink_prediction/models/hyperlink_prediction_algorithm.py b/hyperbench/hyperlink_prediction/models/hyperlink_prediction_algorithm.py index 7fc2f80..e5b9e1c 100644 --- a/hyperbench/hyperlink_prediction/models/hyperlink_prediction_algorithm.py +++ b/hyperbench/hyperlink_prediction/models/hyperlink_prediction_algorithm.py @@ -4,18 +4,36 @@ from .hyperlink_prediction_result import HyperlinkPredictionResult class CommonNeighbors(HyperlinkPredictor): + def __init__(self, device='cpu'): + super().__init__(device) + self.H = None + self.num_node = None + self.num_hyperlink = None + + def fit(self, X, y, edge_index, *args, **kwargs): + + self.num_node = int(edge_index[0].max().item()) + 1 + self.num_hyperlink = int(edge_index[1].max().item()) + 1 - def score_CN(self, H, u, v): - return torch.dot(H[u], H[v]).item() - - def predict(self, edge_index: Tensor): sparse = torch.sparse_coo_tensor( edge_index, torch.ones(edge_index.shape[1], device=self.device), (self.num_node, edge_index.max().item() + 1), device=self.device ) - H = sparse.to_dense() + + self.H = sparse.to_dense() + return self + + def score_CN(self, H, u, v): + return torch.dot(H[u], H[v]).item() + + def predict(self, edge_index: Tensor): + if self.H is None: + if edge_index is None: + raise ValueError("Model not fitted. Call fit() first.") + self.fit(None, None, edge_index) + H = self.H CN_matrix = torch.matmul(H, H.T) diff --git a/hyperbench/hyperlink_prediction/models/hyperlink_prediction_base.py b/hyperbench/hyperlink_prediction/models/hyperlink_prediction_base.py index b5566d0..30daf2a 100644 --- a/hyperbench/hyperlink_prediction/models/hyperlink_prediction_base.py +++ b/hyperbench/hyperlink_prediction/models/hyperlink_prediction_base.py @@ -1,7 +1,7 @@ import torch import numpy as np from abc import abstractmethod -from hyperlink_prediction_result import HyperlinkPredictionResult +from .hyperlink_prediction_result import HyperlinkPredictionResult class HyperlinkPredictor(): def __init__(self, num_node: int, device: torch.device = torch.device('cpu')): @@ -9,7 +9,7 @@ def __init__(self, num_node: int, device: torch.device = torch.device('cpu')): self.device = device @abstractmethod - def fit(self, X, y, *args, **kwargs): + def fit(self, X, y, edge_index, *args, **kwargs): pass @abstractmethod From 36121b512b7e52c881725ae269b8f49113b87198 Mon Sep 17 00:00:00 2001 From: Giovanni Semioli Date: Fri, 10 Oct 2025 17:07:13 +0200 Subject: [PATCH 5/6] include ARB dataset in pipeline --- .../hyperlink_prediction/datasets/__init__.py | 3 +- .../datasets/arb_dataset.py | 2 +- .../hyperlink_prediction/loader/dataloader.py | 2 +- .../hypergraph_negative_sampling_algorithm.py | 2 +- hyperbench/pipelines/pipeline.py | 14 ++------- hyperbench/utils/__init__.py | 5 +-- .../utils/data_and_sampling_selector.py | 31 +++++++++++++++++++ .../utils/set_negative_samplig_method.py | 13 -------- 8 files changed, 42 insertions(+), 30 deletions(-) create mode 100644 hyperbench/utils/data_and_sampling_selector.py delete mode 100644 hyperbench/utils/set_negative_samplig_method.py diff --git a/hyperbench/hyperlink_prediction/datasets/__init__.py b/hyperbench/hyperlink_prediction/datasets/__init__.py index 30e7147..3ed86d5 100644 --- a/hyperbench/hyperlink_prediction/datasets/__init__.py +++ b/hyperbench/hyperlink_prediction/datasets/__init__.py @@ -1,10 +1,11 @@ from .arb_dataset import ARBDataset from .dataset_hypergraph import DatasetHyperGraph -from .imdb_dataset import IMDBHypergraphDataset, ARXIVHypergraphDataset, COURSERAHypergraphDataset +from .imdb_dataset import IMDBHypergraphDataset, ARXIVHypergraphDataset, COURSERAHypergraphDataset, CHLPBaseDataset __all__ = data_classes = [ 'DatasetHyperGraph', 'ARBDataset', + 'CHLPBaseDataset', 'IMDBHypergraphDataset', 'COURSERAHypergraphDataset', 'ARXIVHypergraphDataset' diff --git a/hyperbench/hyperlink_prediction/datasets/arb_dataset.py b/hyperbench/hyperlink_prediction/datasets/arb_dataset.py index 749f1b7..8621456 100644 --- a/hyperbench/hyperlink_prediction/datasets/arb_dataset.py +++ b/hyperbench/hyperlink_prediction/datasets/arb_dataset.py @@ -22,7 +22,7 @@ class ARBDataset(DatasetHyperGraph): "tags-math-sx": "1eDevpF6EZs19rLouNpiKGLIlFOLUfKKG", "contact-high-school": "1VA2P62awVYgluOIh1W4NZQQgkQCBk-Eu", "contact-primary-school": "1sBHSEIyvVKavAho524Ro4cKL66W6rn-t", - "NDC-substances": "1dLJt3qzAOYieay03Sp9h8ZfVMiU-nMqC" + "NDC-substances": "1mGOg0DMh46J2zQdimSXMde1pKNtfAdh8" } diff --git a/hyperbench/hyperlink_prediction/loader/dataloader.py b/hyperbench/hyperlink_prediction/loader/dataloader.py index 1b2aca0..9962436 100644 --- a/hyperbench/hyperlink_prediction/loader/dataloader.py +++ b/hyperbench/hyperlink_prediction/loader/dataloader.py @@ -1,7 +1,7 @@ import torch from typing import List, Any from torch.utils.data import DataLoader -from ...utils.set_negative_samplig_method import setNegativeSamplingAlgorithm +from ...utils.data_and_sampling_selector import setNegativeSamplingAlgorithm from ...negative_sampling.hypergraph_negative_sampling_algorithm import HypergraphNegativeSampler, MotifHypergraphNegativeSampler from torch_geometric.data.hypergraph_data import HyperGraphData from ..datasets.dataset_hypergraph import DatasetHyperGraph diff --git a/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py b/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py index 4a88919..eec8edd 100644 --- a/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py +++ b/hyperbench/negative_sampling/hypergraph_negative_sampling_algorithm.py @@ -119,7 +119,7 @@ def generate(self, edge_index: Tensor) -> ABSizedHypergraphNegativeSamplerResult local_edge_index[1] += num_hyperedges num_hyperedges = torch.max(local_edge_index[1]) + 1 negative_edge_index = torch.cat([negative_edge_index , local_edge_index], dim = 1) - global_positives = torch.cat([global_positives, probabilities], dim = 0) + global_positives = torch.empty((0, probabilities.shape[1]), dtype=torch.float32, device=self.device) global_replace_mask = torch.cat([global_replace_mask, replace_mask], dim = 0) global_replacement = torch.cat([global_replacement, replacement], dim = 0) diff --git a/hyperbench/pipelines/pipeline.py b/hyperbench/pipelines/pipeline.py index 86c14a4..f44fe64 100644 --- a/hyperbench/pipelines/pipeline.py +++ b/hyperbench/pipelines/pipeline.py @@ -2,7 +2,7 @@ def execute(): parser = argparse.ArgumentParser(description="Insert dataset_name, insert negative_sampling method") - parser.add_argument('--dataset_name', type=str, help="The dataset's name, possible dataset's name: \nIMDB,\nCURSERA,\nARXIV", required=True) + parser.add_argument('--dataset_name', type=str, help="The dataset's name, possible dataset's name: \nIMDB,\nCOURSERA,\nARXIV", required=True) parser.add_argument('--negative_sampling', type=str, help="negative sampling method to use, possible methods: \n SizedHypergraphNegativeSampler,\nMotifHypergraphNegativeSampler,\nCliqueHypergraphNegativeSampler", required=True) parser.add_argument('--hlp_method', type=str, help="hyperlink prediction method to use, possible method: \nCommonNeighbors", required=True) parser.add_argument('--output_path', type=str, help="Path to save the results", default="./results") @@ -23,8 +23,7 @@ def execute(): from random import randint, seed from ..hyperlink_prediction.loader.dataloader import DatasetLoader from ..hyperlink_prediction.models.hyperlink_prediction_algorithm import CommonNeighbors - from ..hyperlink_prediction.datasets.imdb_dataset import CHLPBaseDataset, IMDBHypergraphDataset, ARXIVHypergraphDataset, COURSERAHypergraphDataset - from ..utils.set_negative_samplig_method import setNegativeSamplingAlgorithm + from ..utils.data_and_sampling_selector import setNegativeSamplingAlgorithm, select_dataset from ..utils.hyperlink_train_test_split import train_test_split from torch_geometric.nn import HypergraphConv from tqdm.auto import trange, tqdm @@ -57,14 +56,7 @@ def pre_transform(data: HyperGraphData): return data - dataset : CHLPBaseDataset - match(dataset_name): - case 'IMDB': - dataset = IMDBHypergraphDataset("./data", pre_transform= pre_transform) - case 'ARXIV': - dataset = ARXIVHypergraphDataset("./data", pre_transform = pre_transform) - case 'COURSERA': - dataset = COURSERAHypergraphDataset("./data", pre_transform = pre_transform) + dataset = select_dataset(dataset_name, pre_transform= pre_transform) test_size = 0.2 val_size = 0.0 diff --git a/hyperbench/utils/__init__.py b/hyperbench/utils/__init__.py index 4c4c060..19cfe99 100644 --- a/hyperbench/utils/__init__.py +++ b/hyperbench/utils/__init__.py @@ -1,6 +1,7 @@ from .hyperlink_train_test_split import train_test_split -from .set_negative_samplig_method import setNegativeSamplingAlgorithm +from .data_and_sampling_selector import setNegativeSamplingAlgorithm, select_dataset __all__ = [ 'train_test_split', - 'setNegativeSamplingAlgorithm' + 'setNegativeSamplingAlgorithm', + 'select_dataset' ] \ No newline at end of file diff --git a/hyperbench/utils/data_and_sampling_selector.py b/hyperbench/utils/data_and_sampling_selector.py new file mode 100644 index 0000000..2732002 --- /dev/null +++ b/hyperbench/utils/data_and_sampling_selector.py @@ -0,0 +1,31 @@ +from ..hyperlink_prediction.datasets import ARBDataset, IMDBHypergraphDataset, ARXIVHypergraphDataset, COURSERAHypergraphDataset, CHLPBaseDataset +from ..negative_sampling.hypergraph_negative_sampling_algorithm import SizedHypergraphNegativeSampler, MotifHypergraphNegativeSampler, CliqueHypergraphNegativeSampler, HypergraphNegativeSampler + +def setNegativeSamplingAlgorithm(ns_algorithm: str, num_node: int): + ns_method : HypergraphNegativeSampler + match(ns_algorithm): + case 'SizedHypergraphNegativeSampler': + ns_method = SizedHypergraphNegativeSampler(num_node) + case 'MotifHypergraphNegativeSampler': + ns_method = MotifHypergraphNegativeSampler(num_node) + case 'CliqueHypergraphNegativeSampler': + ns_method = CliqueHypergraphNegativeSampler(num_node) + + return ns_method + +def select_dataset(ds: str, pre_transform): + + dataset : ARBDataset + if ds in ARBDataset.GDRIVE_IDs.keys(): + dataset = ARBDataset(ds, pre_transform= pre_transform) + else: + dataset : CHLPBaseDataset + match(ds): + case 'IMDB': + dataset = IMDBHypergraphDataset("./data", pre_transform= pre_transform) + case 'ARXIV': + dataset = ARXIVHypergraphDataset("./data", pre_transform = pre_transform) + case 'COURSERA': + dataset = COURSERAHypergraphDataset("./data", pre_transform = pre_transform) + + return dataset \ No newline at end of file diff --git a/hyperbench/utils/set_negative_samplig_method.py b/hyperbench/utils/set_negative_samplig_method.py deleted file mode 100644 index 4d61ef8..0000000 --- a/hyperbench/utils/set_negative_samplig_method.py +++ /dev/null @@ -1,13 +0,0 @@ -from ..negative_sampling.hypergraph_negative_sampling_algorithm import SizedHypergraphNegativeSampler, MotifHypergraphNegativeSampler, CliqueHypergraphNegativeSampler, HypergraphNegativeSampler - -def setNegativeSamplingAlgorithm(ns_algorithm: str, num_node: int): - ns_method : HypergraphNegativeSampler - match(ns_algorithm): - case 'SizedHypergraphNegativeSampler': - ns_method = SizedHypergraphNegativeSampler(num_node) - case 'MotifHypergraphNegativeSampler': - ns_method = MotifHypergraphNegativeSampler(num_node) - case 'CliqueHypergraphNegativeSampler': - ns_method = CliqueHypergraphNegativeSampler(num_node) - - return ns_method \ No newline at end of file From b28b304ea3f7b3c6316f731ee83b3ccab2b8a894 Mon Sep 17 00:00:00 2001 From: Giovanni Semioli Date: Sun, 12 Oct 2025 17:41:54 +0200 Subject: [PATCH 6/6] add tests --- tests/__init__.py | 5 +++ tests/methods_test.py | 83 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/methods_test.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..ed48da5 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,5 @@ +from . import methods_test + +__all__ = [ + "methods_test" +] \ No newline at end of file diff --git a/tests/methods_test.py b/tests/methods_test.py new file mode 100644 index 0000000..54e1c60 --- /dev/null +++ b/tests/methods_test.py @@ -0,0 +1,83 @@ +import unittest +import subprocess + +def dataset_dict(): + datasets = {} + dataset_arb = [ + 'coauth-DBLP', + # "coauth-MAG-Geology", + # "email-Enron", + # "tags-math-sx", + # "contact-high-school", + # "contact-primary-school", + # "NDC-substances" + ] + datasets_CHLP = [ + #"IMDB", + "COURSERA", + #"ARXIV" + ] + negative_methods = [ + "SizedHypergraphNegativeSampler", + "MotifHypergraphNegativeSampler", + "CliqueHypergraphNegativeSampler" + ] + hlp_methods = ["CommonNeighbors"] + + ns_hlp_union = [] + for ns in negative_methods: + for hlp in hlp_methods: + ns_hlp_union.append([ns, hlp]) + + for dataset in dataset_arb: + datasets[dataset] = {"methods": ns_hlp_union} + + for dataset in datasets_CHLP: + datasets[dataset] = {"methods": ns_hlp_union} + + return datasets + +def create_pipelines_comand(): + datasets = dataset_dict() + pipelines = [] + + for dataset_name, content in datasets.items(): + for ns, hlp in content["methods"]: + cmd = ( + f"uv run pipeline " + f"--dataset_name {dataset_name} " + f"--hlp_method {hlp} " + f"--negative_sampling {ns}" + ) + pipelines.append(cmd) + + return pipelines + +class TestPipelineExecution(unittest.TestCase): + + def test_pipeline_execution(self): + pipelines = create_pipelines_comand() + for cmd in pipelines: + with self.subTest(cmd=cmd): + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + + if result.returncode == 0 and result.stdout.strip() != "": + print(f"[OK] {cmd}") + elif result.returncode != 0: + print(f"[FAIL] {cmd} (exit {result.returncode})") + print(f"STDERR:\n{result.stderr}") + else: + print(f"[FAIL] {cmd} — Nessun output prodotto") + + self.assertEqual( + result.returncode, 0, + msg=f"Pipeline fallita (exit {result.returncode}): {cmd}\nSTDERR:\n{result.stderr}" + ) + + self.assertTrue( + result.stdout.strip() != "", + msg=f"Pipeline terminata senza output: {cmd}" + ) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file