Example #1
0
def IntersectionGraph(S):
    r"""
    Returns the intersection graph of the family `S`

    The intersection graph of a family `S` is a graph `G` with `V(G)=S` such
    that two elements `s_1,s_2\in S` are adjacent in `G` if and only if `s_1\cap
    s_2\neq \emptyset`.

    INPUT:

    - ``S`` -- a list of sets/tuples/iterables

        .. NOTE::

            The elements of `S` must be finite, hashable, and the elements of
            any `s\in S` must be hashable too.

    EXAMPLES::

        sage: graphs.IntersectionGraph([(1,2,3),(3,4,5),(5,6,7)])
        Intersection Graph: Graph on 3 vertices

    TESTS::

        sage: graphs.IntersectionGraph([(1,2,[1])])
        Traceback (most recent call last):
        ...
        TypeError: The elements of S must be hashable, and this one is not: (1, 2, [1])
    """
    from itertools import combinations

    for s in S:
        try:
            hash(s)
        except TypeError:
            raise TypeError(
                "The elements of S must be hashable, and this one is not: {}".
                format(s))

    ground_set_to_sets = {}
    for s in S:
        for x in s:
            if x not in ground_set_to_sets:
                ground_set_to_sets[x] = []
            ground_set_to_sets[x].append(s)

    g = Graph(name="Intersection Graph")
    g.add_vertices(S)
    for clique in itervalues(ground_set_to_sets):
        g.add_clique(set(clique))

    return g
Example #2
0
def IntersectionGraph(S):
    r"""
    Returns the intersection graph of the family `S`

    The intersection graph of a family `S` is a graph `G` with `V(G)=S` such
    that two elements `s_1,s_2\in S` are adjacent in `G` if and only if `s_1\cap
    s_2\neq \emptyset`.

    INPUT:

    - ``S`` -- a list of sets/tuples/iterables

        .. NOTE::

            The elements of `S` must be finite, hashable, and the elements of
            any `s\in S` must be hashable too.

    EXAMPLES::

        sage: graphs.IntersectionGraph([(1,2,3),(3,4,5),(5,6,7)])
        Intersection Graph: Graph on 3 vertices

    TESTS::

        sage: graphs.IntersectionGraph([(1,2,[1])])
        Traceback (most recent call last):
        ...
        TypeError: The elements of S must be hashable, and this one is not: (1, 2, [1])
    """
    from itertools import combinations

    for s in S:
        try:
            hash(s)
        except TypeError:
            raise TypeError("The elements of S must be hashable, and this one is not: {}".format(s))

    ground_set_to_sets = {}
    for s in S:
        for x in s:
            if x not in ground_set_to_sets:
                ground_set_to_sets[x] = []
            ground_set_to_sets[x].append(s)

    g = Graph(name="Intersection Graph")
    g.add_vertices(S)
    for clique in itervalues(ground_set_to_sets):
        g.add_clique(set(clique))

    return g
