Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/label.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ jobs:
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
configuration-path: .github/labeler.yaml
sync-labels: true
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all setup check lint typecheck test docs docs-serve clean help
.PHONY: all setup check lint typecheck test stest docs docs-serve clean help

UV=uv
UVX=uvx
Expand Down Expand Up @@ -29,6 +29,10 @@ test:
@echo '=== Tests ==='
$(UV) run $(PYTEST) --cov=hyperbench --cov-report=term-missing

stest:
@echo '=== Test for $(FILE) ==='
$(UV) run $(PYTEST) hyperbench/tests/$(FILE) -v -s

docs:
@echo '=== Building docs ==='
$(UV) run mkdocs build -f $(MKDOCS_CONFIG)
Expand All @@ -49,6 +53,7 @@ help:
@echo " lint - Run linter"
@echo " typecheck - Run type checker"
@echo " test - Run tests"
@echo " stest - Run single test"
@echo " check - Run lint and typecheck"
@echo " docs - Build documentation"
@echo " docs-serve - Serve docs locally at http://127.0.0.1:8000"
Expand Down
8 changes: 8 additions & 0 deletions hyperbench/data/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,11 @@ def __process_x(self, num_nodes: int) -> Tensor:
x = torch.ones((num_nodes, 1), dtype=torch.float)

return x # shape [num_nodes, num_node_features]

def stats(self) -> Dict[str, Any]:
"""
Compute statistics for the dataset.
This method currently delegates to the underlying HData's stats method.
"""

return self.hdata.stats()
44 changes: 44 additions & 0 deletions hyperbench/tests/data/dataset_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,3 +1022,47 @@ def test_from_hdata_with_explicit_strategy(mock_hdata):

assert dataset.sampling_strategy == SamplingStrategy.NODE
assert len(dataset) == 3 # mock_hdata has 3 nodes


@pytest.fixture
def mock_hdata_stats():
x = torch.tensor(
[
[0.0, 1.0, 2.0, 3.0],
[1.0, 2.0, 3.0, 4.0],
[2.0, 3.0, 4.0, 5.0],
[3.0, 4.0, 5.0, 6.0],
],
dtype=torch.float,
)
hyperedge_index = torch.tensor(
[
[0, 1, 2, 2, 3],
[0, 0, 0, 1, 1],
]
)
return HData(x=x, hyperedge_index=hyperedge_index)


def test_dataset_stats_computation(mock_hdata_stats):
expected_stats = {
"shape_x": torch.Size([4, 4]),
"shape_hyperedge_attr": None,
"num_nodes": 4,
"num_hyperedges": 2,
"avg_degree_node": 1.25,
"avg_degree_hyperedge": 2.5,
"node_degree_max": 2,
"hyperedge_degree_max": 3,
"node_degree_median": 1,
"hyperedge_degree_median": 2,
"distribution_node_degree": [1, 1, 2, 1],
"distribution_hyperedge_size": [3, 2],
"distribution_node_degree_hist": {1: 3, 2: 1},
"distribution_hyperedge_size_hist": {2: 1, 3: 1},
}

dataset = Dataset.from_hdata(mock_hdata_stats)

stats = dataset.stats()
assert stats == expected_stats
54 changes: 54 additions & 0 deletions hyperbench/tests/mock/hif_stats.hif.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"edges": [
{
"attrs": {},
"edges": 0
},
{
"attrs": {},
"edges": 1
}
],
"incidences": [
{
"edge": 0,
"node": "0"
},
{
"edge": 0,
"node": "1"
},
{
"edge": 0,
"node": "2"
},
{
"edge": 1,
"node": "2"
},
{
"edge": 1,
"node": "3"
}
],
"metadata": {},
"network-type": "undirected",
"nodes": [
{
"attrs": {},
"nodes": "0"
},
{
"attrs": {},
"nodes": "1"
},
{
"attrs": {},
"nodes": "2"
},
{
"attrs": {},
"nodes": "3"
}
]
}
68 changes: 68 additions & 0 deletions hyperbench/tests/types/hdata_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ def mock_hdata():
return HData(x=x, hyperedge_index=hyperedge_index, hyperedge_attr=hyperedge_attr)


@pytest.fixture
def mock_hdata_stats():
x = torch.tensor(
[
[0.0, 1.0, 2.0, 3.0],
[1.0, 2.0, 3.0, 4.0],
[2.0, 3.0, 4.0, 5.0],
[3.0, 4.0, 5.0, 6.0],
],
dtype=torch.float,
)
hyperedge_index = torch.tensor(
[
[0, 1, 2, 2, 3],
[0, 0, 0, 1, 1],
]
)
return HData(x=x, hyperedge_index=hyperedge_index)


