ISGCI: Information System on Graph Classes and their Inclusions

This module implements an interface to the ISGCI database in Sage.

This database gathers information on graph classes and their inclusions in each other. It also contains information on the complexity of several computational problems.

It is available on the GraphClasses.org website maintained by H.N. de Ridder et al.

How to use it?

Presently, it is possible to use this database through the variables and methods present in the graph_classes object. For instance:

sage: Trees = graph_classes.Tree
sage: Chordal = graph_classes.Chordal
>>> from sage.all import *
>>> Trees = graph_classes.Tree
>>> Chordal = graph_classes.Chordal

Inclusions

It is then possible to check the inclusion of classes inside of others, if the information is available in the database:

sage: Trees <= Chordal
True
>>> from sage.all import *
>>> Trees <= Chordal
True

And indeed, trees are chordal graphs.

The ISGCI database is not all-knowing, and so comparing two classes can return True, False, or Unknown (see the documentation of the Unknown truth value).

An unknown answer to A <= B only means that ISGCI cannot deduce from the information in its database that A is a subclass of B nor that it is not. For instance, ISGCI does not know at the moment that some chordal graphs are not trees:

sage: graph_classes.Chordal <= graph_classes.Tree
Unknown
>>> from sage.all import *
>>> graph_classes.Chordal <= graph_classes.Tree
Unknown

Descriptions

Given a graph class, one can obtain its associated information in the ISGCI database with the description() method:

sage: Chordal.description()
Class of graphs : Chordal
-------------------------
id                             :  gc_32
name                           :  chordal
...
Problems :
-----------
3-Colourability                :  Linear
Clique                         :  Polynomial
Clique cover                   :  Polynomial
...
>>> from sage.all import *
>>> Chordal.description()
Class of graphs : Chordal
-------------------------
id                             :  gc_32
name                           :  chordal
...
Problems :
-----------
3-Colourability                :  Linear
Clique                         :  Polynomial
Clique cover                   :  Polynomial
...

It is possible to obtain the complete list of the classes stored in ISGCI by calling the show_all() method (beware – long output):

sage: graph_classes.show_all()
id        | name                                     | type                 | smallgraph
----------------------------------------------------------------------------------------------------------------------
gc_309    | $K_4$--minor--free                       | base                 |
gc_541    | $N^*$                                    | base                 |
gc_215    | $N^*$--perfect                           | base                 |
gc_5      | $P_4$--bipartite                         | base                 |
gc_3      | $P_4$--brittle                           | base                 |
gc_6      | $P_4$--comparability                     | base                 |
gc_7      | $P_4$--extendible                        | base                 |
...
>>> from sage.all import *
>>> graph_classes.show_all()
id        | name                                     | type                 | smallgraph
----------------------------------------------------------------------------------------------------------------------
gc_309    | $K_4$--minor--free                       | base                 |
gc_541    | $N^*$                                    | base                 |
gc_215    | $N^*$--perfect                           | base                 |
gc_5      | $P_4$--bipartite                         | base                 |
gc_3      | $P_4$--brittle                           | base                 |
gc_6      | $P_4$--comparability                     | base                 |
gc_7      | $P_4$--extendible                        | base                 |
...

Until a proper search method is implemented, this lets one find classes which do not appear in graph_classes.*.

To retrieve a class of graph from its ISGCI ID one may use the get_class() method:

sage: GC = graph_classes.get_class("gc_5")
sage: GC
$P_4$--bipartite graphs
>>> from sage.all import *
>>> GC = graph_classes.get_class("gc_5")
>>> GC
$P_4$--bipartite graphs

Recognition of graphs

The graph classes represented by the ISGCI database can alternatively be used to access recognition algorithms. For instance, in order to check that a given graph is a tree one has the following the options

sage: graphs.PathGraph(5) in graph_classes.Tree
True
>>> from sage.all import *
>>> graphs.PathGraph(Integer(5)) in graph_classes.Tree
True

or:

sage: graphs.PathGraph(5).is_tree()
True
>>> from sage.all import *
>>> graphs.PathGraph(Integer(5)).is_tree()
True

Furthermore, all ISGCI graph classes which are defined by the exclusion of a finite sequence of induced subgraphs benefit from a generic recognition algorithm. For instance

sage: g = graphs.PetersenGraph()
sage: g in graph_classes.ClawFree
False
sage: g.line_graph() in graph_classes.ClawFree
True
>>> from sage.all import *
>>> g = graphs.PetersenGraph()
>>> g in graph_classes.ClawFree
False
>>> g.line_graph() in graph_classes.ClawFree
True

Or directly from ISGCI

