Skip to content

Commit

Permalink
Add unit tests (#153)
Browse files Browse the repository at this point in the history
* tests: add tests for function.py
* tests: added tests for convert.py
* tests: added tests for the ring lattice generator.
* added RTD pages for the drawing module
  • Loading branch information
nwlandry authored Aug 30, 2022
1 parent 30bc50a commit 14ee956
Show file tree
Hide file tree
Showing 25 changed files with 262 additions and 62 deletions.
11 changes: 11 additions & 0 deletions docs/source/api/drawing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
###############
drawing package
###############

.. rubric:: Modules

.. autosummary::
:toctree: drawing

~xgi.drawing.layout
~xgi.drawing.xgi_pylab
13 changes: 13 additions & 0 deletions docs/source/api/drawing/xgi.drawing.layout.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
xgi.drawing.layout
==================

.. currentmodule:: xgi.drawing.layout

.. automodule:: xgi.drawing.layout

.. rubric:: Functions

.. autofunction:: random_layout
.. autofunction:: pairwise_spring_layout
.. autofunction:: barycenter_spring_layout
.. autofunction:: weighted_barycenter_spring_layout
10 changes: 10 additions & 0 deletions docs/source/api/drawing/xgi.drawing.xgi_pylab.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
xgi.drawing.xgi_pylab
=====================

.. currentmodule:: xgi.drawing.xgi_pylab

.. automodule:: xgi.drawing.xgi_pylab

.. rubric:: Functions

.. autofunction:: draw
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
Generative Models <api/generators.rst>
Linear Algebra <api/linalg.rst>
Read/Write <api/readwrite.rst>
Drawing <api/drawing.rst>
Converting to and from other data formats <api/convert.rst>
Utilities <api/utils.rst>

Expand Down
5 changes: 0 additions & 5 deletions docs/source/tutorials/classes.rst

This file was deleted.

3 changes: 0 additions & 3 deletions docs/source/tutorials/examples.rst

This file was deleted.

3 changes: 0 additions & 3 deletions docs/source/tutorials/quickstart.rst

This file was deleted.

48 changes: 44 additions & 4 deletions tests/classes/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ def test_set_node_attributes(edgelist1):
with pytest.raises(XGIError):
xgi.set_node_attributes(H4, 2)

with pytest.warns(Warning):
xgi.set_node_attributes(H4, {"test": "blue"}, "color")

with pytest.warns(Warning):
xgi.set_node_attributes(H4, {"test": {"blue": "color"}})


def test_get_node_attributes(edgelist1):
H1 = xgi.Hypergraph(edgelist1)
Expand Down Expand Up @@ -239,19 +245,25 @@ def test_set_edge_attributes(edgelist1):
assert H1.edges[e]["weight"] == attr_dict1[e]["weight"]

H2 = xgi.Hypergraph(edgelist1)
xgi.set_node_attributes(H2, "blue", name="color")
xgi.set_edge_attributes(H2, "blue", name="color")

for n in H2.nodes:
assert H2.nodes[n]["color"] == "blue"
for e in H2.edges:
assert H2.edges[e]["color"] == "blue"

H3 = xgi.Hypergraph(edgelist1)

with pytest.raises(XGIError):
with pytest.warns(Warning), pytest.raises(XGIError):
xgi.set_node_attributes(H3, attr_dict2)

with pytest.raises(XGIError):
xgi.set_edge_attributes(H3, 2)

with pytest.warns(Warning):
xgi.set_edge_attributes(H3, {"test": 2}, "weight")

with pytest.warns(Warning):
xgi.set_edge_attributes(H3, {"test": {2: "weight"}})


def test_get_edge_attributes(edgelist1):
H1 = xgi.Hypergraph(edgelist1)
Expand Down Expand Up @@ -327,3 +339,31 @@ def test_convert_labels_to_integers(hypergraph1, hypergraph2):

assert H3.nodes[0]["old_ids"] == "a"
assert H3.edges[0]["old_ids"] == "e1"


def test_maximal_simplices(edgelist5, edgelist8):
S1 = xgi.SimplicialComplex(edgelist5)
S2 = xgi.SimplicialComplex(edgelist8)

m1 = xgi.maximal_simplices(S1)
m2 = xgi.maximal_simplices(S2)

simp1 = S1.edges(m1).members()
simp2 = S2.edges(m2).members()

assert len(m1) == 4
assert {0, 1, 2, 3} in simp1
assert {4} in simp1
assert {5, 6} in simp1
assert {6, 7, 8} in simp1

assert len(m2) == 5
assert {0, 1, 2, 3, 4} in simp2
assert {2, 4, 5} in simp2
assert {1, 3, 5} in simp2
assert {1, 6} in simp2
assert {0, 6} in simp2

H = xgi.Hypergraph(edgelist5)
with pytest.raises(XGIError):
xgi.maximal_simplices(H)
8 changes: 7 additions & 1 deletion tests/classes/test_hypergraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ def test_constructor(edgelist5, dict5, incidence5, dataframe5):
== list(H_hg.edges.members(0))
)

