33
44
55def find_maximal_2_3_subgraph (og_G ):
6+ """
7+ Finds a maximal subgraph of the input graph where all nodes have degree 2 or 3.
8+ This function iteratively constructs a subgraph by identifying cycles and paths
9+ that can be added while maintaining the degree constraints.
10+
11+ Args:
12+ og_G (nx.Graph): The input graph.
13+
14+ Returns:
15+ nx.Graph: A maximal subgraph of the input graph where all nodes have degree 2 or 3.
16+ """
617 G = og_G .copy ()
718 H = nx .Graph ()
819 H .add_nodes_from (G .nodes ())
920 nodes_to_visit = deque (G .nodes ())
1021
11- # Chemin actuel
22+ # Current path
1223 stack = []
1324 in_stack = set ()
1425
15- # stack[0] est connecté à un noeud de H de degré 2 ?
26+ # Is stack[0] connected to H by a degree 2 node ?
1627 start_connected = False
1728
1829 while True :
@@ -25,7 +36,7 @@ def find_maximal_2_3_subgraph(og_G):
2536 break
2637
2738 if start_node is None :
28- # Plus de noeuds à visiter
39+ # No more nodes to visit
2940 break
3041
3142 stack = [start_node ]
@@ -35,7 +46,7 @@ def find_maximal_2_3_subgraph(og_G):
3546 else :
3647 start_connected = False
3748
38- # Extrémité du chemin actuel
49+ # Extract the extremity of the path
3950 u = stack [- 1 ]
4051 parent = stack [- 2 ] if len (stack ) > 1 else None
4152 nb = list (G .neighbors (u ))
@@ -51,22 +62,21 @@ def find_maximal_2_3_subgraph(og_G):
5162 if len (edges_to_remove ) > 0 :
5263 G .remove_edges_from (edges_to_remove )
5364
54- # Si pas de voisins du tout, ou seul voisin est le parent, ou tous les voisins (sans compter le parent)
55- # ont un degré 3 dans H -> cul de sac
65+ # If no valid neighbors, backtrack
5666 if len (valid ) == 0 :
5767 if parent is not None :
5868 if G .has_edge (parent , u ):
5969 G .remove_edge (parent , u )
6070 in_stack .remove (stack .pop ())
6171
62- # Reset si la stack est vide
72+ # Reset if stack is empty
6373 if len (stack ) == 0 :
6474 start_connected = False
6575 continue
6676
6777 v = valid [0 ]
6878
69- # Si v est dans la stack, alors on a un cycle
79+ # If v is already in stack -> cycle detected
7080 if v in in_stack :
7181 idx = stack .index (v )
7282 cycle_nodes = stack [idx :]
@@ -77,17 +87,16 @@ def find_maximal_2_3_subgraph(og_G):
7787 H .add_edges_from (edges_to_add )
7888 G .remove_edges_from (edges_to_add )
7989
80- # Si idx == 0, cycle formé par tous les sommets de la stack
90+ # Handle cycle cases
8191 if idx == 0 :
8292 stack = []
8393 in_stack = set ()
8494 start_connected = False
85-
8695 else :
8796 stack = stack [: idx + 1 ]
8897 in_stack = set (stack )
89- # Le bout de la pile (v) est connecté à H
90- # Si le début est connecté, alors c'est un chemin valide à ajouter
98+ # The end of the stack (v) is connected to H
99+ # If the start is connected, then it's a valid path to add
91100 if start_connected :
92101 path_edges = []
93102 for i in range (len (stack ) - 1 ):
@@ -97,18 +106,16 @@ def find_maximal_2_3_subgraph(og_G):
97106 stack = []
98107 in_stack = set ()
99108 start_connected = False
100-
101109 else :
102- # La pile est inversée pour essayer d'attacher le début du chemin à H ou un autre cycle
110+ # The stack is reversed to try to connect the start of the path to H or another cycle
103111 stack .reverse ()
104- # v était à la fin de la stack, maintenant au début donc le début est connecté à H
112+ # v was at the end of the stack, now at the beginning so the start of the stack is connected to H
105113 start_connected = True
106114
107115 continue
108116
109117 elif H .degree (v ) == 2 :
110- # v est déjà dans H et à un degré 2 donc la connexion du chemin dans stack est possible à v
111- # Si le début est connecté, alors c'est un chemin valide à ajouter
118+ # Connect path to v if start is connected
112119 if start_connected :
113120 path_edges = []
114121 for i in range (len (stack ) - 1 ):
@@ -119,15 +126,14 @@ def find_maximal_2_3_subgraph(og_G):
119126 stack = []
120127 in_stack = set ()
121128 start_connected = False
122-
123129 else :
124130 stack .append (v )
125131 in_stack .add (v )
126132 stack .reverse ()
127133 start_connected = True
128134 continue
129135
130- # v est degré 0 ou 1 dans H, on peut l'ajouter au chemin
136+ # v has degree 0 or 1 in H, we can add it to the path
131137 else :
132138 stack .append (v )
133139 in_stack .add (v )
@@ -139,6 +145,18 @@ def find_maximal_2_3_subgraph(og_G):
139145
140146
141147def get_critical_linkpoints (G , H ):
148+ """
149+ Identifies critical linkpoints in the subgraph.
150+ A critical linkpoint v is a node of degree 2 in H such that removing
151+ all the nodes of H except v from G creates a cycle including v.
152+
153+ Args:
154+ G (nx.Graph): The original graph.
155+ H (nx.Graph): The maximal subgraph with degree 2 or 3 nodes.
156+
157+ Returns:
158+ set: The set of critical linkpoints.
159+ """
142160 linkpoints = {n for n in H .nodes if H .degree (n ) == 2 }
143161 critical_linkpoints = set ()
144162 G_prime = nx .subgraph (G , set (G .nodes ) - (set (H .nodes )))
@@ -155,11 +173,11 @@ def get_critical_linkpoints(G, H):
155173 if nb in H .nodes :
156174 continue
157175
158- # Vérifier si un cycle comprenant n existe dans G\H avec n étant le sommet du cycle appartenant à H
176+ # Verify if a cycle including n exists in G\H with n being the vertex of the cycle belonging to H
159177 if nb in node_in_component :
160178 comp = node_in_component [nb ]
161179 if comp in visited_components :
162- # Si deux voisins appartiennent à la même composante connexe de G\H, alors il y a un cycle avec n
180+ # If two neighbors belong to the same connected component of G\H, then there is a cycle with n
163181 is_critical = True
164182 break
165183
@@ -172,10 +190,30 @@ def get_critical_linkpoints(G, H):
172190
173191
174192def is_cycle (G ):
193+ """
194+ Checks if the graph is a cycle.
195+
196+ Args:
197+ G (nx.Graph): The input graph.
198+
199+ Returns:
200+ bool: True if the graph is a cycle, False otherwise.
201+ """
175202 return all (G .degree (n ) == 2 for n in G .nodes )
176203
177204
178205def get_set_covering_cycles (H , X , Y ):
206+ """
207+ Finds a set of nodes that cover all cycles in the subgraph excluding critical linkpoints and high-degree nodes.
208+
209+ Args:
210+ H (nx.Graph): The subgraph with degree 2 or 3 nodes.
211+ X: Set of critical linkpoints.
212+ Y: Nodes with degree = 3 in H.
213+
214+ Returns:
215+ set: A set of nodes covering all cycles in the subgraph.
216+ """
179217 sg = nx .subgraph (H , set (H .nodes ) - X - Y )
180218 cover_set = set ()
181219
@@ -188,6 +226,15 @@ def get_set_covering_cycles(H, X, Y):
188226
189227
190228def subG_2_3 (G ):
229+ """
230+ Approximates a FVS for G using Bar-Yehuda's algorithm.
231+
232+ Args:
233+ G (nx.Graph): The input graph.
234+
235+ Returns:
236+ set: An approximated FVS for G.
237+ """
191238 if nx .is_forest (G ):
192239 return set ()
193240
@@ -199,4 +246,13 @@ def subG_2_3(G):
199246
200247
201248def approx_decycling_number_bar_yehuda (G ):
249+ """
250+ Approximates the decycling number of the graph using Bar-Yehuda's algorithm.
251+
252+ Args:
253+ G (nx.Graph): The input graph.
254+
255+ Returns:
256+ int: The approximated decycling number.
257+ """
202258 return len (subG_2_3 (G ))
0 commit comments