sage: gc = graph_classes.get_class("gc_441")
sage: gc
diamond--free graphs
sage: graphs.PetersenGraph() in gc
True
>>> from sage.all import *
>>> gc = graph_classes.get_class("gc_441")
>>> gc
diamond--free graphs
>>> graphs.PetersenGraph() in gc
True

Predefined classes

graph_classes currently predefines the following graph classes

Sage’s view of ISGCI

The database is stored by Sage in two ways.

The classes: the list of all graph classes and their properties is stored in a huge dictionary (see classes()). Below is what Sage knows of gc_249:

sage: graph_classes.classes()['gc_249']        # random
{'problem':
    {'Independent set': 'Polynomial',
     'Treewidth': 'Unknown',
     'Weighted independent set': 'Polynomial',
     'Cliquewidth expression': 'NP-complete',
     'Weighted clique': 'Polynomial',
     'Clique cover': 'Unknown',
     'Domination': 'NP-complete',
     'Clique': 'Polynomial',
     'Colourability': 'NP-complete',
     'Cliquewidth': 'Unbounded',
     '3-Colourability': 'NP-complete',
     'Recognition': 'Linear'},
 'type': 'base',
 'id': 'gc_249',
 'name': 'line'}
>>> from sage.all import *
>>> graph_classes.classes()['gc_249']        # random
{'problem':
    {'Independent set': 'Polynomial',
     'Treewidth': 'Unknown',
     'Weighted independent set': 'Polynomial',
     'Cliquewidth expression': 'NP-complete',
     'Weighted clique': 'Polynomial',
     'Clique cover': 'Unknown',
     'Domination': 'NP-complete',
     'Clique': 'Polynomial',
     'Colourability': 'NP-complete',
     'Cliquewidth': 'Unbounded',
     '3-Colourability': 'NP-complete',
     'Recognition': 'Linear'},
 'type': 'base',
 'id': 'gc_249',
 'name': 'line'}

The class inclusion digraph: Sage remembers the class inclusions through the inclusion digraph (see inclusion_digraph()). Its nodes are ID of ISGCI classes:

sage: d = graph_classes.inclusion_digraph()
sage: d.vertices(sort=True)[-10:]
['gc_990', 'gc_991', 'gc_992', 'gc_993', 'gc_994', 'gc_995', 'gc_996', 'gc_997', 'gc_998', 'gc_999']
>>> from sage.all import *
>>> d = graph_classes.inclusion_digraph()
>>> d.vertices(sort=True)[-Integer(10):]
['gc_990', 'gc_991', 'gc_992', 'gc_993', 'gc_994', 'gc_995', 'gc_996', 'gc_997', 'gc_998', 'gc_999']

An arc from gc1 to gc2 means that gc1 is a superclass of gc2. This being said, not all edges are stored ! To ensure that a given class is included in another one, we have to check whether there is in the digraph a path from the first one to the other:

sage: bip_id = graph_classes.Bipartite._gc_id
sage: perfect_id = graph_classes.Perfect._gc_id
sage: d.has_edge(perfect_id, bip_id)
False
sage: d.distance(perfect_id, bip_id)
2
>>> from sage.all import *
>>> bip_id = graph_classes.Bipartite._gc_id
>>> perfect_id = graph_classes.Perfect._gc_id
>>> d.has_edge(perfect_id, bip_id)
False
>>> d.distance(perfect_id, bip_id)
2

Hence bipartite graphs are perfect graphs. We can see how ISGCI obtains this result

sage: p = d.shortest_path(perfect_id, bip_id)
sage: len(p) - 1
2
sage: print(p)                  # random
['gc_56', 'gc_76', 'gc_69']
sage: for c in p:
....:     print(graph_classes.get_class(c))
perfect graphs
...
bipartite graphs
>>> from sage.all import *
>>> p = d.shortest_path(perfect_id, bip_id)
>>> len(p) - Integer(1)
2
>>> print(p)                  # random
['gc_56', 'gc_76', 'gc_69']
>>> for c in p:
...     print(graph_classes.get_class(c))
perfect graphs
...
bipartite graphs

What ISGCI knows is that perfect graphs contain unimodular graph which contain bipartite graphs. Therefore bipartite graphs are perfect !

Note

The inclusion digraph is NOT ACYCLIC. Indeed, several entries exist in the ISGCI database which represent the same graph class, for instance Perfect graphs and Berge graphs:

sage: graph_classes.inclusion_digraph().is_directed_acyclic()
False
sage: Berge = graph_classes.get_class("gc_274"); Berge
Berge graphs
sage: Perfect = graph_classes.get_class("gc_56"); Perfect
perfect graphs
sage: Berge <= Perfect
True
sage: Perfect <= Berge
True
sage: Perfect == Berge
True
>>> from sage.all import *
>>> graph_classes.inclusion_digraph().is_directed_acyclic()
False
>>> Berge = graph_classes.get_class("gc_274"); Berge
Berge graphs
>>> Perfect = graph_classes.get_class("gc_56"); Perfect
perfect graphs
>>> Berge <= Perfect
True
>>> Perfect <= Berge
True
>>> Perfect == Berge
True

