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

64 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2024-11-17 23:31 +0100

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

2 

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

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

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

6 build_recycle_list, 

7 choose_other_edge, 

8 rewire_edge, 

9) 

10from abcd_graph.graph.core.constants import BACKGROUND_GRAPH_ID 

11 

12 

13class 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 

96class BackgroundGraph(AbstractCommunity): 

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

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