Ticket #14712: trac_14712.patch

File trac_14712.patch, 13.8 KB (added by ncohen, 8 years ago)
  • doc/en/reference/graphs/index.rst

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1370860578 -7200
    # Node ID d0b9d29d21159047139b49edc8beaf6641361e3d
    # Parent  ec6a3e8365f9c930df8da62874d84f198ad59ca4
    A Hypergraph class for visualisation
    * * *
    #14712: review patch
    * * *
    An Hypergraph class for visualization -- Warning
    
    diff --git a/doc/en/reference/graphs/index.rst b/doc/en/reference/graphs/index.rst
    a b  
     1
    12Graph Theory
    23============
    34
     
    2324   sage/graphs/graph_generators
    2425   sage/graphs/digraph_generators
    2526   sage/graphs/graph_generators_pyx
    26    sage/graphs/hypergraph_generators
    2727   sage/graphs/graph_database
    2828   sage/graphs/isgci
    2929
     
    3939   sage/graphs/base/dense_graph
    4040   sage/graphs/base/static_sparse_graph
    4141
     42Hypergraphs
     43-----------
     44
     45.. toctree::
     46   :maxdepth: 1
     47
     48   sage/graphs/hypergraph_generators
     49   sage/graphs/hypergraph
     50
     51
    4252
    4353Libraries of algorithms
    4454-----------------------
  • sage/graphs/all.py

    diff --git a/sage/graphs/all.py b/sage/graphs/all.py
    a b  
    66from graph_database import GraphDatabase, GenericGraphQuery, GraphQuery
    77from graph import Graph
    88from digraph import DiGraph
     9from hypergraph import Hypergraph
    910from bipartite_graph import BipartiteGraph
    1011from graph_bundle import GraphBundle
    1112import weakly_chordal
  • sage/graphs/generic_graph.py

    diff --git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
    a b  
    336336            Graph on 0 vertices
    337337        """
    338338        self._latex_opts = None
    339         # FIXME: this has nothing to do here. Ideally, we would want
    340         # this to be run just once, just before the creation of a full
    341         # latex file including a graph.
    342         # There should be a Sage-wide strategy for handling this.
    343         from sage.graphs.graph_latex import setup_latex_preamble
    344         setup_latex_preamble()
    345339
    346340    def __add__(self, other_graph):
    347341        """
     
    602596        object that may be used to also customize the
    603597        output produced here.  Possible options are documented at
    604598        :meth:`sage.graphs.graph_latex.GraphLatex.set_option`.
    605        
    606         EXAMPLES::
    607        
     599
     600        EXAMPLES::
     601
    608602            sage: from sage.graphs.graph_latex import check_tkz_graph
    609603            sage: check_tkz_graph()  # random - depends on TeX installation
    610604            sage: g = graphs.CompleteGraph(2)
     
    628622            %
    629623            \end{tikzpicture}
    630624        """
     625        from sage.graphs.graph_latex import setup_latex_preamble
     626        setup_latex_preamble()
     627
    631628        return self.latex_options().latex()
    632629
    633630    def _matrix_(self, R=None):
    634631        """
    635632        Returns the adjacency matrix of the graph over the specified ring.
    636        
    637         EXAMPLES::
    638        
     633
     634        EXAMPLES::
     635
    639636            sage: G = graphs.CompleteBipartiteGraph(2,3)
    640637            sage: m = matrix(G); m.parent()
    641638            Full MatrixSpace of 5 by 5 dense matrices over Integer Ring
  • sage/graphs/graph_coloring.py

    diff --git a/sage/graphs/graph_coloring.py b/sage/graphs/graph_coloring.py
    a b  
    407407            elif hex_colors:
    408408                return {rainbow(1)[0]: g.vertices()}
    409409            else:
    410                 return g.vertices()
     410                return [g.vertices()]
    411411        # - Bipartite set
    412412        if g.is_bipartite():
    413413            if value_only:
  • new file sage/graphs/hypergraph.py

    diff --git a/sage/graphs/hypergraph.py b/sage/graphs/hypergraph.py
    new file mode 100644
    - +  
     1r"""
     2Hypergraphs
     3
     4This module consists in a very basic implementation of :class:`Hypergraph`,
     5whose only current purpose is to provide method to visualize them. This is
     6done at the moment through `\LaTeX` and TikZ, and can be obtained from Sage
     7through the ``view`` command::
     8
     9    sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
     10    Hypergraph on 6 vertices containing 4 sets
     11    sage: view(H) # not tested
     12
     13Note that hypergraphs are very hard to visualize, and unless it is very small
     14(`\leq 10` sets) or has a very specific structure (like the following one),
     15Sage's drawing will only bring more confusion::
     16
     17    sage: g = graphs.Grid2dGraph(5,5)
     18    sage: sets = Set(map(Set,list(g.subgraph_search_iterator(graphs.CycleGraph(4)))))
     19    sage: H = Hypergraph(sets)
     20    sage: view(H) # not tested
     21
     22.. SEEALSO::
     23
     24    :class:`Hypergraph` for information on the `\LaTeX` output
     25
     26Classes and methods
     27-------------------
     28"""
     29from sage.misc.latex import latex
     30from sage.sets.set import Set
     31
     32class Hypergraph:
     33    r"""
     34    A hypergraph.
     35
     36    A *hypergraph* `H = (V, E)` is a set of vertices `V` and a collection `E` of
     37    sets of vertices called *hyperedges* or edges. In particular `E \subseteq
     38    \mathcal{P}(V)`. If all (hyper)edges contain exactly 2 vertices, then `H` is
     39    a graph in the usual sense.
     40
     41    .. rubric:: Latex output
     42
     43    The `\LaTeX` for a hypergraph `H` is consists of the vertices set and a
     44    set of closed curves. The set of vertices in each closed curve represents a
     45    hyperedge of `H`. A vertex which is encircled by a curve but is not
     46    located on its boundary is **NOT** included in the corresponding set.
     47
     48    The colors are picked for readability and have no other meaning.
     49
     50    INPUT:
     51
     52    - ``sets`` -- A list of hyperedges
     53
     54    EXAMPLES::
     55
     56        sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{6}]); H
     57        Hypergraph on 6 vertices containing 4 sets
     58
     59    REFERENCES:
     60
     61    - :wikipedia:`Hypergraph`
     62    """
     63    def __init__(self, sets):
     64        r"""
     65        Constructor
     66
     67        EXAMPLES::
     68
     69            sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
     70            Hypergraph on 6 vertices containing 4 sets
     71        """
     72        from sage.sets.set import Set
     73        self._sets = map(Set, sets)
     74        self._domain = set([])
     75        for s in sets:
     76            for i in s:
     77                self._domain.add(i)
     78
     79    def __repr__(self):
     80        r"""
     81        Short description of ``self``.
     82
     83        EXAMPLES::
     84
     85            sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
     86            Hypergraph on 6 vertices containing 4 sets
     87        """
     88        return ("Hypergraph on "+str(len(self.domain()))+" "
     89                "vertices containing "+str(len(self._sets))+" sets")
     90
     91    def edge_coloring(self):
     92        r"""
     93        Compute a proper edge-coloring.
     94
     95        A proper edge-coloring is an assignment of colors to the sets of the
     96        hypergraph such that two sets with non-empty intersection receive
     97        different colors. The coloring returned minimizes the number of colors.
     98
     99        OUTPUT:
     100
     101        A partition of the sets into color classes.
     102
     103        EXAMPLES::
     104
     105            sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
     106            Hypergraph on 6 vertices containing 4 sets
     107            sage: C = H.edge_coloring()
     108            sage: C # random
     109            [[{3, 4, 5}], [{4, 5, 6}, {1, 2, 3}], [{2, 3, 4}]]
     110            sage: Set(sum(C,[])) == Set(H._sets)
     111            True
     112        """
     113        from sage.graphs.graph import Graph
     114        g = Graph([self._sets,lambda x,y : len(x&y)],loops = False)
     115        return g.coloring(algorithm="MILP")
     116
     117    def _spring_layout(self):
     118        r"""
     119        Return a spring layout for the vertices.
     120
     121        The layout is computed by creating a graph `G` on the vertices *and*
     122        sets of the hypergraph. Each set is then made adjacent in `G` with all
     123        vertices it contains before a spring layout is computed for this
     124        graph. The position of the vertices in the hypergraph is the position of
     125        the same vertices in the graph's layout.
     126
     127        .. NOTE::
     128
     129            This method also returns the position of the "fake" vertices,
     130            i.e. those representing the sets.
     131
     132        EXAMPLES::
     133
     134            sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
     135            Hypergraph on 6 vertices containing 4 sets
     136            sage: L = H._spring_layout()
     137            sage: L # random
     138            {1: (0.238, -0.926),
     139             2: (0.672, -0.518),
     140             3: (0.449, -0.225),
     141             4: (0.782, 0.225),
     142             5: (0.558, 0.518),
     143             6: (0.992, 0.926),
     144             {3, 4, 5}: (0.504, 0.173),
     145             {2, 3, 4}: (0.727, -0.173),
     146             {4, 5, 6}: (0.838, 0.617),
     147             {1, 2, 3}: (0.393, -0.617)}
     148            sage: all(v in L for v in H.domain())
     149            True
     150            sage: all(v in L for v in H._sets)
     151            True
     152        """
     153        from sage.graphs.graph import Graph
     154
     155        g = Graph()
     156        for s in self._sets:
     157            for x in s:
     158                g.add_edge(s,x)
     159
     160        _ = g.plot(iterations = 50000,save_pos=True)
     161
     162        # The values are rounded as TikZ does not like accuracy.
     163        return {k:(round(x,3),round(y,3)) for k,(x,y) in g.get_pos().items()}
     164
     165    def domain(self):
     166        r"""
     167        Return the set of vertices.
     168
     169        EXAMPLES::
     170
     171            sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
     172            Hypergraph on 6 vertices containing 4 sets
     173            sage: H.domain()
     174            set([1, 2, 3, 4, 5, 6])
     175        """
     176        return self._domain.copy()
     177
     178    def _latex_(self):
     179        r"""
     180        Return a TikZ representation of the hypergraph.
     181
     182        EXAMPLES::
     183
     184            sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
     185            Hypergraph on 6 vertices containing 4 sets
     186            sage: view(H) # not tested
     187
     188        With sets of size 4::
     189
     190            sage: g = graphs.Grid2dGraph(5,5)
     191            sage: C4 = graphs.CycleGraph(4)
     192            sage: sets = Set(map(Set,list(g.subgraph_search_iterator(C4))))
     193            sage: H = Hypergraph(sets)
     194            sage: view(H) # not tested
     195
     196        """
     197        from sage.rings.integer import Integer
     198        from sage.functions.trig import arctan2
     199
     200        from sage.misc.misc import warn
     201        warn("\nThe hypergraph is drawn as a set of closed curves. The curve "
     202             "representing a set S go **THROUGH** the vertices contained "
     203             "in S.\n A vertex which is encircled by a curve but is not located "
     204             "on its boundary is **NOT** included in the corresponding set.\n"
     205             "\n"
     206             "The colors are picked for readability and have no other meaning.")
     207
     208        latex.add_package_to_preamble_if_available("tikz")
     209
     210        if not latex.has_file("tikz.sty"):
     211            raise RuntimeError("You must have TikZ installed in order "
     212                               "to draw a hypergraph.")
     213
     214        domain = self.domain()
     215        pos = self._spring_layout()
     216        tex = "\\begin{tikzpicture}[scale=3]\n"
     217
     218        colors = ["black", "red", "green", "blue", "cyan", "magenta", "yellow","pink","brown"]
     219        colored_sets = [(s,i) for i,S in enumerate(self.edge_coloring()) for s in S]
     220
     221        # Prints each set with its color
     222        for s,i in colored_sets:
     223            current_color = colors[i%len(colors)]
     224
     225            if len(s) == 2:
     226                s = list(s)
     227                tex += ("\\draw[color="+str(current_color)+","+
     228                        "line width=.1cm,opacity = .6] "+
     229                        str(pos[s[0]])+" -- "+str(pos[s[1]])+";\n")
     230                continue
     231
     232            tex += ("\\draw[color="+str(current_color)+","
     233                    "line width=.1cm,opacity = .6,"
     234                    "line cap=round,"
     235                    "line join=round]"
     236                    "plot [smooth cycle,tension=1] coordinates {")
     237
     238            # Reorders the vertices of s according to their angle with the
     239            # "center", i.e. the vertex representing the set s
     240            cx,cy = pos[s]
     241            s = map(lambda x:pos[x],s)
     242            s = sorted(s, key = lambda (x,y) : arctan2(x-cx,y-cy))
     243
     244            for x in s:
     245                tex += str(x)+" "
     246            tex += "};\n"
     247
     248        # Prints each vertex
     249        for v in domain:
     250            tex += "\\draw node[fill,circle,scale=.5,label={90:$"+latex(v)+"$}] at "+str(pos[v])+" {};\n"
     251
     252        tex += "\\end{tikzpicture}"
     253        return tex
     254
  • sage/graphs/hypergraph_generators.py

    diff --git a/sage/graphs/hypergraph_generators.py b/sage/graphs/hypergraph_generators.py
    a b  
    11r"""
    2 Hypergraph generators.
     2Hypergraph generators
    33
    44At the moment this module only implement one method, which calls Brendan McKay's
    55Nauty (`<http://cs.anu.edu.au/~bdm/nauty/>`_) to enumerate hypergraphs up to
  • sage/misc/latex.py

    diff --git a/sage/misc/latex.py b/sage/misc/latex.py
    a b  
    17541754        sage: s = sage.misc.latex._latex_file_(blah())
    17551755        coucou
    17561756    """
    1757     MACROS = latex_extra_preamble()
    1758 
    17591757    process = True
    17601758    if has_latex_attr(objects):
    17611759        objects = [objects]
     
    17681766    else:
    17691767        size=''
    17701768
    1771     s = LATEX_HEADER + '\n' + MACROS
    1772     s += '%s\n\\begin{document}\n\\begin{center}{\\Large\\bf %s}\\end{center}\n%s'%(
     1769    s = '%s\n\\begin{document}\n\\begin{center}{\\Large\\bf %s}\\end{center}\n%s'%(
    17731770        extra_preamble, title, size)
    17741771
    17751772    #s += "(If something is missing it may be on the next page or there may be errors in the latex.  Use view with {\\tt debug=True}.)\\vfill"
     
    17871784    else:
    17881785        s += "\n\n".join([str(x) for x in objects])
    17891786
    1790     s += '\n\\end{document}'
     1787    # latex_extra_preamble() is called here and not before because some objects
     1788    # may require additional packages to be displayed in LaTeX. Hence, the call
     1789    # to latex(x) in the previous loop may change the result of
     1790    # latex_extra_preamble()
     1791    MACROS = latex_extra_preamble()
     1792    s = LATEX_HEADER + '\n' + MACROS + s + '\n\\end{document}'
     1793
    17911794    if debug:
    17921795        print s
    17931796