Information for developers

  • The database is loaded not so large, but it is still preferable to only load it on demand. This is achieved through the cached methods classes() and inclusion_digraph().

  • Upon the first access to the database, the information is extracted from the XML file and stored in the cache of three methods:

    • sage.graphs.isgci._classes (dictionary)

    • sage.graphs.isgci._inclusions (list of dictionaries)

    • sage.graphs.isgci._inclusion_digraph (DiGraph)

    Note that the digraph is only built if necessary (for instance if the user tries to compare two classes).

Todo

Technical things:

  • Query the database for non-inclusion results so that comparisons can return False, and implement strict inclusions.

  • Implement a proper search method for the classes not listed in graph_classes

    See also

    sage.graphs.isgci.show_all().

  • Some of the graph classes appearing in graph_classes already have a recognition algorithm implemented in Sage. It would be so nice to be able to write g in Trees, g in Perfect, g in Chordal, … :-)

Long-term stuff:

  • Implement simple accessors for all the information in the ISGCI database (as can be done from the website)

  • Implement intersection of graph classes

  • Write generic recognition algorithms for specific classes (when a graph class is defined by the exclusion of subgraphs, one can write a generic algorithm checking the existence of each of the graphs, and this method already exists in Sage).

  • Improve the performance of Sage’s graph library by letting it take advantage of the properties of graph classes. For example, Graph.independent_set() could use the library to detect that a given graph is, say, a tree or a planar graph, and use a specialized algorithm for finding an independent set.

AUTHORS:

  • H.N. de Ridder et al. (ISGCI database)

  • Nathann Cohen (Sage implementation)

Methods

class sage.graphs.isgci.GraphClass(name, gc_id, recognition_function=None)[source]

Bases: SageObject, CachedRepresentation

An instance of this class represents a Graph Class, matching some entry in the ISGCI database.

EXAMPLES:

Testing the inclusion of two classes:

sage: Chordal = graph_classes.Chordal
sage: Trees = graph_classes.Tree
sage: Trees <= Chordal
True
sage: Chordal <= Trees
Unknown
>>> from sage.all import *
>>> Chordal = graph_classes.Chordal
>>> Trees = graph_classes.Tree
>>> Trees <= Chordal
True
>>> Chordal <= Trees
Unknown
description()[source]

Print the information of ISGCI about the current class.

EXAMPLES:

sage: graph_classes.Chordal.description()
Class of graphs : Chordal
-------------------------
id                             :  gc_32
name                           :  chordal
...
Problems :
-----------
3-Colourability                :  Linear
Clique                         :  Polynomial
Clique cover                   :  Polynomial
...
Recognition                    :  Linear
...
>>> from sage.all import *
>>> graph_classes.Chordal.description()
Class of graphs : Chordal
-------------------------
id                             :  gc_32
name                           :  chordal
...
Problems :
-----------
3-Colourability                :  Linear
Clique                         :  Polynomial
Clique cover                   :  Polynomial
...
Recognition                    :  Linear
...
forbidden_subgraphs()[source]

Return the list of forbidden induced subgraphs defining the class.

If the graph class is not defined by a finite list of forbidden induced subgraphs, None is returned instead.

EXAMPLES:

sage: graph_classes.Perfect.forbidden_subgraphs()
sage: gc = graph_classes.get_class('gc_62')
sage: gc
claw--free graphs
sage: gc.forbidden_subgraphs()
[Graph on 4 vertices]
sage: gc.forbidden_subgraphs()[0].is_isomorphic(graphs.ClawGraph())
True
>>> from sage.all import *
>>> graph_classes.Perfect.forbidden_subgraphs()
>>> gc = graph_classes.get_class('gc_62')
>>> gc
claw--free graphs
>>> gc.forbidden_subgraphs()
[Graph on 4 vertices]
>>> gc.forbidden_subgraphs()[Integer(0)].is_isomorphic(graphs.ClawGraph())
True
class sage.graphs.isgci.GraphClasses[source]

Bases: UniqueRepresentation

classes()[source]

Return the graph classes, as a dictionary.

Upon the first call, this loads the database from the local XML file. Subsequent calls are cached.

EXAMPLES:

