Static dense graphs - Graph Theory

Skip to content
Back to top
View this page
Edit this page

# Static dense graphs

This module gathers everything which is related to static dense graphs, i.e. :

- The vertices are integer from \(0\) to \(n-1\)

- No labels on vertices/edges

- No multiple edges

- No addition/removal of vertices

This being said, it is technically possible to add/remove edges. The data structure does not mind at all.

It is all based on the binary matrix data structure described in `data_structures/binary_matrix.pxd`, which is almost a copy of the bitset data structure. The only difference is that it differentiates the rows (the vertices) instead of storing the whole data in a long bitset, and we can use that.

For an overview of graph data structures in sage, see `overview`.

## Index

Cython functions

`dense_graph_init` | Fill a binary matrix with the information from a Sage (di)graph.

Python functions

`is_strongly_regular()` | Check whether the graph is strongly regular
`is_triangle_free()` | Check whether \(G\) is triangle free
`triangles_count()` | Return the number of triangles containing \(v\), for every \(v\)
`connected_subgraph_iterator()` | Iterator over the induced connected subgraphs of order at most \(k\)

## Functions

sage.graphs.base.static_dense_graph.connected_full_subgraphs(G, edges_only=False, labels=False, min_edges=None, max_edges=None)

  Return an iterator over the connected subgraphs of \(G\) with same vertex set.

  This method implements a iterator over the connected subgraphs of the input (di)graph with the same ground set of vertices. That is, it iterates over every subgraph \(H = (V_H, E_H)\) of \(G = (V, E)\) such that \(V_H = V\), \(E_H \subseteq E\) and \(H\) is connected. Hence, this method may yield a huge number of graphs.

  When the input (di)graph \(G\) is not connected, this method returns nothing.

  As for method `sage.graphs.generic_graph.connected_components()`, edge orientation is ignored. Hence, the directed graph with a single arc \(0 \to 1\) is considered connected.

  INPUT:

  - `G` – a `Graph` or a `DiGraph`; loops and multiple edges are not allowed

  - `edges_only` – boolean (default: `False`); whether to return (Di)Graph or list of vertices

  - `labels` – boolean (default: `False`); whether to return labeled edges or not. This parameter is used only when `edges_only` is `True`.

  - `min_edges` – integer (default: `None`); minimum number of edges of reported subgraphs. By default (`None`), this lower bound will be set to \(n - 1\).

  - `max_edges` – integer (default: `None`); maximum number of edges of reported subgraphs. By default (`None`), this lower bound will be set to the number of edges of the input (di)graph.

  Note

  Roughly, this method explores all possible subsets of neighbors of each vertex, which represents a huge number of subsets. We have thus chosen to limit the degree of the vertices of the graphs that can be considered, even if the graph has a single connected subgraph (e.g., a tree). It is therefore recommended to call this method on biconnected components, as done in `connected_subgraph_iterator()`.

  EXAMPLES:

  The complete graph of order 3 has 4 connected subgraphs:

  Sage

      sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      sage: G = graphs.CompleteGraph(3)
      sage: len(list(connected_full_subgraphs(G)))
      4

  Python

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      >>> G = graphs.CompleteGraph(Integer(3))
      >>> len(list(connected_full_subgraphs(G)))
      4

  A cycle of order 5 has 6 connected subgraphs:

  Sage

      sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      sage: G = graphs.CycleGraph(5)
      sage: len(list(connected_full_subgraphs(G)))
      6

  [Python]

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      >>> G = graphs.CycleGraph(Integer(5))
      >>> len(list(connected_full_subgraphs(G)))
      6

  The House graph has 18 connected subgraphs of order 5:

  Sage

      sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      sage: G = graphs.HouseGraph()
      sage: L = list(connected_full_subgraphs(G))
      sage: len(L)
      18
      sage: all(g.order() == 5 for g in L)
      True
      sage: all(g.is_connected() for g in L)
      True
      sage: F = frozenset(frozenset(g.edges(sort=False, labels=False)) for g in L)
      sage: len(F)
      18

  Python

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      >>> G = graphs.HouseGraph()
      >>> L = list(connected_full_subgraphs(G))
      >>> len(L)
      18
      >>> all(g.order() == Integer(5) for g in L)
      True
      >>> all(g.is_connected() for g in L)
      True
      >>> F = frozenset(frozenset(g.edges(sort=False, labels=False)) for g in L)
      >>> len(F)
      18

  Specifying bounds on the number of edges:

  Sage

      sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      sage: G = graphs.HouseGraph()
      sage: [g.size() for g in connected_full_subgraphs(G)]
      [6, 5, 5, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4]
      sage: [g.size() for g in connected_full_subgraphs(G, max_edges=4)]
      [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
      sage: [g.size() for g in connected_full_subgraphs(G, min_edges=6)]
      [6]
      sage: [g.size() for g in connected_full_subgraphs(G, min_edges=5, max_edges=5)]
      [5, 5, 5, 5, 5, 5]

  [Python]

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      >>> G = graphs.HouseGraph()
      >>> [g.size() for g in connected_full_subgraphs(G)]
      [6, 5, 5, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4]
      >>> [g.size() for g in connected_full_subgraphs(G, max_edges=Integer(4))]
      [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
      >>> [g.size() for g in connected_full_subgraphs(G, min_edges=Integer(6))]
      [6]
      >>> [g.size() for g in connected_full_subgraphs(G, min_edges=Integer(5), max_edges=Integer(5))]
      [5, 5, 5, 5, 5, 5]

  Asking for edges only:

  Sage

      sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      sage: G = Graph([(0, 1, "01"), (0, 2, "02"), (1, 2, "12")])
      sage: it = connected_full_subgraphs(G, edges_only=True)
      sage: next(it)
      [(0, 1), (0, 2), (1, 2)]
      sage: next(it)
      [(0, 1), (0, 2)]
      sage: it = connected_full_subgraphs(G, edges_only=True, labels=True)
      sage: next(it)
      [(0, 1, '01'), (0, 2, '02'), (1, 2, '12')]
      sage: next(it)
      [(0, 1, '01'), (0, 2, '02')]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      >>> G = Graph([(Integer(0), Integer(1), "01"), (Integer(0), Integer(2), "02"), (Integer(1), Integer(2), "12")])
      >>> it = connected_full_subgraphs(G, edges_only=True)
      >>> next(it)
      [(0, 1), (0, 2), (1, 2)]
      >>> next(it)
      [(0, 1), (0, 2)]
      >>> it = connected_full_subgraphs(G, edges_only=True, labels=True)
      >>> next(it)
      [(0, 1, '01'), (0, 2, '02'), (1, 2, '12')]
      >>> next(it)
      [(0, 1, '01'), (0, 2, '02')]

  Subgraphs of a digraph:

  Sage

      sage: from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      sage: G = digraphs.Complete(2)
      sage: list(connected_full_subgraphs(G, edges_only=True))
      [[(0, 1)], [(1, 0)], [(0, 1), (1, 0)]]

  [Python]

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import connected_full_subgraphs
      >>> G = digraphs.Complete(Integer(2))
      >>> list(connected_full_subgraphs(G, edges_only=True))
      [[(0, 1)], [(1, 0)], [(0, 1), (1, 0)]]

sage.graphs.base.static_dense_graph.connected_subgraph_iterator(G, k=None, vertices_only=False, edges_only=False, labels=False, induced=True, exactly_k=False)

  Return an iterator over the induced connected subgraphs of order at most \(k\).

  This method implements a iterator over the induced connected subgraphs of the input (di)graph. An induced subgraph of a graph is another graph, formed from a subset of the vertices of the graph and all of the edges connecting pairs of vertices in that subset (Wikipedia article Induced_subgraph).

  As for method `sage.graphs.generic_graph.connected_components()`, edge orientation is ignored. Hence, the directed graph with a single arc \(0 \to 1\) is considered connected.

  INPUT:

  - `G` – a `Graph` or a `DiGraph`; loops and multiple edges are allowed

  - `k` – (optional) integer; maximum order of the connected subgraphs to report; by default, the method iterates over all connected subgraphs (equivalent to `k == n`)

  - `vertices_only` – boolean (default: `False`); whether to return (Di)Graph or list of vertices. This parameter is ignored when `induced` is `True`.

  - `edges_only` – boolean (default: `False`); whether to return (Di)Graph or list of edges. When `vertices_only` is `True`, this parameter is ignored.

  - `labels` – boolean (default: `False`); whether to return labeled edges or not. This parameter is used only when `vertices_only` is `False` and `edges_only` is `True`.

  - `induced` – boolean (default: `True`); whether to return induced connected sub(di)graph only or also non-induced sub(di)graphs. This parameter can be set to `False` for simple (di)graphs only.

  - `exactly_k` – boolean (default: `False`); `True` if we only return graphs of order \(k\), `False` if we return graphs of order at most \(k\).

  EXAMPLES:

  Sage

      sage: G = DiGraph([(1, 2), (2, 3), (3, 4), (4, 2)])
      sage: list(G.connected_subgraph_iterator())
      [Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 3 vertices,
       Subgraph of (): Digraph on 4 vertices,
       Subgraph of (): Digraph on 3 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 3 vertices,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex]
      sage: list(G.connected_subgraph_iterator(vertices_only=True))
      [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4],
       [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]
      sage: list(G.connected_subgraph_iterator(k=2))
      [Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex]
      sage: list(G.connected_subgraph_iterator(k=3, vertices_only=True, exactly_k=True))
      [[1, 2, 3], [1, 2, 4], [2, 3, 4]]
      sage: list(G.connected_subgraph_iterator(k=2, vertices_only=True))
      [[1], [1, 2], [2], [2, 3], [2, 4], [3], [3, 4], [4]]

      sage: G = DiGraph([(1, 2), (2, 1)])
      sage: list(G.connected_subgraph_iterator())
      [Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex]
      sage: list(G.connected_subgraph_iterator(vertices_only=True))
      [[1], [1, 2], [2]]

      sage: G = graphs.CompleteGraph(3)
      sage: len(list(G.connected_subgraph_iterator()))
      7
      sage: len(list(G.connected_subgraph_iterator(vertices_only=True)))
      7
      sage: len(list(G.connected_subgraph_iterator(edges_only=True)))
      7
      sage: len(list(G.connected_subgraph_iterator(induced=False)))
      10

      sage: G = DiGraph([(0, 1), (1, 0), (1, 2), (2, 1)])
      sage: len(list(G.connected_subgraph_iterator()))
      6
      sage: len(list(G.connected_subgraph_iterator(vertices_only=True)))
      6
      sage: len(list(G.connected_subgraph_iterator(edges_only=True)))
      6
      sage: len(list(G.connected_subgraph_iterator(induced=False)))
      18

  Python

      >>> from sage.all import *
      >>> G = DiGraph([(Integer(1), Integer(2)), (Integer(2), Integer(3)), (Integer(3), Integer(4)), (Integer(4), Integer(2))])
      >>> list(G.connected_subgraph_iterator())
      [Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 3 vertices,
       Subgraph of (): Digraph on 4 vertices,
       Subgraph of (): Digraph on 3 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 3 vertices,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex]
      >>> list(G.connected_subgraph_iterator(vertices_only=True))
      [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4],
       [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]
      >>> list(G.connected_subgraph_iterator(k=Integer(2)))
      [Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex]
      >>> list(G.connected_subgraph_iterator(k=Integer(3), vertices_only=True, exactly_k=True))
      [[1, 2, 3], [1, 2, 4], [2, 3, 4]]
      >>> list(G.connected_subgraph_iterator(k=Integer(2), vertices_only=True))
      [[1], [1, 2], [2], [2, 3], [2, 4], [3], [3, 4], [4]]

      >>> G = DiGraph([(Integer(1), Integer(2)), (Integer(2), Integer(1))])
      >>> list(G.connected_subgraph_iterator())
      [Subgraph of (): Digraph on 1 vertex,
       Subgraph of (): Digraph on 2 vertices,
       Subgraph of (): Digraph on 1 vertex]
      >>> list(G.connected_subgraph_iterator(vertices_only=True))
      [[1], [1, 2], [2]]

      >>> G = graphs.CompleteGraph(Integer(3))
      >>> len(list(G.connected_subgraph_iterator()))
      7
      >>> len(list(G.connected_subgraph_iterator(vertices_only=True)))
      7
      >>> len(list(G.connected_subgraph_iterator(edges_only=True)))
      7
      >>> len(list(G.connected_subgraph_iterator(induced=False)))
      10

      >>> G = DiGraph([(Integer(0), Integer(1)), (Integer(1), Integer(0)), (Integer(1), Integer(2)), (Integer(2), Integer(1))])
      >>> len(list(G.connected_subgraph_iterator()))
      6
      >>> len(list(G.connected_subgraph_iterator(vertices_only=True)))
      6
      >>> len(list(G.connected_subgraph_iterator(edges_only=True)))
      6
      >>> len(list(G.connected_subgraph_iterator(induced=False)))
      18

sage.graphs.base.static_dense_graph.is_strongly_regular(g, parameters=False)

  Check whether the graph is strongly regular.

  A simple graph \(G\) is said to be strongly regular with parameters \((n, k, \lambda, \mu)\) if and only if:

  - \(G\) has \(n\) vertices

  - \(G\) is \(k\)-regular

  - Any two adjacent vertices of \(G\) have \(\lambda\) common neighbors

  - Any two non-adjacent vertices of \(G\) have \(\mu\) common neighbors

  By convention, the complete graphs, the graphs with no edges and the empty graph are not strongly regular.

  See the Wikipedia article Strongly regular graph.

  INPUT:

  - `parameters` – boolean (default: `False`); whether to return the quadruple \((n, k, \lambda, \mu)\). If `parameters = False` (default), this method only returns `True` and `False` answers. If `parameters = True`, the `True` answers are replaced by quadruples \((n, k, \lambda, \mu)\). See definition above.

  EXAMPLES:

  Petersen’s graph is strongly regular:

  Sage

      sage: g = graphs.PetersenGraph()
      sage: g.is_strongly_regular()
      True
      sage: g.is_strongly_regular(parameters=True)
      (10, 3, 0, 1)

  Python

      >>> from sage.all import *
      >>> g = graphs.PetersenGraph()
      >>> g.is_strongly_regular()
      True
      >>> g.is_strongly_regular(parameters=True)
      (10, 3, 0, 1)

  And Clebsch’s graph is too:

  Sage

      sage: g = graphs.ClebschGraph()
      sage: g.is_strongly_regular()
      True
      sage: g.is_strongly_regular(parameters=True)
      (16, 5, 0, 2)

  [Python]

      >>> from sage.all import *
      >>> g = graphs.ClebschGraph()
      >>> g.is_strongly_regular()
      True
      >>> g.is_strongly_regular(parameters=True)
      (16, 5, 0, 2)

  But Chvatal’s graph is not:

  Sage

      sage: g = graphs.ChvatalGraph()
      sage: g.is_strongly_regular()
      False

  Python

      >>> from sage.all import *
      >>> g = graphs.ChvatalGraph()
      >>> g.is_strongly_regular()
      False

  Complete graphs are not strongly regular. (Issue #14297)

  Sage

      sage: g = graphs.CompleteGraph(5)
      sage: g.is_strongly_regular()
      False

  [Python]

      >>> from sage.all import *
      >>> g = graphs.CompleteGraph(Integer(5))
      >>> g.is_strongly_regular()
      False

  Completements of complete graphs are not strongly regular:

  Sage

      sage: g = graphs.CompleteGraph(5).complement()
      sage: g.is_strongly_regular()
      False

  Python

      >>> from sage.all import *
      >>> g = graphs.CompleteGraph(Integer(5)).complement()
      >>> g.is_strongly_regular()
      False

  The empty graph is not strongly regular:

  Sage

      sage: g = graphs.EmptyGraph()
      sage: g.is_strongly_regular()
      False

  [Python]

      >>> from sage.all import *
      >>> g = graphs.EmptyGraph()
      >>> g.is_strongly_regular()
      False

  If the input graph has loops or multiedges an exception is raised:

  Sage

      sage: Graph([(1,1),(2,2)],loops=True).is_strongly_regular()
      Traceback (most recent call last):
      ...
      ValueError: This method is not known to work on graphs with
      loops. Perhaps this method can be updated to handle them, but in the
      meantime if you want to use it please disallow loops using
      allow_loops().

      sage: Graph([(1,2),(1,2)],multiedges=True).is_strongly_regular()
      Traceback (most recent call last):
      ...
      ValueError: This method is not known to work on graphs with
      multiedges. Perhaps this method can be updated to handle them, but in
      the meantime if you want to use it please disallow multiedges using
      allow_multiple_edges().

  Python

      >>> from sage.all import *
      >>> Graph([(Integer(1),Integer(1)),(Integer(2),Integer(2))],loops=True).is_strongly_regular()
      Traceback (most recent call last):
      ...
      ValueError: This method is not known to work on graphs with
      loops. Perhaps this method can be updated to handle them, but in the
      meantime if you want to use it please disallow loops using
      allow_loops().

      >>> Graph([(Integer(1),Integer(2)),(Integer(1),Integer(2))],multiedges=True).is_strongly_regular()
      Traceback (most recent call last):
      ...
      ValueError: This method is not known to work on graphs with
      multiedges. Perhaps this method can be updated to handle them, but in
      the meantime if you want to use it please disallow multiedges using
      allow_multiple_edges().

sage.graphs.base.static_dense_graph.is_triangle_free(G, certificate=False)

  Check whether \(G\) is triangle free.

  INPUT:

  - `G` – a Sage graph

  - `certificate` – boolean (default: `False`); whether to return a triangle if one is found

  EXAMPLES:

  Sage

      sage: from sage.graphs.base.static_dense_graph import is_triangle_free
      sage: is_triangle_free(graphs.PetersenGraph())
      True
      sage: K4 = graphs.CompleteGraph(4)
      sage: is_triangle_free(K4)
      False
      sage: b, certif = is_triangle_free(K4, certificate=True)
      sage: K4.subgraph(certif).is_clique()
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import is_triangle_free
      >>> is_triangle_free(graphs.PetersenGraph())
      True
      >>> K4 = graphs.CompleteGraph(Integer(4))
      >>> is_triangle_free(K4)
      False
      >>> b, certif = is_triangle_free(K4, certificate=True)
      >>> K4.subgraph(certif).is_clique()
      True

sage.graphs.base.static_dense_graph.triangles_count(G)

  Return the number of triangles containing \(v\), for every \(v\).

  INPUT:

  - `G` – a simple Sage graph

  EXAMPLES:

  Sage

      sage: from sage.graphs.base.static_dense_graph import triangles_count
      sage: triangles_count(graphs.PetersenGraph())
      {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0}
      sage: sum(triangles_count(graphs.CompleteGraph(15)).values()) == 3 * binomial(15, 3)        # needs sage.symbolic
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.base.static_dense_graph import triangles_count
      >>> triangles_count(graphs.PetersenGraph())
      {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0}
      >>> sum(triangles_count(graphs.CompleteGraph(Integer(15))).values()) == Integer(3) * binomial(Integer(15), Integer(3))        # needs sage.symbolic
      True