# HG changeset patch
# User Nathann Cohen <nathann.cohen@gmail.com>
# Date 1365590279 7200
# Node ID 548ba75f6331c3f70d29f95d8ba95fb3b05d2a09
# Parent 28801f75075decdbdf1395376977cdfcdfc59641
implements GenericGraph.feedback_vertex_set for undirected graphs
diff git a/sage/graphs/digraph.py b/sage/graphs/digraph.py
a

b


85  85  :delim:  
86  86  
87  87  :meth:`~DiGraph.feedback_edge_set`  Computes the minimum feedback edge (arc) set of a digraph 
88   :meth:`~DiGraph.feedback_vertex_set`  Computes the minimum feedback vertex set of a digraph. 
89   
90  88  
91  89  Methods 
92  90   
diff git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
a

b


280  280  
281  281  :meth:`~GenericGraph.steiner_tree`  Returns a tree of minimum weight connecting the given set of vertices. 
282  282  :meth:`~GenericGraph.edge_disjoint_spanning_trees`  Returns the desired number of edgedisjoint spanning trees/arborescences. 
 283  :meth:`~GenericGraph.feedback_vertex_set`  Computes the minimum feedback vertex set of a (di)graph. 
283  284  :meth:`~GenericGraph.multiway_cut`  Returns a minimum edge multiway cut 
284  285  :meth:`~GenericGraph.max_cut`  Returns a maximum edge cut of the graph. 
285  286  :meth:`~GenericGraph.longest_path`  Returns a longest path of ``self``. 
… 
… 

6045  6046  
6046  6047  def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constraint_generation = True): 
6047  6048  r""" 
6048   Computes the minimum feedback vertex set of a digraph. 
6049   
6050   The minimum feedback vertex set of a digraph is a set of vertices 
6051   that intersect all the circuits of the digraph. 
6052   Equivalently, a minimum feedback vertex set of a DiGraph is a set 
6053   `S` of vertices such that the digraph `GS` is acyclic. For more 
6054   information, see the 
6055   `Wikipedia article on feedback vertex sets 
6056   <http://en.wikipedia.org/wiki/Feedback_vertex_set>`_. 
 6049  Computes the minimum feedback vertex set of a (di)graph. 
 6050  
 6051  The minimum feedback vertex set of a (di)graph is a set of vertices that 
 6052  intersect all of its cycles. Equivalently, a minimum feedback vertex 
 6053  set of a (di)graph is a set `S` of vertices such that the digraph `GS` 
 6054  is acyclic. For more information, see the `Wikipedia article on feedback 
 6055  vertex sets <http://en.wikipedia.org/wiki/Feedback_vertex_set>`_. 
6057  6056  
6058  6057  INPUT: 
6059  6058  
… 
… 

6080  6079  generation when solving the Mixed Integer Linear Program (default: 
6081  6080  ``True``). 
6082  6081  
6083   ALGORITHM: 
6084   
6085   This problem is solved using Linear Programming, which certainly is not 
6086   the best way and will have to be replaced by a better algorithm. The 
6087   program to be solved is the following: 
 6082  ALGORITHMS: 
 6083  
 6084  (Constraints generation) 
 6085  
 6086  When the parameter ``constraint_generation`` is enabled (default) the 
 6087  following MILP formulation is used to solve the problem:: 
 6088  
 6089  .. MATH:: 
 6090  
 6091  \mbox{Minimize : }&\sum_{v\in G} b_{v}\\ 
 6092  \mbox{Such that : }&\\ 
 6093  &\forall C\text{ circuits }\subseteq G, \sum_{v\in C}b_{v}\geq 1\\ 
 6094  
 6095  As the number of circuits contained in a graph is exponential, this LP 
 6096  is solved through constraint generation. This means that the solver is 
 6097  sequentially asked to solved the problem, knowing only a portion of the 
 6098  circuits contained in `G`, each time adding to the list of its 
 6099  constraints the circuit which its last answer had left intact. 
 6100  
 6101  (Another formulation based on an ordering of the vertices) 
 6102  
 6103  When the graph is directed, a second (and very slow) formulation is 
 6104  available, which should only be used to check the result of the first 
 6105  implementation in case of doubt. 
6088  6106  
6089  6107  .. MATH:: 
6090  6108  
… 
… 

6104  6122  The number of vertices removed is then minimized, which is the 
6105  6123  objective. 
6106  6124  
6107   (Constraint Generation) 
6108   
6109   If the parameter ``constraint_generation`` is enabled, a more efficient 
6110   formulation is used : 
6111   
6112   .. MATH:: 
6113   
6114   \mbox{Minimize : }&\sum_{v\in G} b_{v}\\ 
6115   \mbox{Such that : }&\\ 
6116   &\forall C\text{ circuits }\subseteq G, \sum_{v\in C}b_{v}\geq 1\\ 
6117   
6118   As the number of circuits contained in a graph is exponential, this LP 
6119   is solved through constraint generation. This means that the solver is 
6120   sequentially asked to solved the problem, knowing only a portion of the 
6121   circuits contained in `G`, each time adding to the list of its 
6122   constraints the circuit which its last answer had left intact. 
6123   
6124  6125  EXAMPLES: 
6125  6126  
 6127  The necessary example:: 
 6128  
 6129  sage: g = graphs.PetersenGraph() 
 6130  sage: g.feedback_vertex_set() 
 6131  [1, 3, 5] 
 6132  
6126  6133  In a digraph built from a graph, any edge is replaced by arcs going in 
6127  6134  the two opposite directions, thus creating a cycle of length two. 
6128  6135  Hence, to remove all the cycles from the graph, each edge must see one 
… 
… 

6156  6163  ... constraint_generation = False) 
6157  6164  sage: x == y 
6158  6165  True 
 6166  
 6167  Bad algorithm:: 
 6168  
 6169  sage: g = graphs.PetersenGraph() 
 6170  sage: g.feedback_vertex_set(constraint_generation = False) 
 6171  Traceback (most recent call last): 
 6172  ... 
 6173  ValueError: The only implementation available for undirected graphs is with constraint_generation set to True. 
6159  6174  """ 
6160   
6161   # It would be a pity to start a LP if the digraph is already acyclic 
6162   if self.is_directed_acyclic(): 
 6175  if not constraint_generation and not self.is_directed(): 
 6176  raise ValueError("The only implementation available for "+ 
 6177  "undirected graphs is with constraint_generation " 
 6178  "set to True.") 
 6179  
 6180  # It would be a pity to start a LP if the graph is already acyclic 
 6181  if ((not self.is_directed() and self.is_forest()) or 
 6182  ( self.is_directed() and self.is_directed_acyclic())): 
