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]