Skip to content

Commit

Permalink
made exact assortativity reproducible (#526)
Browse files Browse the repository at this point in the history
  • Loading branch information
nwlandry authored Apr 2, 2024
1 parent 732bd85 commit 369a10a
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 20 deletions.
13 changes: 13 additions & 0 deletions tests/algorithms/test_assortativity.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ def test_degree_assortativity(edgelist1, edgelist5):
assert -1 <= xgi.degree_assortativity(H1, kind="top-2", exact=True) <= 1
assert -1 <= xgi.degree_assortativity(H1, kind="top-bottom", exact=True) <= 1

# check that exact returns the same
rho1 = xgi.degree_assortativity(H1, kind="uniform", exact=True)
rho2 = xgi.degree_assortativity(H1, kind="uniform", exact=True)
assert rho1 == rho2

rho1 = xgi.degree_assortativity(H1, kind="top-2", exact=True)
rho2 = xgi.degree_assortativity(H1, kind="top-2", exact=True)
assert rho1 == rho2

rho1 = xgi.degree_assortativity(H1, kind="top-bottom", exact=True)
rho2 = xgi.degree_assortativity(H1, kind="top-bottom", exact=True)
assert rho1 == rho2

# test empty
H = xgi.Hypergraph()
with pytest.raises(XGIError):
Expand Down
55 changes: 35 additions & 20 deletions xgi/algorithms/assortativity.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Algorithms for finding the degree assortativity of a hypergraph."""

import random
from itertools import combinations
from itertools import combinations, permutations

import numpy as np

Expand Down Expand Up @@ -54,15 +54,12 @@ def dynamical_assortativity(H):
raise XGIError("No singleton edges!")

d = H.nodes.degree
degs = d.asdict()
members = H.edges.members(dtype=dict)
k = d.asdict()
k1 = d.mean()
k2 = d.moment(2)
kk1 = np.mean(
[
degs[n1] * degs[n2]
for e in H.edges
for n1, n2 in combinations(H.edges.members(e), 2)
]
[k[n1] * k[n2] for e in H.edges for n1, n2 in combinations(members[e], 2)]
)

return kk1 * k1**2 / k2**2 - 1
Expand Down Expand Up @@ -112,17 +109,39 @@ def degree_assortativity(H, kind="uniform", exact=False, num_samples=1000):
elif H.num_edges == 0:
raise XGIError("Hypergraph must contain edges!")

degs = H.degree()
k = H.degree()
members = H.edges.members(dtype=dict)
if exact:
k1k2 = [
_choose_degrees(H.edges.members(e), degs, kind)
for e in H.edges
if len(H.edges.members(e)) > 1
]
if kind == "uniform":
k1k2 = [
[k[n1], k[n2]]
for e in H.edges
for n1, n2 in permutations(members[e], 2)
if n1 != n2 and len(members[e]) > 1
# permutations is so that k1 and k2 have the same variance
]
elif kind == "top-2":
k1k2 = [
d
for e in H.edges
if len(members[e]) > 1
for d in permutations(_choose_degrees(members[e], k, "top-2"), 2)
# permutations is so that k1 and k2 have the same variance
]
elif kind == "top-bottom":
k1k2 = [
d
for e in H.edges
if len(members[e]) > 1
for d in permutations(_choose_degrees(members[e], k, "top-bottom"), 2)
# permutations is so that k1 and k2 have the same variance
]
else:
edges = [e for e in H.edges if len(H.edges.members(e)) > 1]
k1k2 = [
_choose_degrees(H.edges.members(random.choice(edges)), degs, kind)
np.random.permutation(
_choose_degrees(members[random.choice(edges)], k, kind)
)
for _ in range(num_samples)
]

Expand Down Expand Up @@ -176,15 +195,11 @@ def _choose_degrees(e, k, kind="uniform"):
return (k[e[i]], k[e[j]])

elif kind == "top-2":
degs = sorted([k[i] for i in e])[-2:]
random.shuffle(degs)
return degs
return sorted([k[i] for i in e])[-2:]

elif kind == "top-bottom":
# this selects the largest and smallest degrees in one line
degs = sorted([k[i] for i in e])[:: len(e) - 1]
random.shuffle(degs)
return degs
return sorted([k[i] for i in e])[:: len(e) - 1]

else:
raise XGIError("Invalid choice function!")
Expand Down

0 comments on commit 369a10a

Please sign in to comment.