with pytest.raises(XGIError):
xgi.Hypergraph(1)


def test_hypergraph_attrs():
H = xgi.Hypergraph()
Expand Down Expand Up @@ -196,7 +199,10 @@ def test_add_edges_from_iterable_of_members():
assert list(H.edges.members()) == edges

H1 = xgi.Hypergraph(edges)
H = xgi.Hypergraph(H1.edges)
with pytest.raises(XGIError):
xgi.Hypergraph(H1.edges)

H = xgi.Hypergraph()
H.add_edges_from(edges)
assert list(H.edges.members()) == edges

Expand Down
3 changes: 3 additions & 0 deletions tests/classes/test_simplicialcomplex.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ def test_constructor(edgelist5, dict5, incidence5, dataframe5):
assert list(S_list.edges) == list(S_df.edges)
assert list(S_list.edges.members(0)) == list(S_df.edges.members(0))

with pytest.raises(XGIError):
xgi.SimplicialComplex(1)


def test_add_simplex():
S = xgi.SimplicialComplex()
Expand Down
4 changes: 3 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ def dataframe5():
[7, 3],
[8, 3],
]
return pd.DataFrame(data)
df = pd.DataFrame(data)
df.columns = ["col1", "col2"]
return df


@pytest.fixture
Expand Down
27 changes: 27 additions & 0 deletions tests/generators/test_classic.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,33 @@ def test_flag_complex():
assert S.edges.members() == simplices


def test_ring_lattice():
H = xgi.ring_lattice(5, 2, 2, 0)
assert H.num_nodes == 5
assert H.num_edges == 5
assert xgi.unique_edge_sizes(H) == [2]

H = xgi.ring_lattice(5, 3, 4, 1)
edges = H.edges.members()
for i in range(H.num_edges - 1):
assert len(set(edges[i]).intersection(set(edges[i + 1]))) == 2 # d-l
assert xgi.unique_edge_sizes(H) == [3]

# k < 2 test
with pytest.warns(Warning):
H = xgi.ring_lattice(5, 2, 1, 0)
assert H.num_nodes == 5
assert H.num_edges == 0

# k % 2 != 0 test
with pytest.warns(Warning):
xgi.ring_lattice(5, 2, 3, 0)

# k < 0 test
with pytest.raises(XGIError):
xgi.ring_lattice(5, 2, -1, 0)


def test_sunflower():
with pytest.raises(XGIError):
H = xgi.sunflower(3, 4, 2)
Expand Down
66 changes: 66 additions & 0 deletions tests/test_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,72 @@
from xgi.exception import XGIError


def test_convert_empty_hypergraph():
H = xgi.convert_to_hypergraph(None)
assert H.num_nodes == 0
assert H.num_edges == 0


def test_convert_empty_simplicial_complex():
S = xgi.convert_to_simplicial_complex(None)
assert S.num_nodes == 0
assert S.num_edges == 0


def test_convert_to_graph(edgelist2, edgelist5):
H1 = xgi.Hypergraph(edgelist2)
H2 = xgi.Hypergraph(edgelist5)

G1 = xgi.convert_to_graph(H1)
assert set(G1.nodes) == {1, 2, 3, 4, 5, 6}
assert list(G1.edges) == [(1, 2), (3, 4), (4, 5), (4, 6), (5, 6)]

G2 = xgi.convert_to_graph(H2)
assert set(G2.nodes) == {0, 1, 2, 3, 4, 5, 6, 7, 8}
assert list(G2.edges) == [
(0, 1),
(0, 2),
(0, 3),
(1, 2),
(1, 3),
(2, 3),
(5, 6),
(6, 7),
(6, 8),
(7, 8),
]


def test_to_hyperedge_list(edgelist1):
H = xgi.Hypergraph(edgelist1)
assert xgi.to_hyperedge_list(H) == edgelist1


