Skip to content

Commit

Permalink
Refactor core interface (xgi-org#41)
Browse files Browse the repository at this point in the history
* fix: node attributes were not being created correctly

* chore: whitespace changes

* refactor: H[x] now accesses hypergraph attributes

* fix: add a way to access hypergraph attributes. fix: a broken test

* refactor: now use members to access the nodes of an edge

* refactor: use *_attr rather than *_attrs

* refactor: use __getitem__ to access attributes, not members

* docs: update docs

* feat: add H.nodes.memberships, which is the same as H.nodes.members, and add a test for it

* Updates

* Created a base class for the Node/Edge Views and Degree/Edge Size Views.
* Removed the NodeDataView and EdgeDataView.
* All unit tests pass
* Removed all old references to `.node[id]` and `.edge[id]`

* updated list of developers

* update hypergraph getitem method

* Fixed tests

* refactor: use memberships rather than memberships

* fix: members associated with edges, memberships associated with nodes

* fix: specified that incorrect/missing IDs are KeyError exceptions

* docs: added documentation and removed old API references

* fix: added correct handling for calling as a slice

* tests: added tests for missing or slice ids

* BREAKING CHANGE: removed the name attribute and fixed methods and tests to match.

Co-authored-by: nwlandry <[email protected]>
  • Loading branch information
leotrs and nwlandry authored Feb 8, 2022
1 parent 7b08b6f commit 99eb038
Show file tree
Hide file tree
Showing 14 changed files with 319 additions and 1,074 deletions.
10 changes: 10 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ authors:
-
family-names: Torres
given-names: Leo
-
family-names: Iacopini
given-names: Iacopo
-
family-names: Lucas
given-names: Maxime
-
family-names: Petri
given-names: Giovanni

cff-version: "1.1.0"
license: "BSD-3"
message: "If you use this software, please cite it using these metadata."
Expand Down
5 changes: 4 additions & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
XGI is distributed with the 3-clause BSD license.

Copyright (C) 2021, Hypergraph Developers
Copyright (C) 2021, XGI Developers
Nicholas Landry <[email protected]>
Leo Torres <[email protected]>
Iacopo Iacopini <[email protected]>
Maxime Lucas <[email protected]>
Giovanni Petri <[email protected]>
All rights reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
8 changes: 4 additions & 4 deletions tests/classes/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,15 @@ def test_create_empty_copy(edgelist1):

assert E1.shape == (8, 0)
for node in E1.nodes:
assert len(E1.nodes[node]) == 0
assert E1.name == ""
assert len(E1.nodes.memberships(node)) == 0
assert E1._hypergraph == {}

assert E2.shape == (8, 0)
for node in E2.nodes:
assert len(E1.nodes[node]) == 0
assert len(E1.nodes.memberships(node)) == 0
assert E2._hypergraph == {"name": "test", "timestamp": "Nov. 20"}
assert dict(E2.nodes.data()) == attr_dict
for n in H.nodes:
assert H.nodes[n]["name"] == attr_dict[n]["name"]


def test_is_empty():
Expand Down
68 changes: 53 additions & 15 deletions tests/classes/test_hypergraph.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pytest
import xgi
from xgi.exception import XGIError


def test_constructor(edgelist5, dict5, incidence5, dataframe5):
Expand All @@ -21,20 +23,20 @@ def test_constructor(edgelist5, dict5, incidence5, dataframe5):
== list(H_df.edges)
)
assert (
list(H_list.edges[0])
== list(H_dict.edges[0])
== list(H_mat.edges[0])
== list(H_df.edges[0])
list(H_list.edges.members(0))
== list(H_dict.edges.members(0))
== list(H_mat.edges.members(0))
== list(H_df.edges.members(0))
)


def test_name():
def test_hypergraph_attrs():
H = xgi.Hypergraph()
assert H.name == ""
H.name = "test"
assert H.name == "test"
assert H._hypergraph == dict()
with pytest.raises(XGIError):
name = H["name"]
H = xgi.Hypergraph(name="test")
assert H.name == "test"
assert H["name"] == "test"


def test_contains(edgelist1):
Expand All @@ -46,6 +48,12 @@ def test_contains(edgelist1):

assert 0 not in H

def test_string():
H1 = xgi.Hypergraph()
assert str(H1) == "Unnamed Hypergraph with 0 nodes and 0 hyperedges"
H2 = xgi.Hypergraph(name="test")
# H2["name"] = "test"
assert str(H2) == "Hypergraph named test with 0 nodes and 0 hyperedges"

def test_len(edgelist1, edgelist2):
el1 = edgelist1
Expand Down Expand Up @@ -93,7 +101,7 @@ def test_dual(edgelist1, edgelist2, edgelist4):
assert D3.shape == (3, 5)


def test_max_edge_order(edgelist1, edgelist4,edgelist5):
def test_max_edge_order(edgelist1, edgelist4, edgelist5):
H0 = xgi.empty_hypergraph()
H1 = xgi.empty_hypergraph()
H1.add_nodes_from(range(5))
Expand All @@ -103,7 +111,7 @@ def test_max_edge_order(edgelist1, edgelist4,edgelist5):

