# HG changeset patch
# User Nathann Cohen
# 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/doc/en/reference/graphs/index.rst
+++ b/doc/en/reference/graphs/index.rst
@@ 1,3 +1,4 @@
+
Graph Theory
============
@@ 23,7 +24,6 @@
sage/graphs/graph_generators
sage/graphs/digraph_generators
sage/graphs/graph_generators_pyx
 sage/graphs/hypergraph_generators
sage/graphs/graph_database
sage/graphs/isgci
@@ 39,6 +39,16 @@
sage/graphs/base/dense_graph
sage/graphs/base/static_sparse_graph
+Hypergraphs
+
+
+.. toctree::
+ :maxdepth: 1
+
+ sage/graphs/hypergraph_generators
+ sage/graphs/hypergraph
+
+
Libraries of algorithms

diff git a/sage/graphs/all.py b/sage/graphs/all.py
 a/sage/graphs/all.py
+++ b/sage/graphs/all.py
@@ 6,6 +6,7 @@
from graph_database import GraphDatabase, GenericGraphQuery, GraphQuery
from graph import Graph
from digraph import DiGraph
+from hypergraph import Hypergraph
from bipartite_graph import BipartiteGraph
from graph_bundle import GraphBundle
import weakly_chordal
diff git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
 a/sage/graphs/generic_graph.py
+++ b/sage/graphs/generic_graph.py
@@ 336,12 +336,6 @@
Graph on 0 vertices
"""
self._latex_opts = None
 # FIXME: this has nothing to do here. Ideally, we would want
 # this to be run just once, just before the creation of a full
 # latex file including a graph.
 # There should be a Sagewide strategy for handling this.
 from sage.graphs.graph_latex import setup_latex_preamble
 setup_latex_preamble()
def __add__(self, other_graph):
"""
@@ 602,9 +596,9 @@
object that may be used to also customize the
output produced here. Possible options are documented at
:meth:`sage.graphs.graph_latex.GraphLatex.set_option`.

 EXAMPLES::

+
+ EXAMPLES::
+
sage: from sage.graphs.graph_latex import check_tkz_graph
sage: check_tkz_graph() # random  depends on TeX installation
sage: g = graphs.CompleteGraph(2)
@@ 628,14 +622,17 @@
%
\end{tikzpicture}
"""
+ from sage.graphs.graph_latex import setup_latex_preamble
+ setup_latex_preamble()
+
return self.latex_options().latex()
def _matrix_(self, R=None):
"""
Returns the adjacency matrix of the graph over the specified ring.

 EXAMPLES::

+
+ EXAMPLES::
+
sage: G = graphs.CompleteBipartiteGraph(2,3)
sage: m = matrix(G); m.parent()
Full MatrixSpace of 5 by 5 dense matrices over Integer Ring
diff git a/sage/graphs/graph_coloring.py b/sage/graphs/graph_coloring.py
 a/sage/graphs/graph_coloring.py
+++ b/sage/graphs/graph_coloring.py
@@ 407,7 +407,7 @@
elif hex_colors:
return {rainbow(1)[0]: g.vertices()}
else:
 return g.vertices()
+ return [g.vertices()]
#  Bipartite set
if g.is_bipartite():
if value_only:
diff git a/sage/graphs/hypergraph.py b/sage/graphs/hypergraph.py
new file mode 100644
 /dev/null
