# Ticket #14434: trac_14434-ALL.patch

File trac_14434-ALL.patch, 22.8 KB (added by ncohen, 6 years ago)
• ## sage/graphs/digraph.py

# HG changeset patch
# User Nathann Cohen <nathann.cohen@gmail.com>
# Date 1365581838 -7200
# Node ID 0dce8ee365602d8f7af7653d95b6d765ee7117c3
# Parent  d0b9d29d21159047139b49edc8beaf6641361e3d
Move digraph.feedback_vertex_set to generic_digraph.py
* * *
implements GenericGraph.feedback_vertex_set for undirected graphs
* * *
Implement feedback_vertex_set for graphs -- broken doctest
* * *
trac 14334: clean documentation for feedback_vertex_set
* * *
Implement feedback_vertex_set for graphs -- a bug in is_tree

diff --git a/sage/graphs/digraph.py b/sage/graphs/digraph.py
 a :delim: | :meth:~DiGraph.feedback_edge_set | Computes the minimum feedback edge (arc) set of a digraph :meth:~DiGraph.feedback_vertex_set | Computes the minimum feedback vertex set of a digraph. Methods ------- return [(u,v) for (u,v) in self.edges(labels=None) if b_sol[(u,v)]==1] def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constraint_generation = True): r""" Computes the minimum feedback vertex set of a digraph. The minimum feedback vertex set of a digraph is a set of vertices that intersect all the circuits of the digraph. Equivalently, a minimum feedback vertex set of a DiGraph is a set S of vertices such that the digraph G-S is acyclic. For more information, see the Wikipedia article on feedback vertex sets _. INPUT: - value_only -- boolean (default: False) - When set to True, only the minimum cardinal of a minimum vertex set is returned. - When set to False, the Set of vertices of a minimal feedback vertex set is returned. - solver -- (default: None) Specify a Linear Program (LP) solver to be used. If set to None, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:solve  of the class :class:MixedIntegerLinearProgram . - verbose -- integer (default: 0). Sets the level of verbosity. Set to 0 by default, which means quiet. - constraint_generation (boolean) -- whether to use constraint generation when solving the Mixed Integer Linear Program (default: True). ALGORITHM: This problem is solved using Linear Programming, which certainly is not the best way and will have to be replaced by a better algorithm.  The program to be solved is the following: .. MATH:: \mbox{Minimize : }&\sum_{v\in G} b_v\\ \mbox{Such that : }&\\ &\forall (u,v)\in G, d_u-d_v+nb_u+nb_v\geq 0\\ &\forall u\in G, 0\leq d_u\leq |G|\\ A brief explanation: An acyclic digraph can be seen as a poset, and every poset has a linear extension. This means that in any acyclic digraph the vertices can be ordered with a total order < in such a way that if (u,v)\in G, then u .5] else: ###################################### # Ordering-based MILP Implementation # ###################################### p = MixedIntegerLinearProgram(maximization=False, solver=solver) b = p.new_variable(binary = True) d = p.new_variable(integer = True) n = self.order() # The removed vertices cover all the back arcs ( third condition ) for (u,v) in self.edges(labels=None): p.add_constraint(d[u]-d[v]+n*(b[u]+b[v]),min=1) for u in self: p.add_constraint(d[u],max=n) p.set_objective(p.sum([b[v] for v in self])) if value_only: return Integer(round(p.solve(objective_only=True, log=verbose))) else: p.solve(log=verbose) b_sol=p.get_values(b) return [v for v in self if b_sol[v]==1] ### Construction def reverse(self):
• ## sage/graphs/generic_graph.py

diff --git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
 a :meth:~GenericGraph.steiner_tree | Returns a tree of minimum weight connecting the given set of vertices. :meth:~GenericGraph.edge_disjoint_spanning_trees | Returns the desired number of edge-disjoint spanning trees/arborescences. :meth:~GenericGraph.feedback_vertex_set | Computes the minimum feedback vertex set of a (di)graph. :meth:~GenericGraph.multiway_cut | Returns a minimum edge multiway cut :meth:~GenericGraph.max_cut | Returns a maximum edge cut of the graph. :meth:~GenericGraph.longest_path | Returns a longest path of self. height = p.new_variable(dim = 2) # cut[e] represents whether e is in the cut cut = p.new_variable() cut = p.new_variable(binary = True) # Reorder R = lambda x,y : (x,y) if x .5, self.edge_iterator()) else: return filter(lambda (u,v,l) : cut[R(u,v)] > .5, self.edge_iterator()) return filter(lambda (u,v,l) : cut[u,v] == 1, self.edge_iterator()) else: return filter(lambda (u,v,l) : cut[R(u,v)] ==1, self.edge_iterator()) def max_cut(self, value_only=True, use_edge_labels=False, vertices=False, solver=None, verbose=0): vertex_used = p.get_values(vertex_used) if self._directed: g = self.subgraph( vertices=(v for v in self if vertex_used[v] >= 0.5), vertices=(v for v in self if vertex_used[v] == 1), edges=((u,v,l) for u, v, l in self.edges() if edge_used[(u,v)] >= 0.5)) if edge_used[(u,v)] == 1)) else: g = self.subgraph( vertices=(v for v in self if vertex_used[v] >= 0.5), vertices=(v for v in self if vertex_used[v] == 1), edges=((u,v,l) for u, v, l in self.edges() if f_edge_used(u,v) >= 0.5)) if f_edge_used(u,v) == 1)) if use_edge_labels: return sum(map(weight, g.edge_labels())), g else: # We build the DiGraph representing the current solution h = DiGraph() for u,v,l in g.edges(): if p.get_values(b[u][v]) > .5: if p.get_values(b[u][v]) == 1: h.add_edge(u,v,l) # If there is only one circuit, we are done ! # We build the DiGraph representing the current solution h = Graph() for u,v,l in g.edges(): if p.get_values(B(u,v)) > .5: if p.get_values(B(u,v)) == 1: h.add_edge(u,v,l) # If there is only one circuit, we are done ! else: raise ValueError("algorithm (%s) should be 'tsp' or 'backtrack'."%(algorithm)) def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constraint_generation = True): r""" Computes the minimum feedback vertex set of a (di)graph. The minimum feedback vertex set of a (di)graph is a set of vertices that intersect all of its cycles.  Equivalently, a minimum feedback vertex set of a (di)graph is a set S of vertices such that the digraph G-S is acyclic. For more information, see :wikipedia:Feedback_vertex_set. INPUT: - value_only -- boolean (default: False) - When set to True, only the minimum cardinal of a minimum vertex set is returned. - When set to False, the Set of vertices of a minimal feedback vertex set is returned. - solver -- (default: None) Specify a Linear Program (LP) solver to be used. If set to None, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:solve  of the class :class:MixedIntegerLinearProgram . - verbose -- integer (default: 0). Sets the level of verbosity. Set to 0 by default, which means quiet. - constraint_generation (boolean) -- whether to use constraint generation when solving the Mixed Integer Linear Program (default: True). ALGORITHMS: (Constraints generation) When the parameter constraint_generation is enabled (default) the following MILP formulation is used to solve the problem: .. MATH:: \mbox{Minimize : }&\sum_{v\in G} b_{v}\\ \mbox{Such that : }&\\ &\forall C\text{ circuits }\subseteq G, \sum_{v\in C}b_{v}\geq 1\\ As the number of circuits contained in a graph is exponential, this LP is solved through constraint generation. This means that the solver is sequentially asked to solve the problem, knowing only a portion of the circuits contained in G, each time adding to the list of its constraints the circuit which its last answer had left intact. (Another formulation based on an ordering of the vertices) When the graph is directed, a second (and very slow) formulation is available, which should only be used to check the result of the first implementation in case of doubt. .. MATH:: \mbox{Minimize : }&\sum_{v\in G} b_v\\ \mbox{Such that : }&\\ &\forall (u,v)\in G, d_u-d_v+nb_u+nb_v\geq 0\\ &\forall u\in G, 0\leq d_u\leq |G|\\ A brief explanation: An acyclic digraph can be seen as a poset, and every poset has a linear extension. This means that in any acyclic digraph the vertices can be ordered with a total order < in such a way that if (u,v)\in G, then u
• ## sage/graphs/graph.py

diff --git a/sage/graphs/graph.py b/sage/graphs/graph.py
 a sage: -1 in cycle True TESTS: :trac:14434 is fixed:: sage: g = Graph({0:[1,4,5],3:[4,8,9],4:[9],5:[7,8],7:[9]}) sage: _,cycle = g.is_tree(certificate=True) sage: g.size() 10 sage: g.add_cycle(cycle) sage: g.size() 10 """ if self.order() == 0: n = self.order() seen = {} u = self.vertex_iterator().next() v = self.neighbor_iterator(u).next() seen[u] = u stack = [(u,v)] stack = [(u,v) for v in self.neighbor_iterator(u)] while stack: u,v = stack.pop(-1) if v in seen: sage: g.is_forest(certificate = True) (True, None) sage: (2*g + graphs.PetersenGraph() + g).is_forest(certificate = True) (False, [60, 61, 62, 63, 64]) (False, [63, 62, 61, 60, 64]) """ number_of_connected_components = len(self.connected_components()) isit = (self.num_verts() ==