sage: t = graph_classes.classes()
sage: type(t)
<... 'dict'>
sage: sorted(t["gc_151"].keys())
['id', 'name',... 'problem',... 'type']
sage: t["gc_151"]['name']
'cograph'
sage: t["gc_151"]['problem']['Clique']
{'complexity': 'Linear'}
>>> from sage.all import *
>>> t = graph_classes.classes()
>>> type(t)
<... 'dict'>
>>> sorted(t["gc_151"].keys())
['id', 'name',... 'problem',... 'type']
>>> t["gc_151"]['name']
'cograph'
>>> t["gc_151"]['problem']['Clique']
{'complexity': 'Linear'}
get_class(id)[source]

Return the class corresponding to the given id in the ISGCI database.

INPUT:

  • id – string; the desired class’ ID

See also

show_all()

EXAMPLES:

With an existing id:

sage: Cographs = graph_classes.get_class("gc_151")
sage: Cographs
cograph graphs
>>> from sage.all import *
>>> Cographs = graph_classes.get_class("gc_151")
>>> Cographs
cograph graphs

With a wrong id:

sage: graph_classes.get_class(-1)
Traceback (most recent call last):
...
ValueError: The given class id does not exist in the ISGCI database. Is the db too old ? You can update it with graph_classes.update_db().
>>> from sage.all import *
>>> graph_classes.get_class(-Integer(1))
Traceback (most recent call last):
...
ValueError: The given class id does not exist in the ISGCI database. Is the db too old ? You can update it with graph_classes.update_db().
inclusion_digraph()[source]

Return the class inclusion digraph.

Upon the first call, this loads the database from the local XML file. Subsequent calls are cached.

EXAMPLES:

sage: g = graph_classes.inclusion_digraph(); g
Digraph on ... vertices
>>> from sage.all import *
>>> g = graph_classes.inclusion_digraph(); g
Digraph on ... vertices
inclusions()[source]

Return the graph class inclusions.

OUTPUT: list of dictionaries

Upon the first call, this loads the database from the local XML file. Subsequent calls are cached.

EXAMPLES:

sage: t = graph_classes.inclusions()
sage: type(t)
<... 'list'>
sage: t[0]
{'sub': 'gc_1', 'super': 'gc_2'}
>>> from sage.all import *
>>> t = graph_classes.inclusions()
>>> type(t)
<... 'list'>
>>> t[Integer(0)]
{'sub': 'gc_1', 'super': 'gc_2'}
show_all()[source]

Print all graph classes stored in ISGCI.

EXAMPLES:

sage: graph_classes.show_all()
id        | name                                     | type                 | smallgraph
----------------------------------------------------------------------------------------------------------------------
gc_309    | $K_4$--minor--free                       | base                 |
gc_541    | $N^*$                                    | base                 |
gc_215    | $N^*$--perfect                           | base                 |
gc_5      | $P_4$--bipartite                         | base                 |
gc_3      | $P_4$--brittle                           | base                 |
gc_6      | $P_4$--comparability                     | base                 |
gc_7      | $P_4$--extendible                        | base                 |
...
>>> from sage.all import *
>>> graph_classes.show_all()
id        | name                                     | type                 | smallgraph
----------------------------------------------------------------------------------------------------------------------
gc_309    | $K_4$--minor--free                       | base                 |
gc_541    | $N^*$                                    | base                 |
gc_215    | $N^*$--perfect                           | base                 |
gc_5      | $P_4$--bipartite                         | base                 |
gc_3      | $P_4$--brittle                           | base                 |
gc_6      | $P_4$--comparability                     | base                 |
gc_7      | $P_4$--extendible                        | base                 |
...
smallgraphs()[source]

Return a dictionary associating a graph to a graph description string.

Upon the first call, this loads the database from the local XML files. Subsequent calls are cached.

EXAMPLES:

sage: t = graph_classes.smallgraphs()
sage: t['2C_4']
Graph on 8 vertices
sage: t['2K_3 + e']
Graph on 6 vertices
sage: t['fish']
Graph on 6 vertices
sage: t['bull']
Graph on 5 vertices
>>> from sage.all import *
>>> t = graph_classes.smallgraphs()
>>> t['2C_4']
Graph on 8 vertices
>>> t['2K_3 + e']
Graph on 6 vertices
>>> t['fish']
Graph on 6 vertices
>>> t['bull']
Graph on 5 vertices
update_db()[source]

Update the ISGCI database by downloading the latest version from internet.

This method downloads the ISGCI database from the website GraphClasses.org. It then extracts the zip file and parses its XML content.

Depending on the credentials of the user running Sage when this command is run, one attempt is made at saving the result in Sage’s directory so that all users can benefit from it. If the credentials are not sufficient, the XML file are saved instead in the user’s directory (in the SAGE_DB folder).

EXAMPLES:

sage: graph_classes.update_db()  # optional - internet
Database downloaded
>>> from sage.all import *
>>> graph_classes.update_db()  # optional - internet
Database downloaded