+++ b/sage/graphs/hypergraph.py
@@ 0,0 +1,254 @@
+r"""
+Hypergraphs
+
+This module consists in a very basic implementation of :class:`Hypergraph`,
+whose only current purpose is to provide method to visualize them. This is
+done at the moment through `\LaTeX` and TikZ, and can be obtained from Sage
+through the ``view`` command::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+ sage: view(H) # not tested
+
+Note that hypergraphs are very hard to visualize, and unless it is very small
+(`\leq 10` sets) or has a very specific structure (like the following one),
+Sage's drawing will only bring more confusion::
+
+ sage: g = graphs.Grid2dGraph(5,5)
+ sage: sets = Set(map(Set,list(g.subgraph_search_iterator(graphs.CycleGraph(4)))))
+ sage: H = Hypergraph(sets)
+ sage: view(H) # not tested
+
+.. SEEALSO::
+
+ :class:`Hypergraph` for information on the `\LaTeX` output
+
+Classes and methods
+
+"""
+from sage.misc.latex import latex
+from sage.sets.set import Set
+
+class Hypergraph:
+ r"""
+ A hypergraph.
+
+ A *hypergraph* `H = (V, E)` is a set of vertices `V` and a collection `E` of
+ sets of vertices called *hyperedges* or edges. In particular `E \subseteq
+ \mathcal{P}(V)`. If all (hyper)edges contain exactly 2 vertices, then `H` is
+ a graph in the usual sense.
+
+ .. rubric:: Latex output
+
+ The `\LaTeX` for a hypergraph `H` is consists of the vertices set and a
+ set of closed curves. The set of vertices in each closed curve represents a
+ hyperedge of `H`. A vertex which is encircled by a curve but is not
+ located on its boundary is **NOT** included in the corresponding set.
+
+ The colors are picked for readability and have no other meaning.
+
+ INPUT:
+
+  ``sets``  A list of hyperedges
+
+ EXAMPLES::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+
+ REFERENCES:
+
+  :wikipedia:`Hypergraph`
+ """
+ def __init__(self, sets):
+ r"""
+ Constructor
+
+ EXAMPLES::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+ """
+ from sage.sets.set import Set
+ self._sets = map(Set, sets)
+ self._domain = set([])
+ for s in sets:
+ for i in s:
+ self._domain.add(i)
+
+ def __repr__(self):
+ r"""
+ Short description of ``self``.
+
+ EXAMPLES::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+ """
+ return ("Hypergraph on "+str(len(self.domain()))+" "
+ "vertices containing "+str(len(self._sets))+" sets")
+
+ def edge_coloring(self):
+ r"""
+ Compute a proper edgecoloring.
+
+ A proper edgecoloring is an assignment of colors to the sets of the
+ hypergraph such that two sets with nonempty intersection receive
+ different colors. The coloring returned minimizes the number of colors.
+
+ OUTPUT:
+
+ A partition of the sets into color classes.
+
+ EXAMPLES::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+ sage: C = H.edge_coloring()
+ sage: C # random
+ [[{3, 4, 5}], [{4, 5, 6}, {1, 2, 3}], [{2, 3, 4}]]
+ sage: Set(sum(C,[])) == Set(H._sets)
+ True
+ """
+ from sage.graphs.graph import Graph
+ g = Graph([self._sets,lambda x,y : len(x&y)],loops = False)
+ return g.coloring(algorithm="MILP")
+
+ def _spring_layout(self):
+ r"""
+ Return a spring layout for the vertices.
+
+ The layout is computed by creating a graph `G` on the vertices *and*
+ sets of the hypergraph. Each set is then made adjacent in `G` with all
+ vertices it contains before a spring layout is computed for this
+ graph. The position of the vertices in the hypergraph is the position of
+ the same vertices in the graph's layout.
+
+ .. NOTE::
+
+ This method also returns the position of the "fake" vertices,
+ i.e. those representing the sets.
+
+ EXAMPLES::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+ sage: L = H._spring_layout()
+ sage: L # random
+ {1: (0.238, 0.926),
+ 2: (0.672, 0.518),
+ 3: (0.449, 0.225),
+ 4: (0.782, 0.225),
+ 5: (0.558, 0.518),
+ 6: (0.992, 0.926),
+ {3, 4, 5}: (0.504, 0.173),
+ {2, 3, 4}: (0.727, 0.173),
+ {4, 5, 6}: (0.838, 0.617),
+ {1, 2, 3}: (0.393, 0.617)}
+ sage: all(v in L for v in H.domain())
+ True
+ sage: all(v in L for v in H._sets)
+ True
+ """
+ from sage.graphs.graph import Graph
+
+ g = Graph()
+ for s in self._sets:
+ for x in s:
+ g.add_edge(s,x)
+
+ _ = g.plot(iterations = 50000,save_pos=True)
+
+ # The values are rounded as TikZ does not like accuracy.
+ return {k:(round(x,3),round(y,3)) for k,(x,y) in g.get_pos().items()}
+
+ def domain(self):
+ r"""
+ Return the set of vertices.
+
+ EXAMPLES::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+ sage: H.domain()
+ set([1, 2, 3, 4, 5, 6])
+ """
+ return self._domain.copy()
+
+ def _latex_(self):
+ r"""
+ Return a TikZ representation of the hypergraph.
+
+ EXAMPLES::
+
+ sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H
+ Hypergraph on 6 vertices containing 4 sets
+ sage: view(H) # not tested
+
+ With sets of size 4::
+
+ sage: g = graphs.Grid2dGraph(5,5)
+ sage: C4 = graphs.CycleGraph(4)
+ sage: sets = Set(map(Set,list(g.subgraph_search_iterator(C4))))
+ sage: H = Hypergraph(sets)
+ sage: view(H) # not tested
+
+ """
+ from sage.rings.integer import Integer
+ from sage.functions.trig import arctan2
+
+ from sage.misc.misc import warn
+ warn("\nThe hypergraph is drawn as a set of closed curves. The curve "
+ "representing a set S go **THROUGH** the vertices contained "
+ "in S.\n A vertex which is encircled by a curve but is not located "
+ "on its boundary is **NOT** included in the corresponding set.\n"
+ "\n"
+ "The colors are picked for readability and have no other meaning.")
+
+ latex.add_package_to_preamble_if_available("tikz")
+
+ if not latex.has_file("tikz.sty"):
+ raise RuntimeError("You must have TikZ installed in order "
+ "to draw a hypergraph.")
+
+ domain = self.domain()
+ pos = self._spring_layout()
+ tex = "\\begin{tikzpicture}[scale=3]\n"
+
+ colors = ["black", "red", "green", "blue", "cyan", "magenta", "yellow","pink","brown"]
+ colored_sets = [(s,i) for i,S in enumerate(self.edge_coloring()) for s in S]
+
+ # Prints each set with its color
+ for s,i in colored_sets:
+ current_color = colors[i%len(colors)]
+
+ if len(s) == 2:
+ s = list(s)
+ tex += ("\\draw[color="+str(current_color)+","+
+ "line width=.1cm,opacity = .6] "+
+ str(pos[s[0]])+"  "+str(pos[s[1]])+";\n")
+ continue
+
+ tex += ("\\draw[color="+str(current_color)+","
+ "line width=.1cm,opacity = .6,"
+ "line cap=round,"
+ "line join=round]"
+ "plot [smooth cycle,tension=1] coordinates {")
+
+ # Reorders the vertices of s according to their angle with the
+ # "center", i.e. the vertex representing the set s
+ cx,cy = pos[s]
+ s = map(lambda x:pos[x],s)
+ s = sorted(s, key = lambda (x,y) : arctan2(xcx,ycy))
+
+ for x in s:
+ tex += str(x)+" "
+ tex += "};\n"
+
+ # Prints each vertex
+ for v in domain:
+ tex += "\\draw node[fill,circle,scale=.5,label={90:$"+latex(v)+"$}] at "+str(pos[v])+" {};\n"
+
+ tex += "\\end{tikzpicture}"
+ return tex
+
diff git a/sage/graphs/hypergraph_generators.py b/sage/graphs/hypergraph_generators.py
 a/sage/graphs/hypergraph_generators.py