assert H0.max_edge_order() == None
assert H1.max_edge_order() == 0
assert H2.max_edge_order() == 2
assert H2.max_edge_order() == 2
assert H3.max_edge_order() == 3
assert H4.max_edge_order() == 3

Expand All @@ -112,7 +120,7 @@ def test_is_possible_order(edgelist1):
H1 = xgi.Hypergraph(edgelist1)

assert H1.is_possible_order(-1) == False
assert H1.is_possible_order(0) == False
assert H1.is_possible_order(0) == False
assert H1.is_possible_order(1) == True
assert H1.is_possible_order(2) == True
assert H1.is_possible_order(3) == False
Expand All @@ -122,7 +130,7 @@ def test_singleton_edges(edgelist1, edgelist2):
H1 = xgi.Hypergraph(edgelist1)
H2 = xgi.Hypergraph(edgelist2)

assert H1.singleton_edges() == {1 : [4]}
assert H1.singleton_edges() == {1: [4]}
assert H2.singleton_edges() == {}


Expand All @@ -143,8 +151,8 @@ def test_is_uniform(edgelist1, edgelist6, edgelist7):
H2 = xgi.Hypergraph(edgelist7)
H3 = xgi.empty_hypergraph()

assert H0.is_uniform() == False
assert H1.is_uniform() == 2
assert H0.is_uniform() == False
assert H1.is_uniform() == 2
assert H2.is_uniform() == 2
assert H3.is_uniform() == False

Expand All @@ -155,3 +163,33 @@ def test_isolates(edgelist1):
assert H.isolates() == {4}
H.remove_isolates()
assert 4 not in H


def test_add_node_attr(edgelist1):
H = xgi.Hypergraph(edgelist1)
assert "new_node" not in H
H.add_node("new_node", color="red")
assert "new_node" in H
assert "color" in H.nodes["new_node"]
assert H.nodes["new_node"]["color"] == "red"


def test_hypergraph_attr(edgelist1):
H = xgi.Hypergraph(edgelist1)
with pytest.raises(XGIError):
H["color"]
H["color"] = "red"
assert H["color"] == "red"


def test_members(edgelist1):
H = xgi.Hypergraph(edgelist1)
assert H.nodes.memberships(1) == [0]
assert H.nodes.memberships(2) == [0]
assert H.nodes.memberships(3) == [0]
assert H.nodes.memberships(4) == [1]
assert H.nodes.memberships(6) == [2, 3]
with pytest.raises(XGIError):
H.nodes.memberships(0)
with pytest.raises(XGIError):
H.nodes.memberships(slice(1, 4))
8 changes: 5 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ def edgelist4():
@pytest.fixture
def edgelist5():
return [[0, 1, 2, 3], [4], [5, 6], [6, 7, 8]]



@pytest.fixture
def edgelist6():
return [[0, 1, 2], [1, 2, 3], [2, 3, 4]]



@pytest.fixture
def edgelist7():
return [[0, 1, 2], [1, 2, 3], [2, 3, 4], [4]]
return [[0, 1, 2], [1, 2, 3], [2, 3, 4], [4]]


@pytest.fixture
Expand Down
3 changes: 1 addition & 2 deletions tests/generators/test_nonuniform.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_erdos_renyi_hypergraph():
H = xgi.erdos_renyi_hypergraph(10, 20, 0.0)
assert H.number_of_nodes() == 10
assert H.number_of_edges() == 0

H = xgi.erdos_renyi_hypergraph(10, 20, 1.0)
assert H.number_of_nodes() == 10
assert H.number_of_edges() == 20
Expand All @@ -32,4 +32,3 @@ def test_chung_lu_hypergraph():
k1 = {1: 1, 2: 2}
k2 = {1: 2, 1: 2}
H = xgi.chung_lu_hypergraph(k1, k2)

8 changes: 4 additions & 4 deletions xgi/classes/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def unique_edge_sizes(H, return_counts=False):
list()
The unique edge sizes
"""
return list({len(H.edges[edge]) for edge in H.edges})
return list({len(H.edges.members(edge)) for edge in H.edges})


def frozen(*args, **kwargs):
Expand Down Expand Up @@ -198,7 +198,7 @@ def create_empty_copy(H, with_data=True):
H_copy = H.__class__()
H_copy.add_nodes_from(H.nodes)
if with_data:
xgi.set_node_attributes(H_copy, dict(H.nodes.data()))
xgi.set_node_attributes(H_copy, dict(H._node_attr))
H_copy._hypergraph.update(H._hypergraph)
return H_copy

Expand Down Expand Up @@ -251,7 +251,7 @@ def set_node_attributes(H, values, name=None):
try: # `values` is a dict
for n, v in values.items():
try:
H._node_attr[n][name] = values[n]
H._node_attr[n][name] = v
except KeyError:
pass
except AttributeError: # `values` is a constant
Expand Down Expand Up @@ -362,7 +362,7 @@ def get_edge_attributes(H, name):
get_node_attributes
set_edge_attributes
"""
edge_data = H.edges.data
edge_data = H._edge_attr
return {id: edge_data[edge][name] for edge in edge_data if name in edge_data[edge]}


Expand Down
Loading

0 comments on commit 99eb038

Please sign in to comment.