6163  6183  if value_only: 
6164  6184  return 0 
6165  6185  return [] 
… 
… 

6185  6205  
6186  6206  # For as long as we do not break because the digraph is 
6187  6207  # acyclic.... 
6188   while (1): 
 6208  while True: 
6189  6209  
6190  6210  # Building the graph without the edges removed by the LP 
6191  6211  h = self.subgraph(vertices = 
6192  6212  [v for v in self if p.get_values(b[v]) < .5]) 
6193  6213  
6194   # Is the digraph acyclic ? 
6195   isok, certificate = h.is_directed_acyclic(certificate = True) 
 6214  # Is the graph acyclic ? 
 6215  if self.is_directed(): 
 6216  isok, certificate = h.is_directed_acyclic(certificate = True) 
 6217  else: 
 6218  isok, certificate = h.is_forest(certificate = True) 
6196  6219  
6197  6220  # If so, we are done ! 
6198  6221  if isok: 
… 
… 

6213  6236  
6214  6237  else: 
6215  6238  
6216   # listing the edges contained in the MFAS 
 6239  # listing the edges contained in the MFVS 
6217  6240  return [v for v in self if p.get_values(b[v]) > .5] 
6218  6241  
6219   
6220  6242  else: 
6221  6243  
6222  6244  ###################################### 
… 
… 

6263  6285  &\forall x\in G, b_x\mbox{ is a binary variable} 
6264  6286  
6265  6287  INPUT: 
6266   
 6288  
6267  6289   ``x``  Source vertex 
6268  6290  
6269  6291   ``y``  Sink vertex 