Coverage for src/abcd_graph/graph/core/abcd_objects/community.py: 98%

1 __all__ = ["Community", "BackgroundGraph"]

2

3 from abcd_graph.graph.core.abcd_objects.abstract import AbstractCommunity

4 from abcd_graph.graph.core.abcd_objects.edge import Edge

5 from abcd_graph.graph.core.abcd_objects.utils import (

6 build_recycle_list,

7 choose_other_edge,

8 rewire_edge,

9)

10 from abcd_graph.graph.core.constants import BACKGROUND_GRAPH_ID

11

12

13 class Community(AbstractCommunity):

14 def __init__(

15 self,

16 edges: list[Edge],

17 vertices: list[int],

18 deg_b: dict[int, int],

19 deg_c: dict[int, int],

20 community_id: int,

21 ) -> None:

22 super().__init__(edges, community_id)

23

24 self._vertices = vertices

25 self._deg_b = deg_b

26 self._deg_c = deg_c

27

28 def __eq__(self, other: object) -> bool:

29 if not isinstance(other, AbstractCommunity):

30 return False

31 return self.community_id == other.community_id

32

33 def __hash__(self) -> int:

34 return hash(self.community_id)

35

36 @property

37 def vertices(self) -> list[int]:

38 return self._vertices

39

40 @property

41 def average_degree(self) -> float:

42 return sum(self.degree_sequence.values()) / len(self.vertices)

43

44 @property

45 def degree_sequence(self) -> dict[int, int]:

46 res = {}

47 for vert in self.vertices:

48 res[vert] = self._deg_c[vert] + self._deg_b[vert]

49 return res

50

51 @property

52 def local_deg_c(self) -> dict[int, int]:

53 return {k: v for k, v in self._deg_c.items() if k in self.vertices}

54

55 @property

56 def empirical_xi(self) -> float:

57 return sum(self._deg_b[i] for i in self.vertices) / (

58 sum(self._deg_b[i] for i in self.vertices) + sum(self.local_deg_c.values())

59 )

60

61 def push_to_background(self, edges: list[Edge], deg_b: dict[int, int]) -> None:

62 for edge in edges:

63 if edge.is_loop:

64 for i in range(self.adj_dict[edge]):

65 self.adj_dict[edge] -= 1

66 if self.adj_dict[edge] == 0:

67 del self.adj_dict[edge]

68

69 self._update_degree_sequences(edge, deg_b)

70 else:

71 for i in range(self.adj_dict[edge] - 1):

72 self.adj_dict[edge] -= 1

73

74 self._update_degree_sequences(edge, deg_b)

75

76 def _update_degree_sequences(self, edge: Edge, deg_b: dict[int, int]) -> None:

77 deg_b[edge.v1] += 1

78 deg_b[edge.v2] += 1

79 self._deg_c[edge.v1] -= 1

80 self._deg_c[edge.v2] -= 1

81

82 def rewire_community(self) -> None:

83 while len(self._bad_edges) > 0:

84 for edge in self._bad_edges:

85 other_edge = choose_other_edge(self.adj_dict, edge)

86 rewire_edge(self.adj_dict, edge, other_edge)

87

88 new_bad_edges = build_recycle_list(self.adj_dict)

89 if len(new_bad_edges) >= len(self._bad_edges):

90 self.push_to_background(new_bad_edges, self._deg_b)

91 return

92 else:

93 self._bad_edges = new_bad_edges

94

95

96 class BackgroundGraph(AbstractCommunity):

97 def __init__(self, edges: list[Edge]) -> None:

98 super().__init__(edges, community_id=-BACKGROUND_GRAPH_ID)