def get_fbvs(self, graph: Graph): if is_acyclic(graph): return set() # remove all nodes of degree 0 or 1 as they can't be part of any cycles remove_node_deg_01(graph) nodes = graph.nodes() for L in range(0, len(nodes) + 1): for subset in itertools.combinations(nodes, L): # make an induced subgraph with the current node subset removed new_graph = graph_minus(graph, subset) if is_acyclic(new_graph): return subset return set()
def get_fbvs(self, graph: Graph): if is_acyclic(graph): return set() # remove all nodes of degree 0 or 1 as they can't be part of any cycles remove_node_deg_01(graph) # reset the cache dict self.cacheDict = {} return self._get_fbvs(graph)
def get_fbvs(self, graph: Graph): if is_acyclic(graph): return set() # remove all nodes of degree 0 or 1 as they can't be part of any cycles remove_node_deg_01(graph) # get the set of nodes that is in at least one cycle cycles = cycle_basis(graph) nodes_in_cycles = set([item for sublist in cycles for item in sublist]) for L in range(0, len(nodes_in_cycles) + 1): for subset in itertools.combinations(nodes_in_cycles, L): # make an induced subgraph with the current node subset removed new_graph = graph_minus(graph, subset) if is_acyclic(new_graph): return subset return set()
def get_fbvs(self, graph: Graph): if is_acyclic(graph): return set() if type(graph) is not MultiGraph: graph = MultiGraph(graph) for i in range(1, graph.number_of_nodes()): result = self.get_fbvs_max_size(graph, i) if result is not None: return result # in the worst case, result is n-2 nodes
def fvs_disjoint(self, g: MultiGraph, w: set, k: int) -> set: """ Given an undirected graph G and a fbvs W in G of size at least (k + 1), is it possible to construct a fbvs X of size at most k using only the nodes of G - W? :return: The set X, or `None` if it's not possible to construct X """ # If G[W] isn't a forest, then a solution X not using W can't remove W's cycles. if not is_acyclic(g.subgraph(w)): return None # Apply reductions exhaustively. k, soln_redux = self.apply_reductions(g, w, k) # If k becomes negative, it indicates that the reductions included # more than k nodes. In other word, reduction 2 shows that there are more than k nodes # in G - W that will create cycle in W. Hence, no solution of size <= k exists. if k < 0: return None # From now onwards we assume that k >= 0 # If G has been reduced to nothing and k is >= 0 then the solution generated by the reductions # is already optimal. if len(g) == 0: return soln_redux # Recall that H is a forest as W is a feedback vertex set. Thus H has a node x of degree at most 1. # Find an x in H of degree at most 1. h = graph_minus(g, w) x = None for v in h.nodes(): if h.degree(v) <= 1: x = v break assert x is not None, "There must be at least one node x of degree at most 1" # Branch on (G - {x}, W, k−1) and (G, W ∪ {x}, k) # G is copied in the left branch (as it is modified), but passed directly in the right. soln_left = self.fvs_disjoint(graph_minus(g, {x}), w, k - 1) if soln_left is not None: return soln_redux.union(soln_left).union({x}) soln_right = self.fvs_disjoint(g, w.union({x}), k) if soln_right is not None: return soln_redux.union(soln_right) return None
def reduction2(self, g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool): """ If there exists a node v in H such that G[W ∪ {v}] contains a cycle, then include v in the solution, delete v and decrease the parameter by 1. That is, the new instance is (G - {v}, W, k - 1). If v introduces a cycle, it must be part of X as none of the vertices in W will be available to neutralise this cycle. """ for v in h.nodes(): # Check if G[W ∪ {v}] contains a cycle. if not is_acyclic(g.subgraph(w.union({v}))): g.remove_node(v) h.remove_nodes_from([v]) return k - 1, v, True return k, None, False