Files
2026-07-13 12:36:30 +08:00

1002 lines
29 KiB
Python

import sys
from copy import deepcopy
import easygraph as eg
import pytest
@pytest.fixture()
def g1():
e_list = [(0, 1, 2, 5), (0, 1), (2, 3, 4), (3, 2, 4)]
g = eg.Hypergraph(6, e_list=e_list)
return g
@pytest.fixture()
def g2():
e_list = [(1, 2, 3), (0, 1, 3), (0, 1), (2, 4, 3), (2, 3)]
e_weight = [0.5, 1, 0.5, 1, 0.5]
g = eg.Hypergraph(5, e_list=e_list, e_weight=e_weight)
return g
@pytest.fixture()
def g3():
e_list = [[0, 1], [0, 1, 2], [2, 3, 4]]
e_weight = [1, 1, 1]
g = eg.Hypergraph(5, e_list=e_list, e_weight=e_weight)
return g
def test_expansion(g3):
star_expansion_graph = g3.get_star_expansion()
node_clique_expansion_graph = g3.get_clique_expansion()
edge_clique_expansion_graph = g3.get_clique_expansion()
print(star_expansion_graph.edges)
print(node_clique_expansion_graph.edges)
print(edge_clique_expansion_graph.edges)
def test_property(g1, g2):
assert g2.distance(1, 2) == 1
assert g2.diameter() == 2
assert g1.adjacency_matrix != None
assert g1.edge_adjacency_matrix != None
assert g2.adjacency_matrix != None
assert g2.edge_adjacency_matrix != None
def test_save(g1, tmp_path):
from easygraph import load_structure
print("g1:", g1, g1.e)
g1.save(tmp_path / "g1")
g2 = load_structure(tmp_path / "g1")
for e1, e2 in zip(g1.e[0], g2.e[0]):
assert e1 == e2
for w1, w2 in zip(g1.e[1], g2.e[1]):
assert w1 == w2
# test construction
def test_from_feature_kNN():
import numpy as np
import scipy.spatial
import torch
ft = np.random.rand(32, 8)
cdist = scipy.spatial.distance.cdist(ft, ft)
tk_mat = np.argsort(cdist, axis=1)[:, :3]
hg = eg.Hypergraph.from_feature_kNN(torch.tensor(ft), k=3)
assert tuple(sorted(tk_mat[0].tolist())) in hg.e[0]
assert tuple(sorted(tk_mat[8].tolist())) in hg.e[0]
assert tuple(sorted(tk_mat[13].tolist())) in hg.e[0]
assert tuple(sorted(tk_mat[26].tolist())) in hg.e[0]
def test_from_graph():
g = eg.Graph()
g.add_nodes(list(range(0, 5)))
g.add_edges(
[(0, 1), (0, 3), (1, 4), (2, 3), (3, 4)],
[
{"weight": 1.0},
{"weight": 1.0},
{"weight": 1.0},
{"weight": 1.0},
{"weight": 1.0},
],
)
hg = eg.Hypergraph.from_graph(g)
assert hg.num_e == 5
assert (0, 1) in hg.e[0]
assert (1, 4) in hg.e[0]
def test_from_graph_kHop():
g = eg.Graph()
g.add_nodes(range(0, 5))
g.add_edges(
[(0, 1), (0, 3), (1, 4), (2, 3)],
[{"weight": 1.0}, {"weight": 1.0}, {"weight": 1.0}, {"weight": 1.0}],
)
hg = eg.Hypergraph.from_graph_kHop(g, k=1)
assert hg.num_e == 5
assert (0, 1, 3) in hg.e[0]
assert (0, 1, 4) in hg.e[0]
assert (1, 4) in hg.e[0]
assert (2, 3) in hg.e[0]
assert (0, 2, 3) in hg.e[0]
hg = eg.Hypergraph.from_graph_kHop(g, k=2)
assert hg.num_e == 5
assert (0, 1, 3, 4) in hg.e[0]
hg = eg.Hypergraph.from_graph_kHop(g, k=2, only_kHop=True)
assert hg.num_e == 4
# test representation
def test_empty():
g = eg.Hypergraph(10)
assert g.num_v == 10
assert g.e == ([], [], [])
def test_init(g1, g2):
assert g1.num_v == 6
assert g1.num_e == 3
assert g1.e[0] == [(0, 1, 2, 5), (0, 1), (2, 3, 4)]
assert g1.e[1] == [1, 1, 1]
assert g2.num_v == 5
assert g2.num_e == 5
assert g2.e[0] == [(1, 2, 3), (0, 1, 3), (0, 1), (2, 3, 4), (2, 3)]
assert g2.e[1] == [0.5, 1, 0.5, 1, 0.5]
def test_clear(g1):
assert g1.num_e == 3
g1.clear()
assert g1.num_e == 0
assert g1.e == ([], [], [])
def test_add_and_merge_hyperedges(g1):
assert g1.e[1] == [1, 1, 1]
print("g1:", g1, g1.e)
g1.add_hyperedges(e_list=[0, 1], e_weight=3, merge_op="mean")
assert g1.e[1] == [1, 2, 1]
assert g1.e[0] == [(0, 1, 2, 5), (0, 1), (2, 3, 4)]
g1.add_hyperedges([(2, 4, 3), (1, 0), (3, 4)], [1, 1, 1], merge_op="sum")
assert g1.e[0] == [(0, 1, 2, 5), (0, 1), (2, 3, 4), (3, 4)]
assert g1.e[1] == [1, 3, 2, 1]
def test_add_hyperedges_from_feature_kNN(g1):
import numpy as np
import scipy.spatial
import torch
origin_e = deepcopy(g1.e[0])
ft = np.random.rand(6, 8)
cdist = scipy.spatial.distance.cdist(ft, ft)
tk_mat = np.argsort(cdist, axis=1)[:, :3]
g1.add_hyperedges_from_feature_kNN(torch.tensor(ft), k=3, group_name="knn")
assert tuple(sorted(tk_mat[0].tolist())) in g1.e_of_group("knn")[0]
assert tuple(sorted(tk_mat[3].tolist())) in g1.e_of_group("knn")[0]
assert tuple(sorted(tk_mat[4].tolist())) in g1.e_of_group("knn")[0]
assert tuple(sorted(tk_mat[5].tolist())) in g1.e_of_group("knn")[0]
for e in origin_e:
assert e in g1.e_of_group("main")[0]
for e in g1.e_of_group("main")[0]:
assert e in origin_e
# def test_add_hyperedges_from_graph(g1):
# g = eg.graph_Gnm(6, 3)
# origin_e = deepcopy(g1.e[0])
#
# g1.add_hyperedges_from_graph(g, group_name="graph")
# g_e = g.e[0]
# g1_e = g1.e_of_group("graph")[0]
#
# for e in g_e:
# assert e in g1_e
#
# for e in origin_e:
# assert e in g1.e_of_group("main")[0]
#
# for e in g1.e[0]:
# assert e in origin_e or e in g_e
def test_add_hyperedges_from_graph_kHop(g1):
g = eg.graph_Gnm(6, 5)
origin_e = deepcopy(g1.e[0])
for k in range(1, 3):
gg1 = deepcopy(g1)
gg1.add_hyperedges_from_graph_kHop(g, k=k, group_name="kHop")
khop = [[] for _ in range(6)]
for kk in range(k):
for v in range(6):
if kk == 0:
khop[v] = g.nbr_v(v)
else:
kk_hop_v = []
for nbr in khop[v]:
kk_hop_v += g.nbr_v(nbr)
khop[v] += kk_hop_v
khop[v] = list(set(khop[v]))
for v in range(6):
edge = [v] + khop[v]
edge = tuple(set(sorted(edge)))
assert edge in gg1.e_of_group("kHop")[0]
gg2 = deepcopy(g1)
gg2.add_hyperedges_from_graph_kHop(g, k=k, group_name="kHop", only_kHop=True)
khop = [[] for _ in range(6)]
for kk in range(k):
for v in range(6):
if len(khop[v]) == 0:
khop[v] = g.nbr_v(v)
else:
kk_hop_v = []
for nbr in khop[v]:
kk_hop_v += g.nbr_v(nbr)
khop[v] = kk_hop_v
khop[v] = list(set(khop[v]))
for v in range(6):
edge = [v] + khop[v]
edge = tuple(set(sorted(edge)))
assert edge in gg2.e_of_group("kHop")[0]
for e in origin_e:
assert e in gg1.e_of_group("main")[0]
assert e in gg2.e_of_group("main")[0]
for e in gg1.e_of_group("main")[0]:
assert e in origin_e
for e in gg2.e_of_group("main")[0]:
assert e in origin_e
def test_remove_hyperedges(g1):
assert g1.e[0] == [(0, 1, 2, 5), (0, 1), (2, 3, 4)]
assert g1.e[1] == [1, 1, 1]
g1.remove_hyperedges([0, 1])
assert (0, 1) not in g1.e[0]
assert (0, 1, 5) not in g1.e[0]
g1.add_hyperedges([[0, 1, 5], [2, 3, 4]])
assert (0, 1, 5) in g1.e[0]
g1.remove_hyperedges([[0, 1, 5], (0, 1, 2, 5)])
assert (0, 1, 5) not in g1.e[0]
assert (0, 1, 2, 5) not in g1.e[0]
g1.clear()
assert g1.num_e == 0
assert g1.e == ([], [], [])
# def test_remove_group(g1):
# origin_e = deepcopy(g1.e[0])
#
# g1.add_hyperedges(([0, 1, 2, 5], [0, 1]), group_name="test")
# for e in origin_e:
# assert e in g1.e_of_group("main")[0]
# for e in g1.e_of_group("main")[0]:
# assert e in origin_e
#
# # g1.remove_group("none")
#
# g1.remove_group("test")
# assert "test" not in g1.group_names
#
# for e in origin_e:
# assert e in g1.e_of_group("main")[0]
# for e in g1.e_of_group("main")[0]:
# assert e in origin_e
#
# g1.remove_group("main")
#
# assert len(g1.e[0]) == 0
# assert len(g1.e[1]) == 0
#
#
# def test_add_and_remove_group(g1):
# assert g1.group_names == ["main"]
# g1.add_hyperedges([0, 2, 3], group_name="knn")
# assert len(g1.group_names) == 2
# assert "main" in g1.group_names
# assert "knn" in g1.group_names
# assert (0, 2, 3) in g1.e[0]
# assert (0, 2, 3) in g1.e_of_group("knn")[0]
# assert (0, 2, 3) not in g1.e_of_group("main")[0]
# g1.remove_hyperedges([0, 2, 3], group_name="knn")
# assert (0, 2, 3) not in g1.e[0]
# assert (0, 2, 3) not in g1.e_of_group("knn")[0]
def test_deg(g1, g2):
assert g1.deg_v == [2, 2, 2, 1, 1, 1]
assert g1.deg_e == [4, 2, 3]
assert g2.deg_v == [2, 3, 3, 4, 1]
assert g2.deg_e == [3, 3, 2, 3, 2]
# def test_deg_group(g1):
# assert g1.deg_v == [2, 2, 2, 1, 1, 1]
# assert g1.deg_e == [4, 2, 3]
# g1.add_hyperedges([0, 2], 1, group_name="knn")
# assert g1.deg_v == [3, 2, 3, 1, 1, 1]
# assert g1.deg_e == [4, 2, 3, 2]
# assert g1.deg_v_of_group("main") == [2, 2, 2, 1, 1, 1]
# assert g1.deg_e_of_group("main") == [4, 2, 3]
# assert g1.deg_v_of_group("knn") == [1, 0, 1, 0, 0, 0]
# assert g1.deg_e_of_group("knn") == [2]
def test_nbr(g1, g2):
assert g1.nbr_v(0) == [0, 1, 2, 5]
assert g1.nbr_e(1) == [0, 1]
assert g2.nbr_v(2) == [0, 1]
assert g2.nbr_e(4) == [3]
# def test_nbr_group(g1):
# print("g1:", g1.e, g1.v)
# assert g1.nbr_v(1) == [0, 1]
# assert g1.nbr_e(0) == [0, 1]
# g1.add_hyperedges([[0, 1]], group_name="knn")
# assert g1.nbr_v(1) == [0, 1]
# assert g1.nbr_e(1) == [0, 1, 3]
# assert g1.nbr_v_of_group(1, "main") == [0, 1]
# assert g1.nbr_e_of_group(2, "main") == [0, 2]
# assert g1.nbr_v_of_group(0, "knn") == [0, 1]
# assert g1.nbr_e_of_group(1, "knn") == [0]
def test_clone(g1):
assert g1.num_v == 6
assert g1.num_e == 3
g1_clone = g1.clone()
g1_clone.add_hyperedges([0, 2], 1, group_name="knn")
assert g1.num_e == 3
assert g1_clone.num_e == 4
# test deep learning
def test_v2e_index(g1):
import torch
v2e_src = g1.v2e_src.view(-1, 1)
v2e_dst = g1.v2e_dst.view(-1, 1)
index = torch.cat((v2e_src, v2e_dst), dim=1)
index = index.numpy().tolist()
index = list(map(lambda x: tuple(x), index))
assert (0, 0) in index
assert (1, 0) in index
assert (2, 0) in index
assert (5, 0) in index
assert (0, 1) in index
assert (1, 1) in index
assert (2, 2) in index
assert (3, 2) in index
assert (4, 2) in index
def test_v2e_index_group(g1):
import torch
v2e_src = g1.v2e_src_of_group("main").view(-1, 1)
v2e_dst = g1.v2e_dst_of_group("main").view(-1, 1)
index = torch.cat((v2e_src, v2e_dst), dim=1)
index = index.numpy().tolist()
index = list(map(lambda x: tuple(x), index))
assert (0, 0) in index
assert (1, 0) in index
assert (2, 0) in index
assert (5, 0) in index
assert (0, 1) in index
assert (1, 1) in index
assert (2, 2) in index
assert (3, 2) in index
assert (4, 2) in index
def test_e2v_index(g1):
import torch
e2v_src = g1.e2v_src.view(-1, 1)
e2v_dst = g1.e2v_dst.view(-1, 1)
index = torch.cat((e2v_src, e2v_dst), dim=1)
index = index.numpy().tolist()
index = list(map(lambda x: tuple(x), index))
assert (0, 0) in index
assert (0, 1) in index
assert (0, 2) in index
assert (0, 5) in index
assert (1, 0) in index
assert (1, 1) in index
assert (2, 2) in index
assert (2, 3) in index
assert (2, 4) in index
def test_e2v_index_group(g1):
import torch
e2v_src = g1.e2v_src_of_group("main").view(-1, 1)
e2v_dst = g1.e2v_dst_of_group("main").view(-1, 1)
index = torch.cat((e2v_src, e2v_dst), dim=1)
index = index.numpy().tolist()
index = list(map(lambda x: tuple(x), index))
assert (0, 0) in index
assert (0, 1) in index
assert (0, 2) in index
assert (0, 5) in index
assert (1, 0) in index
assert (1, 1) in index
assert (2, 2) in index
assert (2, 3) in index
assert (2, 4) in index
def test_H(g1):
import torch
print("g1", g1.H.to_dense())
assert (
g1.H.to_dense().cpu()
== torch.tensor(
[[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]]
)
).all()
# def test_H_group(g1):
# import torch
#
# g1.add_hyperedges([0, 4, 5], group_name="knn")
# assert (
# g1.H.to_dense().cpu()
# == torch.tensor(
# [
# [1, 1, 0, 1],
# [1, 1, 0, 0],
# [1, 0, 1, 0],
# [0, 0, 1, 0],
# [0, 0, 1, 1],
# [1, 0, 0, 1],
# ]
# )
# ).all()
# assert (
# g1.H_of_group("main").to_dense().cpu()
# == torch.tensor(
# [[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]]
# )
# ).all()
# assert (
# g1.H_of_group("knn").to_dense().cpu()
# == torch.tensor([[1], [0], [0], [0], [1], [1]])
# ).all()
def test_H_T(g1):
import torch
assert (
g1.H_T.to_dense().cpu()
== torch.tensor(
[[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]]
).t()
).all()
# def test_H_T_group(g1):
# import torch
#
# g1.add_hyperedges([0, 4, 5], group_name="knn")
# assert (
# g1.H_T.to_dense().cpu()
# == torch.tensor(
# [
# [1, 1, 0, 1],
# [1, 1, 0, 0],
# [1, 0, 1, 0],
# [0, 0, 1, 0],
# [0, 0, 1, 1],
# [1, 0, 0, 1],
# ]
# ).t()
# ).all()
# assert (
# g1.H_T_of_group("main").to_dense().cpu()
# == torch.tensor(
# [[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]]
# ).t()
# ).all()
# assert (
# g1.H_T_of_group("knn").to_dense().cpu() == torch.tensor([[1, 0, 0, 0, 1, 1]])
# ).all()
def test_W_e(g2):
import torch
assert (
g2.W_e.to_sparse_coo().cpu()._values() == torch.tensor([0.5, 1, 0.5, 1, 0.5])
).all()
# def test_W_e_group(g2):
# import torch
#
# g2.add_hyperedges([0, 4, 5], group_name="knn")
# assert (g2.W_e.cpu()._values() == torch.tensor([0.5, 1, 0.5, 1, 0.5, 1])).all()
# assert (
# g2.W_e_of_group("main").cpu()._values() == torch.tensor([0.5, 1, 0.5, 1, 0.5])
# ).all()
# assert (g2.W_e_of_group("knn").cpu()._values() == torch.tensor([1])).all()
def test_D(g1, g2):
import torch
assert (g1.D_v.cpu()._values() == torch.tensor([2, 2, 2, 1, 1, 1])).all()
assert (g1.D_e.to_sparse_coo().cpu()._values() == torch.tensor([4, 2, 3])).all()
assert (g2.D_v.cpu()._values() == torch.tensor([2, 3, 3, 4, 1])).all()
assert (
g2.D_e.to_sparse_coo().cpu()._values() == torch.tensor([3, 3, 2, 3, 2])
).all()
# def test_D_group(g1):
# import torch
#
# assert (g1.D_v.cpu()._values() == torch.tensor([2, 2, 2, 1, 1, 1])).all()
# assert (g1.D_e.cpu()._values() == torch.tensor([4, 2, 3])).all()
# g1.add_hyperedges([[0, 2], [1, 2, 3]], group_name="knn")
# assert (g1.D_v.cpu()._values() == torch.tensor([3, 3, 4, 2, 1, 1])).all()
# assert (g1.D_e.cpu()._values() == torch.tensor([4, 2, 3, 2, 3])).all()
# assert (
# g1.D_v_of_group("main").cpu()._values() == torch.tensor([2, 2, 2, 1, 1, 1])
# ).all()
# assert (g1.D_e_of_group("main").cpu()._values() == torch.tensor([4, 2, 3])).all()
# assert (
# g1.D_v_of_group("knn").cpu()._values() == torch.tensor([1, 1, 2, 1, 0, 0])
# ).all()
# assert (g1.D_e_of_group("knn").cpu()._values() == torch.tensor([2, 3])).all()
def test_D_neg(g1, g2):
import torch
# -1
assert (
g1.D_v_neg_1.to_sparse_coo().cpu()._values()
== torch.tensor([2, 2, 2, 1, 1, 1]) ** (-1.0)
).all()
assert (
g1.D_e_neg_1.to_sparse_coo().cpu()._values()
== torch.tensor([4, 2, 3]) ** (-1.0)
).all()
assert (
g2.D_v_neg_1.to_sparse_coo().cpu()._values()
== torch.tensor([2, 3, 3, 4, 1]) ** (-1.0)
).all()
assert (
g2.D_e_neg_1.to_sparse_coo().cpu()._values()
== torch.tensor([3, 3, 2, 3, 2]) ** (-1.0)
).all()
# -1/2
assert (
g1.D_v_neg_1_2.to_sparse_coo().cpu()._values()
== torch.tensor([2, 2, 2, 1, 1, 1]) ** (-0.5)
).all()
assert (
g2.D_v_neg_1_2.to_sparse_coo().cpu()._values()
== torch.tensor([2, 3, 3, 4, 1]) ** (-0.5)
).all()
# isolated vertex
g3 = eg.Hypergraph(num_v=3, e_list=[0, 1])
assert (
g3.D_v_neg_1.to_sparse_coo().cpu()._values() == torch.tensor([1, 1, 0])
).all()
# def test_D_neg_group(g1):
# import torch
#
# # -1
# assert (
# g1.D_v_neg_1.cpu()._values() == torch.tensor([2, 2, 2, 1, 1, 1]) ** (-1.0)
# ).all()
# assert (g1.D_e_neg_1.cpu()._values() == torch.tensor([4, 2, 3]) ** (-1.0)).all()
# g1.add_hyperedges([[0, 2], [1, 2, 3]], group_name="knn")
# assert (
# g1.D_v_neg_1.cpu()._values() == torch.tensor([3, 3, 4, 2, 1, 1]) ** (-1.0)
# ).all()
# assert (
# g1.D_e_neg_1.cpu()._values() == torch.tensor([4, 2, 3, 2, 3]) ** (-1.0)
# ).all()
# assert (
# g1.D_v_neg_1_of_group("main").cpu()._values()
# == torch.tensor([2, 2, 2, 1, 1, 1]) ** (-1.0)
# ).all()
# assert (
# g1.D_e_neg_1_of_group("main").cpu()._values()
# == torch.tensor([4, 2, 3]) ** (-1.0)
# ).all()
# assert (
# g1.D_v_neg_1_of_group("knn").cpu()._values()
# == torch.tensor([1 / 1, 1 / 1, 1 / 2, 1 / 1, 0, 0])
# ).all()
# assert (
# g1.D_e_neg_1_of_group("knn").cpu()._values() == torch.tensor([2, 3]) ** (-1.0)
# ).all()
# # -1/2
# assert (
# g1.D_v_neg_1_2.cpu()._values() == torch.tensor([3, 3, 4, 2, 1, 1]) ** (-0.5)
# ).all()
# assert (
# g1.D_v_neg_1_2_of_group("main").cpu()._values()
# == torch.tensor([2, 2, 2, 1, 1, 1]) ** (-0.5)
# ).all()
# assert (
# g1.D_v_neg_1_2_of_group("knn").cpu()._values()
# == torch.tensor([1 ** (-0.5), 1 ** (-0.5), 2 ** (-0.5), 1 ** (-0.5), 0, 0])
# ).all()
def test_N(g1, g2):
import torch
assert (g1.N_v(0).cpu() == torch.tensor([0, 1, 2, 5])).all()
assert (g1.N_e(2).cpu() == torch.tensor([0, 2])).all()
assert (g2.N_v(1).cpu() == torch.tensor([0, 1, 3])).all()
assert (g2.N_e(3).cpu() == torch.tensor([0, 1, 3, 4])).all()
# def test_N_group(g1):
# import torch
#
# assert (g1.N_v(1).cpu() == torch.tensor([0, 1])).all()
# assert (g1.N_e(1).cpu() == torch.tensor([0, 1])).all()
# g1.add_hyperedges([[0, 1], [1, 2]], group_name="knn")
# assert (g1.N_v(1).cpu() == torch.tensor([0, 1])).all()
# assert (g1.N_e(1).cpu() == torch.tensor([0, 1, 3, 4])).all()
# assert (g1.N_v_of_group(1, "main").cpu() == torch.tensor([0, 1])).all()
# assert (g1.N_e_of_group(2, "main").cpu() == torch.tensor([0, 2])).all()
# assert (g1.N_v_of_group(1, "knn").cpu() == torch.tensor([1, 2])).all()
# assert (g1.N_e_of_group(1, "knn").cpu() == torch.tensor([0, 1])).all()
#
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
def test_L_HGNN(g1):
import torch
print("g1:", g1, g1.e)
H = g1.H.to_dense().cpu()
D_v_neg_1_2 = torch.diag(H.sum(dim=1).view(-1) ** (-0.5))
D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
W_e = g1.W_e.to_dense()
L_HGNN = D_v_neg_1_2 @ H @ W_e @ D_e_neg_1 @ H.t() @ D_v_neg_1_2
assert (L_HGNN == g1.L_HGNN.to_dense().cpu()).all()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
# def test_L_HGNN_group(g1):
# import torch
#
# g1.add_hyperedges([[0, 1]], group_name="knn")
# # all
# H = g1.H.to_dense().cpu()
# D_v_neg_1_2 = torch.diag(H.sum(dim=1).view(-1) ** (-0.5))
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e.to_dense()
# L_HGNN = D_v_neg_1_2 @ H @ W_e @ D_e_neg_1 @ H.t() @ D_v_neg_1_2
# assert (L_HGNN == g1.L_HGNN.to_dense().cpu()).all()
# # main group
# H = g1.H_of_group("main").to_dense().cpu()
# D_v_neg_1_2 = torch.diag(H.sum(dim=1).view(-1) ** (-0.5))
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e_of_group("main").to_dense()
# L_HGNN = D_v_neg_1_2 @ H @ W_e @ D_e_neg_1 @ H.t() @ D_v_neg_1_2
# assert (L_HGNN == g1.L_HGNN_of_group("main").to_dense().cpu()).all()
# # knn group
# H = g1.H_of_group("knn").to_dense().cpu()
# D_v_neg_1_2 = H.sum(dim=1).view(-1) ** (-0.5)
# D_v_neg_1_2[torch.isinf(D_v_neg_1_2)] = 0
# D_v_neg_1_2 = torch.diag(D_v_neg_1_2)
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e_of_group("knn").to_dense()
# L_HGNN = D_v_neg_1_2 @ H @ W_e @ D_e_neg_1 @ H.t() @ D_v_neg_1_2
# assert (L_HGNN == g1.L_HGNN_of_group("knn").to_dense().cpu()).all()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
def test_smoothing():
import torch
x = torch.rand(10, 5)
L = torch.rand(10, 10)
g = eg.Hypergraph(10)
lbd = 0.1
assert pytest.approx(g.smoothing(x, L, lbd)) == x + lbd * L @ x
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
def test_L_sym(g1):
import torch
H = g1.H.to_sparse_coo().to_dense().cpu()
D_v_neg_1_2 = torch.diag(H.sum(dim=1).view(-1) ** (-0.5))
D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
W_e = g1.W_e.to_dense()
L_sym = (
torch.eye(H.shape[0])
- D_v_neg_1_2.to_sparse_coo()
@ H.to_sparse_coo()
@ W_e
@ D_e_neg_1.to_sparse_coo()
@ H.t().to_sparse_coo()
@ D_v_neg_1_2.to_sparse_coo()
)
assert (L_sym == g1.L_sym.to_dense().cpu()).all()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
# def test_L_sym_group(g1):
# import torch
#
# g1.add_hyperedges([[0, 1]], group_name="knn")
# # all
# H = g1.H.to_dense().cpu()
# D_v_neg_1_2 = torch.diag(H.sum(dim=1).view(-1) ** (-0.5))
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e.to_dense()
# L_sym = (
# torch.eye(H.shape[0]) - D_v_neg_1_2 @ H @ W_e @ D_e_neg_1 @ H.t() @ D_v_neg_1_2
# )
# assert (L_sym == g1.L_sym.to_dense().cpu()).all()
# # main group
# H = g1.H_of_group("main").to_dense().cpu()
# D_v_neg_1_2 = torch.diag(H.sum(dim=1).view(-1) ** (-0.5))
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e_of_group("main").to_dense()
# L_sym = (
# torch.eye(H.shape[0]) - D_v_neg_1_2 @ H @ W_e @ D_e_neg_1 @ H.t() @ D_v_neg_1_2
# )
# assert (L_sym == g1.L_sym_of_group("main").to_dense().cpu()).all()
# # knn group
# H = g1.H_of_group("knn").to_dense().cpu()
# D_v_neg_1_2 = H.sum(dim=1).view(-1) ** (-0.5)
# D_v_neg_1_2[torch.isinf(D_v_neg_1_2)] = 0
# D_v_neg_1_2 = torch.diag(D_v_neg_1_2)
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e_of_group("knn").to_dense()
# L_sym = (
# torch.eye(H.shape[0]) - D_v_neg_1_2 @ H @ W_e @ D_e_neg_1 @ H.t() @ D_v_neg_1_2
# )
# assert (L_sym == g1.L_sym_of_group("knn").to_dense().cpu()).all()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
# def test_L_rw(g1):
# import torch
#
# H = g1.H.to_dense().cpu()
# D_v_neg_1 = torch.diag(H.sum(dim=1).view(-1) ** (-1))
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e.to_dense()
# L_rw = torch.eye(H.shape[0]) - D_v_neg_1 @ H @ W_e @ D_e_neg_1 @ H.t()
# assert (L_rw == g1.L_rw.to_dense().cpu()).all()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
# def test_L_rw_group(g1):
# import torch
#
# g1.add_hyperedges([[0, 1]], group_name="knn")
# # all
# H = g1.H.to_dense().cpu()
# D_v_neg_1 = torch.diag(H.sum(dim=1).view(-1) ** (-1))
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e.to_dense()
# L_rw = torch.eye(H.shape[0]) - D_v_neg_1 @ H @ W_e @ D_e_neg_1 @ H.t()
# assert (L_rw == g1.L_rw.to_dense().cpu()).all()
# # main group
# H = g1.H_of_group("main").to_dense().cpu()
# D_v_neg_1 = torch.diag(H.sum(dim=1).view(-1) ** (-1))
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e_of_group("main").to_dense()
# L_rw = torch.eye(H.shape[0]) - D_v_neg_1 @ H @ W_e @ D_e_neg_1 @ H.t()
# assert (L_rw == g1.L_rw_of_group("main").to_dense().cpu()).all()
# # knn group
# H = g1.H_of_group("knn").to_dense().cpu()
# D_v_neg_1 = H.sum(dim=1).view(-1) ** (-1)
# D_v_neg_1[torch.isinf(D_v_neg_1)] = 0
# D_v_neg_1 = torch.diag(D_v_neg_1)
# D_e_neg_1 = torch.diag(H.sum(dim=0).view(-1) ** (-1))
# W_e = g1.W_e_of_group("knn").to_dense()
# L_rw = torch.eye(H.shape[0]) - D_v_neg_1 @ H @ W_e @ D_e_neg_1 @ H.t()
# assert (L_rw == g1.L_rw_of_group("knn").to_dense().cpu()).all()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
def test_smoothing_with_HGNN(g1):
import torch
H = torch.tensor(
[[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]],
dtype=torch.float32,
)
D_v_inv_1_2 = H.sum(1).view(-1) ** (-0.5)
D_v_inv_1_2[torch.isinf(D_v_inv_1_2)] = 0
D_v_inv_1_2 = torch.diag(D_v_inv_1_2)
D_e_inv = H.sum(0).view(-1) ** (-1)
D_e_inv[torch.isinf(D_e_inv)] = 0
D_e_inv = torch.diag(D_e_inv)
x = torch.rand(H.shape[0], 8)
gt = D_v_inv_1_2 @ H @ D_e_inv @ H.t() @ D_v_inv_1_2 @ x
res = g1.smoothing_with_HGNN(x)
assert pytest.approx(gt, rel=1e-6) == res.cpu()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
def test_smoothing_with_HGNN_group(g1):
import torch
H = torch.tensor(
[[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]],
dtype=torch.float32,
)
D_v_inv_1_2 = H.sum(1).view(-1) ** (-0.5)
D_v_inv_1_2[torch.isinf(D_v_inv_1_2)] = 0
D_v_inv_1_2 = torch.diag(D_v_inv_1_2)
D_e_inv = H.sum(0).view(-1) ** (-1)
D_e_inv[torch.isinf(D_e_inv)] = 0
D_e_inv = torch.diag(D_e_inv)
x = torch.rand(H.shape[0], 8)
gt = D_v_inv_1_2 @ H @ D_e_inv @ H.t() @ D_v_inv_1_2 @ x
res = g1.smoothing_with_HGNN_of_group("main", x)
assert pytest.approx(gt, rel=1e-6) == res.cpu()
def test_v2e_message_passing(g1):
import torch
H = torch.tensor(
[[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]],
dtype=torch.float32,
)
x = torch.rand(H.shape[0], 8)
gt_sum = H.t() @ x
res_sum = g1.v2e(x, aggr="sum")
assert pytest.approx(gt_sum, rel=1e-6) == res_sum.cpu()
D_e_inv = H.sum(0).view(-1) ** (-1)
D_e_inv[torch.isinf(D_e_inv)] = 0
D_e_inv = torch.diag(D_e_inv)
gt_mean = D_e_inv @ gt_sum
res_mean = g1.v2e(x, aggr="mean")
assert pytest.approx(gt_mean, rel=1e-6) == res_mean.cpu()
def test_e2v_message_passing(g1):
import torch
H = torch.tensor(
[[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]],
dtype=torch.float32,
)
x = torch.rand(3, 8)
gt_sum = H @ x
res_sum = g1.e2v(x, aggr="sum")
assert pytest.approx(gt_sum, rel=1e-6) == res_sum.cpu()
D_v_inv = H.sum(1).view(-1) ** (-1)
D_v_inv[torch.isinf(D_v_inv)] = 0
D_v_inv = torch.diag(D_v_inv)
gt_mean = D_v_inv @ gt_sum
res_mean = g1.e2v(x, aggr="mean")
assert pytest.approx(gt_mean, rel=1e-6) == res_mean.cpu()
def test_v2v_message_passing(g1):
import torch
H = torch.tensor(
[[1, 1, 0], [1, 1, 0], [1, 0, 1], [0, 0, 1], [0, 0, 1], [1, 0, 0]],
dtype=torch.float32,
)
x = torch.rand(6, 8)
gt_sum = H @ H.t() @ x
res_sum = g1.v2v(x, aggr="sum")
assert pytest.approx(gt_sum, rel=1e-6) == res_sum.cpu()
D_v_inv = H.sum(1).view(-1) ** (-1)
D_v_inv[torch.isinf(D_v_inv)] = 0
D_v_inv = torch.diag(D_v_inv)
D_e_inv = H.sum(0).view(-1) ** (-1)
D_e_inv[torch.isinf(D_e_inv)] = 0
D_e_inv = torch.diag(D_e_inv)
gt_mean = D_v_inv @ H @ D_e_inv @ H.t() @ x
res_mean = g1.v2v(x, aggr="mean")
assert pytest.approx(gt_mean, rel=1e-6) == res_mean.cpu()
@pytest.mark.skipif(
sys.version_info.major <= 3 and sys.version_info.minor < 7,
reason="python requires >= 3.7",
)
# def test_graph_and_hypergraph():
# import torch
#
# g = eg.Graph()
# g.add_nodes([0, 1, 2, 3])
# g.add_edges(
# [(0, 1), (0, 2), (1, 3)], [{"weight": 1.0}, {"weight": 1.0}, {"weight": 1.0}]
# )
# hg = eg.Hypergraph.from_graph(g)
# _mm = torch.sparse.mm
# est_A = _mm(_mm(g.D_v_neg_1_2, g.A), g.D_v_neg_1_2) + torch.eye(4).to_sparse()
# assert pytest.approx(est_A.to_dense() / 2) == hg.L_HGNN.to_dense()
@pytest.mark.skip(reason="skip")
def test_get_linegraph():
num_v = 5
e_list = [[0, 1], [1, 2, 3], [0, 3, 4]]
e_weight = [1.0, 0.5, 2.0]
v_weight = [0.2, 0.3, 0.4, 0.5, 0.6]
hg = eg.Hypergraph(num_v=num_v, e_list=e_list, e_weight=e_weight)
lg = hg.get_clique_expansion()
assert lg.edges == [[0, 1], [0, 2], [1, 2]]
assert lg.nodes == [0, 1, 2]