Example #3
0
def CompleteMultipartiteGraph(l):
    r"""
    Return a complete multipartite graph.

    INPUT:

    - ``l`` -- a list of integers; the respective sizes of the components

    PLOTTING: Produce a layout of the vertices so that vertices in the same
    vertex set are adjacent and clearly separated from vertices in other vertex
    sets.

    This is done by calculating the vertices of an `r`-gon then calculating the
    slope between adjacent vertices. We then 'walk' around the `r`-gon placing
    graph vertices in regular intervals between adjacent vertices of the
    `r`-gon.

    Makes a nicely organized graph like in this picture:
    https://commons.wikimedia.org/wiki/File:Turan_13-4.svg

    EXAMPLES:

    A complete tripartite graph with sets of sizes `5, 6, 8`::

        sage: g = graphs.CompleteMultipartiteGraph([5, 6, 8]); g
        Multipartite Graph with set sizes [5, 6, 8]: Graph on 19 vertices

    It clearly has a chromatic number of 3::

        sage: g.chromatic_number()
        3
    """
    r = len(l)  # getting the number of partitions
    name = "Multipartite Graph with set sizes {}".format(l)

    if not r:
        g = Graph()
    elif r == 1:
        g = Graph(l[0])
        g._line_embedding(range(l[0]), first=(0, 0), last=(l[0], 0))
    elif r == 2:
        g = CompleteBipartiteGraph(l[0], l[1])
        g.name(name)
    else:
        # This position code gives bad results on bipartite or isolated graphs
        points = [(cos(2 * pi * i / r), sin(2 * pi * i / r)) for i in range(r)]
        slopes = [(points[(i + 1) % r][0] - points[i % r][0],
                   points[(i + 1) % r][1] - points[i % r][1]) for i in range(r)]

        counter = 0
        positions = {}
        for i in range(r):
            vertex_set_size = l[i] + 1
            for j in range(1, vertex_set_size):
                x = points[i][0] + slopes[i][0] * j / vertex_set_size
                y = points[i][1] + slopes[i][1] * j / vertex_set_size
                positions[counter] = (x, y)
                counter += 1

        g = Graph(sum(l))
        s = 0
        for i in l:
            g.add_clique(range(s, s + i))
            s += i

        g = g.complement()
        g.set_pos(positions)

    g.name(name)
    return g
Example #4
0
def RandomBlockGraph(m, k, kmax=None, incidence_structure=False):
    r"""
    Return a Random Block Graph.

    A block graph is a connected graph in which every biconnected component
    (block) is a clique.

    .. SEEALSO::

        - :wikipedia:`Block_graph` for more details on these graphs
        - :meth:`~sage.graphs.graph.Graph.is_block_graph` -- test if a graph is a block graph
        - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices`
        - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cuts_tree`
        - :meth:`~sage.combinat.designs.incidence_structures.IncidenceStructure` 

    INPUT:

    - ``m`` -- integer; number of blocks (at least one).

    - ``k`` -- integer; minimum number of vertices of a block (at least two).

    - ``kmax`` -- integer (default: ``None``) By default, each block has `k`
      vertices. When the parameter `kmax` is specified (with `kmax \geq k`), the
      number of vertices of each block is randomly chosen between `k` and
      `kmax`.

    - ``incidence_structure`` -- boolean (default: ``False``) when set to
      ``True``, the incidence structure of the graphs is returned instead of the
      graph itself, that is the list of the lists of vertices in each
      block. This is useful for the creation of some hypergraphs.

    OUTPUT:

    A Graph when ``incidence_structure==False`` (default), and otherwise an
    incidence structure.

    EXAMPLES:

    A block graph with a single block is a clique::

        sage: B = graphs.RandomBlockGraph(1, 4)
        sage: B.is_clique()
        True

    A block graph with blocks of order 2 is a tree::

        sage: B = graphs.RandomBlockGraph(10, 2)
        sage: B.is_tree()
        True

    Every biconnected component of a block graph is a clique::

        sage: B = graphs.RandomBlockGraph(5, 3, kmax=6)
        sage: blocks,cuts = B.blocks_and_cut_vertices()
        sage: all(B.is_clique(block) for block in blocks)
        True

    A block graph with blocks of order `k` has `m*(k-1)+1` vertices::

        sage: m, k = 6, 4
        sage: B = graphs.RandomBlockGraph(m, k)
        sage: B.order() == m*(k-1)+1
        True

    Test recognition methods::

        sage: B = graphs.RandomBlockGraph(6, 2, kmax=6)
        sage: B.is_block_graph()
        True
        sage: B in graph_classes.Block
        True

    Asking for the incidence structure::

        sage: m, k = 6, 4
        sage: IS = graphs.RandomBlockGraph(m, k, incidence_structure=True)
        sage: from sage.combinat.designs.incidence_structures import IncidenceStructure
        sage: IncidenceStructure(IS)
        Incidence structure with 19 points and 6 blocks
        sage: m*(k-1)+1
        19

    TESTS:

    A block graph has at least one block, so `m\geq 1`::

        sage: B = graphs.RandomBlockGraph(0, 1)
        Traceback (most recent call last):
        ...
        ValueError: the number `m` of blocks must be >= 1

    A block has at least 2 vertices, so `k\geq 2`::

        sage: B = graphs.RandomBlockGraph(1, 1)
        Traceback (most recent call last):
        ...
        ValueError: the minimum number `k` of vertices in a block must be >= 2

    The maximum size of a block is at least its minimum size, so `k\leq kmax`::

        sage: B = graphs.RandomBlockGraph(1, 3, kmax=2)
        Traceback (most recent call last):
        ...
        ValueError: the maximum number `kmax` of vertices in a block must be >= `k`
    """
    import itertools
    from sage.misc.prandom import choice
    from sage.sets.disjoint_set import DisjointSet

    if m < 1:
        raise ValueError("the number `m` of blocks must be >= 1")
    if k < 2:
        raise ValueError(
            "the minimum number `k` of vertices in a block must be >= 2")
    if kmax is None:
        kmax = k
    elif kmax < k:
        raise ValueError(
            "the maximum number `kmax` of vertices in a block must be >= `k`")

    if m == 1:
        # A block graph with a single block is a clique
        IS = [list(range(randint(k, kmax)))]

    elif kmax == 2:
        # A block graph with blocks of order 2 is a tree
        IS = [list(e) for e in RandomTree(m + 1).edges(labels=False)]

    else:
        # We start with a random tree of order m
        T = RandomTree(m)

        # We create a block of order in range [k,kmax] per vertex of the tree
        B = {u: [(u, i) for i in range(randint(k, kmax))] for u in T}

        # For each edge of the tree, we choose 1 vertex in each of the
        # corresponding blocks and we merge them. We use a disjoint set data
        # structure to keep a unique identifier per merged vertices
        DS = DisjointSet([i for u in B for i in B[u]])
        for u, v in T.edges(labels=0):
            DS.union(choice(B[u]), choice(B[v]))

        # We relabel vertices in the range [0, m*(k-1)] and build the incidence
        # structure
        new_label = {
            root: i
            for i, root in enumerate(DS.root_to_elements_dict())
        }
        IS = [[new_label[DS.find(v)] for v in B[u]] for u in B]

    if incidence_structure:
        return IS

    # We finally build the block graph
    if k == kmax:
        BG = Graph(
            name="Random Block Graph with {} blocks of order {}".format(m, k))
    else:
        BG = Graph(
            name="Random Block Graph with {} blocks of order {} to {}".format(
                m, k, kmax))
    for block in IS:
        BG.add_clique(block)
    return BG
