22
33
44def cleanup (G ):
5- new_G = G .copy ()
65 while True :
7- to_remove = [node for node in new_G .nodes if new_G .degree (node ) <= 1 ]
6+ to_remove = [node for node in G .nodes if G .degree (node ) <= 1 ]
87 if len (to_remove ) == 0 :
98 break
10- new_G .remove_nodes_from (to_remove )
9+ G .remove_nodes_from (to_remove )
1110
12- return new_G
11+ return G
1312
1413
1514def find_semidisjoint_cycle (G ):
16- for cycle in nx .cycle_basis (G ):
17- cnt = 0
18- for node in cycle :
19- if G .degree (node ) != 2 :
20- cnt += 1
21- if cnt > 1 :
22- break
23- if cnt <= 1 :
24- return cycle
15+ degree_2 = {n for n , d in G .degree () if d == 2 }
16+ if len (degree_2 ) == 0 :
17+ return None
18+
19+ sg_d2 = nx .subgraph (G , degree_2 )
20+ visited = set ()
21+
22+ for node in degree_2 :
23+ if node in visited :
24+ continue
25+
26+ component = list (nx .node_connected_component (sg_d2 , node ))
27+ visited .update (component )
28+ sg_comp = nx .subgraph (G , component )
29+
30+ # If in the component the number of edges equals the number of nodes, then it's a cycle
31+ if 0 < len (component ) == sg_comp .number_of_edges ():
32+ return component
33+
34+ # Else, we need to check if it's a path with endpoints connected to a common junction node
35+ endpoints = [n for n in component if sg_comp .degree (n ) <= 1 ]
36+ if len (endpoints ) == 2 :
37+ ep1 , ep2 = endpoints
38+ nb_1 = {n for n in G .neighbors (ep1 ) if n not in component }
39+ nb_2 = {n for n in G .neighbors (ep2 ) if n not in component }
40+ common_junctions = {n for n in nb_1 & nb_2 if G .degree (n ) > 2 }
41+
42+ if len (common_junctions ) > 0 :
43+ return component + [list (common_junctions )[0 ]]
44+
2545 return None
2646
2747
2848def get_fvs (og_G ):
2949 F = set ()
30- i = 0
3150 stack = []
3251 G = og_G .copy ()
3352
3453 while len (G .nodes ) > 0 :
35- i += 1
3654 sd_cycle = find_semidisjoint_cycle (G )
3755 if sd_cycle is not None :
3856 for node in sd_cycle :
39- G .node [node ]["weight" ] = 0.0
57+ G .nodes [node ]["weight" ] = 0.0
4058
4159 else :
4260 gamma = min (1 / (G .degree (node ) - 1 ) for node in G .nodes )
4361 for node in G .nodes :
4462 G .nodes [node ]["weight" ] -= gamma * (G .degree (node ) - 1 )
4563
46- to_remove = [node for node in G .nodes if G .nodes [node ]["weight" ] = = 0.0 ]
64+ to_remove = [node for node in G .nodes if G .nodes [node ]["weight" ] < = 0.0 ]
4765 F .update (to_remove )
4866 G .remove_nodes_from (to_remove )
4967 stack .extend (to_remove )
@@ -52,14 +70,15 @@ def get_fvs(og_G):
5270 while len (stack ) > 0 :
5371 node = stack .pop ()
5472 sg = nx .subgraph (og_G , og_G .nodes - (F - {node }))
55- if nx .is_forest (sg ):
73+ # With this check, it should ensure that each call for nx.is_forest is in O(|V|) time since |E| < |V|
74+ if G .number_of_edges () <= G .number_of_nodes () - 1 and nx .is_forest (sg ):
5675 F .remove (node )
5776
5877 return F
5978
6079
6180def get_decycling_number_2_approx (G ):
62- clean_G = cleanup (G )
81+ clean_G = cleanup (G . copy () )
6382 for node in clean_G .nodes :
6483 clean_G .nodes [node ]["weight" ] = 1.0
6584
0 commit comments