Ticket #14434: trac_14434-move.patch

File trac_14434-move.patch, 16.3 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 55162ee978f3fee3c4cf0b11fc74f91ad806c3a0
    # Parent  d6beda1cb55b76c26ba6167d653882eec02b5cbd
    Move digraph.feedback_vertex_set to generic_digraph.py
    
    diff --git a/sage/graphs/digraph.py b/sage/graphs/digraph.py
    a b  
    15921592
    15931593                return [(u,v) for (u,v) in self.edges(labels=None) if b_sol[(u,v)]==1]
    15941594
    1595     def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constraint_generation = True):
    1596         r"""
    1597         Computes the minimum feedback vertex set of a digraph.
    1598 
    1599         The minimum feedback vertex set of a digraph is a set of vertices
    1600         that intersect all the circuits of the digraph.
    1601         Equivalently, a minimum feedback vertex set of a DiGraph is a set
    1602         `S` of vertices such that the digraph `G-S` is acyclic. For more
    1603         information, see the
    1604         `Wikipedia article on feedback vertex sets
    1605         <http://en.wikipedia.org/wiki/Feedback_vertex_set>`_.
    1606 
    1607         INPUT:
    1608 
    1609         - ``value_only`` -- boolean (default: ``False``)
    1610 
    1611           - When set to ``True``, only the minimum cardinal of a minimum vertex
    1612             set is returned.
    1613 
    1614           - When set to ``False``, the ``Set`` of vertices of a minimal feedback
    1615             vertex set is returned.
    1616 
    1617         - ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
    1618           solver to be used. If set to ``None``, the default one is used. For
    1619           more information on LP solvers and which default solver is used,
    1620           see the method
    1621           :meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>`
    1622           of the class
    1623           :class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
    1624 
    1625         - ``verbose`` -- integer (default: ``0``). Sets the level of
    1626           verbosity. Set to 0 by default, which means quiet.
    1627 
    1628         - ``constraint_generation`` (boolean) -- whether to use constraint
    1629           generation when solving the Mixed Integer Linear Program (default:
    1630           ``True``).
    1631 
    1632         ALGORITHM:
    1633 
    1634         This problem is solved using Linear Programming, which certainly is not
    1635         the best way and will have to be replaced by a better algorithm.  The
    1636         program to be solved is the following:
    1637 
    1638         .. MATH::
    1639 
    1640             \mbox{Minimize : }&\sum_{v\in G} b_v\\
    1641             \mbox{Such that : }&\\
    1642             &\forall (u,v)\in G, d_u-d_v+nb_u+nb_v\geq 0\\
    1643             &\forall u\in G, 0\leq d_u\leq |G|\\
    1644                        
    1645         A brief explanation:
    1646 
    1647         An acyclic digraph can be seen as a poset, and every poset has a linear
    1648         extension. This means that in any acyclic digraph the vertices can be
    1649         ordered with a total order `<` in such a way that if `(u,v)\in G`, then
    1650         `u<v`.  Thus, this linear program is built in order to assign to each
    1651         vertex `v` a number `d_v\in [0,\dots,n-1]` such that if there exists an
    1652         edge `(u,v)\in G` then either `d_v<d_u` or one of `u` or `v` is removed.
    1653         The number of vertices removed is then minimized, which is the
    1654         objective.
    1655 
    1656         (Constraint Generation)
    1657  
    1658         If the parameter ``constraint_generation`` is enabled, a more efficient
    1659         formulation is used :
    1660  
    1661         .. MATH::
    1662  
    1663             \mbox{Minimize : }&\sum_{v\in G} b_{v}\\ 
    1664             \mbox{Such that : }&\\ 
    1665             &\forall C\text{ circuits }\subseteq G, \sum_{v\in C}b_{v}\geq 1\\
    1666  
    1667         As the number of circuits contained in a graph is exponential, this LP
    1668         is solved through constraint generation. This means that the solver is
    1669         sequentially asked to solved the problem, knowing only a portion of the
    1670         circuits contained in `G`, each time adding to the list of its
    1671         constraints the circuit which its last answer had left intact.
    1672 
    1673         EXAMPLES:
    1674 
    1675         In a digraph built from a graph, any edge is replaced by arcs going in
    1676         the two opposite directions, thus creating a cycle of length two.
    1677         Hence, to remove all the cycles from the graph, each edge must see one
    1678         of its neighbors removed : a feedback vertex set is in this situation a
    1679         vertex cover::
    1680 
    1681             sage: cycle=graphs.CycleGraph(5)
    1682             sage: dcycle=DiGraph(cycle)
    1683             sage: cycle.vertex_cover(value_only=True)
    1684             3
    1685             sage: feedback = dcycle.feedback_vertex_set()
    1686             sage: len(feedback)
    1687             3
    1688             sage: (u,v,l) = cycle.edge_iterator().next()
    1689             sage: u in feedback or v in feedback
    1690             True
    1691            
    1692         For a circuit, the minimum feedback arc set is clearly `1`::
    1693 
    1694             sage: circuit = digraphs.Circuit(5)
    1695             sage: circuit.feedback_vertex_set(value_only=True) == 1
    1696             True
    1697 
    1698         TESTS:
    1699  
    1700         Comparing with/without constraint generation::
    1701  
    1702             sage: g = digraphs.RandomDirectedGNP(10,.3)
    1703             sage: x = g.feedback_vertex_set(value_only = True)
    1704             sage: y = g.feedback_vertex_set(value_only = True,
    1705             ...            constraint_generation = False)
    1706             sage: x == y
    1707             True
    1708          """
    1709 
    1710         # It would be a pity to start a LP if the digraph is already acyclic
    1711         if self.is_directed_acyclic():
    1712             if value_only:
    1713                 return 0
    1714             return []
    1715 
    1716         from sage.numerical.mip import MixedIntegerLinearProgram
    1717 
    1718         ########################################
    1719         # Constraint Generation Implementation #
    1720         ########################################
    1721         if constraint_generation:
    1722 
    1723             p = MixedIntegerLinearProgram(constraint_generation = True,
    1724                                           maximization = False)
    1725 
    1726             # An variable for each vertex
    1727             b = p.new_variable(binary = True)
    1728 
    1729             # Variables are binary, and their coefficient in the objective is 1
    1730 
    1731             p.set_objective( p.sum( b[v] for v in self))
    1732 
    1733             p.solve(log = verbose)
    1734 
    1735             # For as long as we do not break because the digraph is
    1736             # acyclic....
    1737             while (1):
    1738 
    1739                 # Building the graph without the edges removed by the LP
    1740                 h = self.subgraph(vertices =
    1741                                   [v for v in self if p.get_values(b[v]) < .5])
    1742 
    1743                 # Is the digraph acyclic ?
    1744                 isok, certificate = h.is_directed_acyclic(certificate = True)
    1745 
    1746                 # If so, we are done !
    1747                 if isok:
    1748                     break
    1749 
    1750                 if verbose:
    1751                     print "Adding a constraint on circuit : ",certificate
    1752 
    1753                 # There is a circuit left. Let's add the corresponding
    1754                 # constraint !
    1755 
    1756                 p.add_constraint( p.sum( b[v] for v in certificate), min = 1)
    1757                
    1758                 obj = p.solve(log = verbose)
    1759 
    1760             if value_only:
    1761                 return obj
    1762            
    1763             else:
    1764            
    1765                 # listing the edges contained in the MFAS
    1766                 return [v for v in self if p.get_values(b[v]) > .5]
    1767 
    1768 
    1769         else:
    1770 
    1771         ######################################
    1772         # Ordering-based MILP Implementation #
    1773         ######################################
    1774 
    1775             p = MixedIntegerLinearProgram(maximization=False, solver=solver)
    1776            
    1777             b = p.new_variable(binary = True)
    1778             d = p.new_variable(integer = True)
    1779             n = self.order()
    1780 
    1781             # The removed vertices cover all the back arcs ( third condition )
    1782             for (u,v) in self.edges(labels=None):
    1783                 p.add_constraint(d[u]-d[v]+n*(b[u]+b[v]),min=1)
    1784 
    1785             for u in self:
    1786                 p.add_constraint(d[u],max=n)
    1787 
    1788             p.set_objective(p.sum([b[v] for v in self]))
    1789 
    1790             if value_only:
    1791                 return Integer(round(p.solve(objective_only=True, log=verbose)))
    1792             else:
    1793                 p.solve(log=verbose)
    1794                 b_sol=p.get_values(b)
    1795 
    1796                 return [v for v in self if b_sol[v]==1]
    1797 
    1798 
    17991595    ### Construction
    18001596
    18011597    def reverse(self):
  • sage/graphs/generic_graph.py

    diff --git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
    a b  
    61756175        else:
    61766176            raise ValueError("``algorithm`` (%s) should be 'tsp' or 'backtrack'."%(algorithm))
    61776177
     6178    def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, constraint_generation = True):
     6179        r"""
     6180        Computes the minimum feedback vertex set of a digraph.
     6181
     6182        The minimum feedback vertex set of a digraph is a set of vertices
     6183        that intersect all the circuits of the digraph.
     6184        Equivalently, a minimum feedback vertex set of a DiGraph is a set
     6185        `S` of vertices such that the digraph `G-S` is acyclic. For more
     6186        information, see the
     6187        `Wikipedia article on feedback vertex sets
     6188        <http://en.wikipedia.org/wiki/Feedback_vertex_set>`_.
     6189
     6190        INPUT:
     6191
     6192        - ``value_only`` -- boolean (default: ``False``)
     6193
     6194          - When set to ``True``, only the minimum cardinal of a minimum vertex
     6195            set is returned.
     6196
     6197          - When set to ``False``, the ``Set`` of vertices of a minimal feedback
     6198            vertex set is returned.
     6199
     6200        - ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
     6201          solver to be used. If set to ``None``, the default one is used. For
     6202          more information on LP solvers and which default solver is used,
     6203          see the method
     6204          :meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>`
     6205          of the class
     6206          :class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
     6207
     6208        - ``verbose`` -- integer (default: ``0``). Sets the level of
     6209          verbosity. Set to 0 by default, which means quiet.
     6210
     6211        - ``constraint_generation`` (boolean) -- whether to use constraint
     6212          generation when solving the Mixed Integer Linear Program (default:
     6213          ``True``).
     6214
     6215        ALGORITHM:
     6216
     6217        This problem is solved using Linear Programming, which certainly is not
     6218        the best way and will have to be replaced by a better algorithm.  The
     6219        program to be solved is the following:
     6220
     6221        .. MATH::
     6222
     6223            \mbox{Minimize : }&\sum_{v\in G} b_v\\
     6224            \mbox{Such that : }&\\
     6225            &\forall (u,v)\in G, d_u-d_v+nb_u+nb_v\geq 0\\
     6226            &\forall u\in G, 0\leq d_u\leq |G|\\
     6227
     6228        A brief explanation:
     6229
     6230        An acyclic digraph can be seen as a poset, and every poset has a linear
     6231        extension. This means that in any acyclic digraph the vertices can be
     6232        ordered with a total order `<` in such a way that if `(u,v)\in G`, then
     6233        `u<v`.  Thus, this linear program is built in order to assign to each
     6234        vertex `v` a number `d_v\in [0,\dots,n-1]` such that if there exists an
     6235        edge `(u,v)\in G` then either `d_v<d_u` or one of `u` or `v` is removed.
     6236        The number of vertices removed is then minimized, which is the
     6237        objective.
     6238
     6239        (Constraint Generation)
     6240
     6241        If the parameter ``constraint_generation`` is enabled, a more efficient
     6242        formulation is used :
     6243
     6244        .. MATH::
     6245
     6246            \mbox{Minimize : }&\sum_{v\in G} b_{v}\\
     6247            \mbox{Such that : }&\\
     6248            &\forall C\text{ circuits }\subseteq G, \sum_{v\in C}b_{v}\geq 1\\
     6249
     6250        As the number of circuits contained in a graph is exponential, this LP
     6251        is solved through constraint generation. This means that the solver is
     6252        sequentially asked to solved the problem, knowing only a portion of the
     6253        circuits contained in `G`, each time adding to the list of its
     6254        constraints the circuit which its last answer had left intact.
     6255
     6256        EXAMPLES:
     6257
     6258        In a digraph built from a graph, any edge is replaced by arcs going in
     6259        the two opposite directions, thus creating a cycle of length two.
     6260        Hence, to remove all the cycles from the graph, each edge must see one
     6261        of its neighbors removed : a feedback vertex set is in this situation a
     6262        vertex cover::
     6263
     6264            sage: cycle=graphs.CycleGraph(5)
     6265            sage: dcycle=DiGraph(cycle)
     6266            sage: cycle.vertex_cover(value_only=True)
     6267            3
     6268            sage: feedback = dcycle.feedback_vertex_set()
     6269            sage: len(feedback)
     6270            3
     6271            sage: (u,v,l) = cycle.edge_iterator().next()
     6272            sage: u in feedback or v in feedback
     6273            True
     6274
     6275        For a circuit, the minimum feedback arc set is clearly `1`::
     6276
     6277            sage: circuit = digraphs.Circuit(5)
     6278            sage: circuit.feedback_vertex_set(value_only=True) == 1
     6279            True
     6280
     6281        TESTS:
     6282
     6283        Comparing with/without constraint generation::
     6284
     6285            sage: g = digraphs.RandomDirectedGNP(10,.3)
     6286            sage: x = g.feedback_vertex_set(value_only = True)
     6287            sage: y = g.feedback_vertex_set(value_only = True,
     6288            ...            constraint_generation = False)
     6289            sage: x == y
     6290            True
     6291         """
     6292
     6293        # It would be a pity to start a LP if the digraph is already acyclic
     6294        if self.is_directed_acyclic():
     6295            if value_only:
     6296                return 0
     6297            return []
     6298
     6299        from sage.numerical.mip import MixedIntegerLinearProgram
     6300
     6301        ########################################
     6302        # Constraint Generation Implementation #
     6303        ########################################
     6304        if constraint_generation:
     6305
     6306            p = MixedIntegerLinearProgram(constraint_generation = True,
     6307                                          maximization = False)
     6308
     6309            # An variable for each vertex
     6310            b = p.new_variable(binary = True)
     6311
     6312            # Variables are binary, and their coefficient in the objective is 1
     6313
     6314            p.set_objective( p.sum( b[v] for v in self))
     6315
     6316            p.solve(log = verbose)
     6317
     6318            # For as long as we do not break because the digraph is
     6319            # acyclic....
     6320            while (1):
     6321
     6322                # Building the graph without the edges removed by the LP
     6323                h = self.subgraph(vertices =
     6324                                  [v for v in self if p.get_values(b[v]) < .5])
     6325
     6326                # Is the digraph acyclic ?
     6327                isok, certificate = h.is_directed_acyclic(certificate = True)
     6328
     6329                # If so, we are done !
     6330                if isok:
     6331                    break
     6332
     6333                if verbose:
     6334                    print "Adding a constraint on circuit : ",certificate
     6335
     6336                # There is a circuit left. Let's add the corresponding
     6337                # constraint !
     6338
     6339                p.add_constraint( p.sum( b[v] for v in certificate), min = 1)
     6340
     6341                obj = p.solve(log = verbose)
     6342
     6343            if value_only:
     6344                return obj
     6345
     6346            else:
     6347
     6348                # listing the edges contained in the MFAS
     6349                return [v for v in self if p.get_values(b[v]) > .5]
     6350
     6351
     6352        else:
     6353
     6354        ######################################
     6355        # Ordering-based MILP Implementation #
     6356        ######################################
     6357
     6358            p = MixedIntegerLinearProgram(maximization=False, solver=solver)
     6359
     6360            b = p.new_variable(binary = True)
     6361            d = p.new_variable(integer = True)
     6362            n = self.order()
     6363
     6364            # The removed vertices cover all the back arcs ( third condition )
     6365            for (u,v) in self.edges(labels=None):
     6366                p.add_constraint(d[u]-d[v]+n*(b[u]+b[v]),min=1)
     6367
     6368            for u in self:
     6369                p.add_constraint(d[u],max=n)
     6370
     6371            p.set_objective(p.sum([b[v] for v in self]))
     6372
     6373            if value_only:
     6374                return Integer(round(p.solve(objective_only=True, log=verbose)))
     6375            else:
     6376                p.solve(log=verbose)
     6377                b_sol=p.get_values(b)
     6378
     6379                return [v for v in self if b_sol[v]==1]
     6380
    61786381    def flow(self, x, y, value_only=True, integer=False, use_edge_labels=True, vertex_bound=False, method = None, solver=None, verbose=0):
    61796382        r"""
    61806383        Returns a maximum flow in the graph from ``x`` to ``y``