Example #5
0
def RandomBlockGraph(m, k, kmax=None, incidence_structure=False):
    r"""
    Return a Random Block Graph.

    A block graph is a connected graph in which every biconnected component
    (block) is a clique.

    .. SEEALSO::

        - :wikipedia:`Block_graph` for more details on these graphs
        - :meth:`~sage.graphs.graph.Graph.is_block_graph` -- test if a graph is a block graph
        - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices`
        - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cuts_tree`
        - :meth:`~sage.combinat.designs.incidence_structures.IncidenceStructure` 

    INPUT:

    - ``m`` -- integer; number of blocks (at least one).

    - ``k`` -- integer; minimum number of vertices of a block (at least two).

    - ``kmax`` -- integer (default: ``None``) By default, each block has `k`
      vertices. When the parameter `kmax` is specified (with `kmax \geq k`), the
      number of vertices of each block is randomly chosen between `k` and
      `kmax`.

    - ``incidence_structure`` -- boolean (default: ``False``) when set to
      ``True``, the incidence structure of the graphs is returned instead of the
      graph itself, that is the list of the lists of vertices in each
      block. This is useful for the creation of some hypergraphs.

    OUTPUT:

    A Graph when ``incidence_structure==False`` (default), and otherwise an
    incidence structure.

    EXAMPLES:

    A block graph with a single block is a clique::

        sage: B = graphs.RandomBlockGraph(1, 4)
        sage: B.is_clique()
        True

    A block graph with blocks of order 2 is a tree::

        sage: B = graphs.RandomBlockGraph(10, 2)
        sage: B.is_tree()
        True

    Every biconnected component of a block graph is a clique::

        sage: B = graphs.RandomBlockGraph(5, 3, kmax=6)
        sage: blocks,cuts = B.blocks_and_cut_vertices()
        sage: all(B.is_clique(block) for block in blocks)
        True

    A block graph with blocks of order `k` has `m*(k-1)+1` vertices::

        sage: m, k = 6, 4
        sage: B = graphs.RandomBlockGraph(m, k)
        sage: B.order() == m*(k-1)+1
        True

    Test recognition methods::

        sage: B = graphs.RandomBlockGraph(6, 2, kmax=6)
        sage: B.is_block_graph()
        True
        sage: B in graph_classes.Block
        True

    Asking for the incidence structure::

        sage: m, k = 6, 4
        sage: IS = graphs.RandomBlockGraph(m, k, incidence_structure=True)
        sage: from sage.combinat.designs.incidence_structures import IncidenceStructure
        sage: IncidenceStructure(IS)
        Incidence structure with 19 points and 6 blocks
        sage: m*(k-1)+1
        19

    TESTS:

    A block graph has at least one block, so `m\geq 1`::

        sage: B = graphs.RandomBlockGraph(0, 1)
        Traceback (most recent call last):
        ...
        ValueError: the number `m` of blocks must be >= 1

    A block has at least 2 vertices, so `k\geq 2`::

        sage: B = graphs.RandomBlockGraph(1, 1)
        Traceback (most recent call last):
        ...
        ValueError: the minimum number `k` of vertices in a block must be >= 2

    The maximum size of a block is at least its minimum size, so `k\leq kmax`::

        sage: B = graphs.RandomBlockGraph(1, 3, kmax=2)
        Traceback (most recent call last):
        ...
        ValueError: the maximum number `kmax` of vertices in a block must be >= `k`
    """
    import itertools
    from sage.misc.prandom import choice
    from sage.sets.disjoint_set import DisjointSet

    if m < 1:
        raise ValueError("the number `m` of blocks must be >= 1")
    if k < 2:
        raise ValueError("the minimum number `k` of vertices in a block must be >= 2")
    if kmax is None:
        kmax = k
    elif kmax < k:
        raise ValueError("the maximum number `kmax` of vertices in a block must be >= `k`")

    if m == 1:
        # A block graph with a single block is a clique
        IS = [ list(range(randint(k, kmax))) ]
        
    elif kmax == 2:
        # A block graph with blocks of order 2 is a tree
        IS = [ list(e) for e in RandomTree(m+1).edges(labels=False) ]

    else:
        # We start with a random tree of order m
        T = RandomTree(m)

        # We create a block of order in range [k,kmax] per vertex of the tree
        B = {u:[(u,i) for i in range(randint(k, kmax))] for u in T}

        # For each edge of the tree, we choose 1 vertex in each of the
        # corresponding blocks and we merge them. We use a disjoint set data
        # structure to keep a unique identifier per merged vertices
        DS = DisjointSet([i for u in B for i in B[u]])
        for u,v in T.edges(labels=0):
            DS.union(choice(B[u]), choice(B[v]))

        # We relabel vertices in the range [0, m*(k-1)] and build the incidence
        # structure
        new_label = {root:i for i,root in enumerate(DS.root_to_elements_dict())}
        IS = [ [new_label[DS.find(v)] for v in B[u]] for u in B ]

    if incidence_structure:
        return IS
    
    # We finally build the block graph
    if k == kmax:
        BG = Graph(name = "Random Block Graph with {} blocks of order {}".format(m, k))
    else:
        BG = Graph(name = "Random Block Graph with {} blocks of order {} to {}".format(m, k, kmax))
    for block in IS:
        BG.add_clique( block )
    return BG