def test_to_hyperedge_dict(edgelist1):
H = xgi.Hypergraph(edgelist1)
assert xgi.to_hyperedge_dict(H) == {i: d for i, d in enumerate(edgelist1)}


def test_from_bipartite_pandas_dataframe(dataframe5):

H1 = xgi.from_bipartite_pandas_dataframe(
dataframe5, node_column="col2", edge_column="col1"
)
H2 = xgi.from_bipartite_pandas_dataframe(dataframe5, node_column=1, edge_column=0)

assert H1.edges.members() == H2.edges.members()

with pytest.raises(XGIError):
xgi.from_bipartite_pandas_dataframe(
dataframe5, node_column="test1", edge_column=1
)

with pytest.raises(XGIError):
xgi.from_bipartite_pandas_dataframe(
dataframe5, node_column=0, edge_column="test2"
)


def test_to_bipartite_graph(edgelist1, edgelist3, edgelist4):
H1 = xgi.Hypergraph(edgelist1)
H2 = xgi.Hypergraph(edgelist3)
Expand Down
2 changes: 1 addition & 1 deletion tutorials/quickstart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@
" * flag complex\n",
" * uniform configuration model\n",
" * sunflower\n",
" * lattice\n",
" * ring lattice\n",
" * star clique\n",
"* subhypergraph\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion xgi/algorithms/centrality.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from numpy.linalg import norm
from scipy.sparse.linalg import eigsh

from ..classes import is_uniform, convert_labels_to_integers
from ..classes import convert_labels_to_integers, is_uniform
from ..exception import XGIError
from ..linalg import clique_motif_matrix, incidence_matrix

Expand Down
33 changes: 17 additions & 16 deletions xgi/classes/function.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Functional interface to hypergraph methods and assorted utilities."""

from collections import Counter
from warnings import warn

from ..exception import XGIError
from ..exception import IDNotFound, XGIError
from .hypergraph import Hypergraph

__all__ = [
Expand Down Expand Up @@ -424,22 +425,22 @@ def set_node_attributes(H, values, name=None):
"""
# Set node attributes based on type of `values`
if name is not None: # `values` must not be a dict of dict
try: # `values` is a dict
if isinstance(values, dict): # `values` is a dict
for n, v in values.items():
try:
H._node_attr[n][name] = v
except KeyError:
pass
except AttributeError: # `values` is a constant
except IDNotFound:
warn(f"Node {n} does not exist!")
else: # `values` is a constant
for n in H:
H._node_attr[n][name] = values
else: # `values` must be dict of dict
try:
for n, d in values.items():
try:
H._node_attr[n].update(d)
except KeyError:
pass
except IDNotFound:
warn(f"Node {n} does not exist!")
except (TypeError, ValueError, AttributeError):
raise XGIError("Must pass a dictionary of dictionaries")

Expand Down Expand Up @@ -507,22 +508,22 @@ def set_edge_attributes(H, values, name=None):
if name is not None:
# `values` does not contain attribute names
try:
for id, value in values.items():
for e, value in values.items():
try:
H._edge_attr[id][name] = value
except KeyError:
pass
except IDNotFound:
warn(f"Edge {e} does not exist!")
except AttributeError:
# treat `values` as a constant
for id in H.edges:
H._edge_attr[id][name] = values
for e in H.edges:
H._edge_attr[e][name] = values
else:
try:
for id, d in values.items():
for e, d in values.items():
try:
H._edge_attr[id].update(d)
except KeyError:
pass
H._edge_attr[e].update(d)
except IDNotFound:
warn(f"Edge {e} does not exist!")
except AttributeError:
raise XGIError(
"name property has not been set and a dict-of-dicts has not been provided."
Expand Down
2 changes: 0 additions & 2 deletions xgi/classes/hypergraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from itertools import count
from warnings import warn

import numpy as np

from ..exception import IDNotFound, XGIError
from .reportviews import EdgeView, NodeView

Expand Down
2 changes: 1 addition & 1 deletion xgi/classes/reportviews.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"""

from collections.abc import Mapping, Set
from collections import Counter, defaultdict
from collections.abc import Mapping, Set

from ..exception import IDNotFound, XGIError
from ..stats import EdgeStatDispatcher, NodeStatDispatcher
Expand Down
Loading

0 comments on commit 14ee956

Please sign in to comment.