@pytest.mark.parametrize(
"explicit_num_nodes, expected_num_nodes",
[
Expand Down Expand Up @@ -629,3 +649,51 @@ def test_shuffle_with_no_seed_set(mock_hdata):
assert shuffled_hdata1.num_nodes == mock_hdata.num_nodes
assert shuffled_hdata1.num_hyperedges == mock_hdata.num_hyperedges
assert shuffled_hdata1.hyperedge_index.shape == mock_hdata.hyperedge_index.shape


def test_stats_returns_correct_statistics(mock_hdata_stats):
expected_stats = {
"shape_x": torch.Size([4, 4]),
"shape_hyperedge_attr": None,
"num_nodes": 4,
"num_hyperedges": 2,
"avg_degree_node": 1.25,
"avg_degree_hyperedge": 2.5,
"node_degree_max": 2,
"hyperedge_degree_max": 3,
"node_degree_median": 1,
"hyperedge_degree_median": 2,
"distribution_node_degree": [1, 1, 2, 1],
"distribution_hyperedge_size": [3, 2],
"distribution_node_degree_hist": {1: 3, 2: 1},
"distribution_hyperedge_size_hist": {2: 1, 3: 1},
}

stats = mock_hdata_stats.stats()

assert stats == expected_stats


def test_stats_with_empty_hdata():
empty_hdata = HData.empty()

expected_stats = {
"shape_x": torch.Size([0, 0]),
"shape_hyperedge_attr": None,
"num_nodes": 0,
"num_hyperedges": 0,
"avg_degree_node": 0,
"avg_degree_hyperedge": 0,
"node_degree_max": 0,
"hyperedge_degree_max": 0,
"node_degree_median": 0,
"hyperedge_degree_median": 0,
"distribution_node_degree": [],
"distribution_hyperedge_size": [],
"distribution_node_degree_hist": {},
"distribution_hyperedge_size_hist": {},
}

stats = empty_hdata.stats()

assert stats == expected_stats
91 changes: 91 additions & 0 deletions hyperbench/tests/types/hypergraph_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,97 @@ def test_neighbors_of_all(hyperedges, expected_neighbors_map):
assert hypergraph.neighbors_of_all() == expected_neighbors_map


@pytest.mark.parametrize(
"hyperedges, expected_stats",
[
pytest.param(
[],
{
"num_nodes": 0,
"num_hyperedges": 0,
"avg_degree_node": 0.0,
"avg_degree_hyperedge": 0.0,
"node_degree_max": 0,
"hyperedge_degree_max": 0,
"node_degree_median": 0.0,
"hyperedge_degree_median": 0.0,
"distribution_node_degree": [],
"distribution_hyperedge_size": [],
"distribution_node_degree_hist": {},
"distribution_hyperedge_size_hist": {},
},
id="empty_hypergraph",
),
pytest.param(
[[0, 1]],
{
"num_nodes": 2,
"num_hyperedges": 1,
"avg_degree_node": 1.0,
"avg_degree_hyperedge": 2.0,
"node_degree_max": 1,
"hyperedge_degree_max": 2,
"node_degree_median": 1.0,
"hyperedge_degree_median": 2.0,
"distribution_node_degree": [1, 1],
"distribution_hyperedge_size": [2],
"distribution_node_degree_hist": {1: 2},
"distribution_hyperedge_size_hist": {2: 1},
},
id="single_hyperedge_two_nodes",
),
pytest.param(
[[0, 1, 2], [2, 3]],
{
"num_nodes": 4,
"num_hyperedges": 2,
"avg_degree_node": 1.25,
"avg_degree_hyperedge": 2.5,
"node_degree_max": 2,
"hyperedge_degree_max": 3,
"node_degree_median": 1.0,
"hyperedge_degree_median": 2.5,
"distribution_node_degree": [1, 1, 1, 2],
"distribution_hyperedge_size": [3, 2],
"distribution_node_degree_hist": {1: 3, 2: 1},
"distribution_hyperedge_size_hist": {3: 1, 2: 1},
},
id="two_hyperedges_varying_sizes",
),
],
)
def test_hypergraph_stats_returns_correct_statistics(hyperedges, expected_stats):
hypergraph = Hypergraph(hyperedges)
stats = hypergraph.stats()

assert stats == expected_stats


def test_hifhypergraph_stats_returns_correct_statistics():
expected_stats = {
"num_nodes": 4,
"num_hyperedges": 2,
"avg_degree_node": 1.25,
"avg_degree_hyperedge": 2.5,
"node_degree_max": 2,
"hyperedge_degree_max": 3,
"node_degree_median": 1.0,
"hyperedge_degree_median": 2.5,
"distribution_node_degree": [1, 1, 1, 2],
"distribution_hyperedge_size": [2, 3],
"distribution_node_degree_hist": {1: 3, 2: 1},
"distribution_hyperedge_size_hist": {3: 1, 2: 1},
}

with open(f"{MOCK_BASE_PATH}/hif_stats.hif.json", "r") as f:
hiftext = json.load(f)

hypergraph = HIFHypergraph.from_hif(hiftext)
stats = hypergraph.stats()

assert stats == expected_stats


@pytest.mark.parametrize(
"hyperedge_index_tensor, hyperedge_id, expected_nodes",
[
Expand Down
Loading
Loading