+++ b/sage/graphs/hypergraph_generators.py
@@ 1,5 +1,5 @@
r"""
Hypergraph generators.
+Hypergraph generators
At the moment this module only implement one method, which calls Brendan McKay's
Nauty (``_) to enumerate hypergraphs up to
diff git a/sage/misc/latex.py b/sage/misc/latex.py
 a/sage/misc/latex.py
+++ b/sage/misc/latex.py
@@ 1754,8 +1754,6 @@
sage: s = sage.misc.latex._latex_file_(blah())
coucou
"""
 MACROS = latex_extra_preamble()

process = True
if has_latex_attr(objects):
objects = [objects]
@@ 1768,8 +1766,7 @@
else:
size=''
 s = LATEX_HEADER + '\n' + MACROS
 s += '%s\n\\begin{document}\n\\begin{center}{\\Large\\bf %s}\\end{center}\n%s'%(
+ s = '%s\n\\begin{document}\n\\begin{center}{\\Large\\bf %s}\\end{center}\n%s'%(
extra_preamble, title, size)
#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"
@@ 1787,7 +1784,13 @@
else:
s += "\n\n".join([str(x) for x in objects])
 s += '\n\\end{document}'
+ # latex_extra_preamble() is called here and not before because some objects
+ # may require additional packages to be displayed in LaTeX. Hence, the call
+ # to latex(x) in the previous loop may change the result of
+ # latex_extra_preamble()
+ MACROS = latex_extra_preamble()
+ s = LATEX_HEADER + '\n' + MACROS + s + '\n\\end{document}'
+
if debug:
print s