# HG changeset patch
# User Nicolas M. Thiery <nthiery@users.sf.net>
# Date 1329571825 3600
# Node ID 93a220ee001973026d6326b12ee5f98ff263632f
# Parent 36cfce1f5e12bc409a3cf23db990224bb4de9bd6
[mq]: trac_11880graph_classesreviewnt.patch
diff git a/sage/graphs/isgci.py b/sage/graphs/isgci.py
a

b


1  1  r""" 
2  2  Information System on Graph Classes and their Inclusions 
3  3  
4   This module deals with anything related to the ISGCI database in Sage. 
 4  This module implements an interface to the `ISGCI <http://www.graphclasses.org/>`_ database in Sage. 
5  5  
6   The ISGCI database gathers information on graph classes and their inclusions in 
7   each other. It also contains information on the complexity of several 
8   computational problems. 
 6  This database gathers information on graph classes and their 
 7  inclusions in each other. It also contains information on the 
 8  complexity of several computational problems. 
9  9  
10   It is available on the `GraphClasses.org website 
11   <http://www.graphclasses.org/>`_ maintained by H.N. de Ridder et al. 
 10  It is available on the `GraphClasses.org <http://www.graphclasses.org/>`_ 
 11  website maintained by H.N. de Ridder et al. 
12  12  
13   How to use it ? 
14    
 13  How to use it? 
 14   
15  15  
16   The current support of the ISGCI database is elementary, and aims at 
17   implementing a pure Sage/Python interface which is easier to deal with than a 
18   XML file. I hope this code will be rewritten many times until it stabilizes :) 
 16  The current limited aim of this module is to provide a pure 
 17  Sage/Python interface which is easier to deal with than a XML file. I 
 18  hope it will be rewritten many times until it stabilizes :) 
19  19  
20  20  Presently, it is possible to use this database through the variables and methods 
21   present in the ``graph_classes`` object. For instance :: 
 21  present in the :obj:`graph_classes <sage.graphs.isgci.GraphClasses>` object. 
 22  For instance:: 
22  23  
23  24  sage: Trees = graph_classes.Tree 
24  25  sage: Chordal = graph_classes.Chordal 
25  26  
26   It is then possible to check the inclusion of classes inside of others, if the 
27   information is available in the db:: 
 27  It is then possible to check the inclusion of classes inside of 
 28  others, if the information is available in the database:: 
28  29  
29  30  sage: Trees <= Chordal 
30  31  True 
31  32  
32   And indeed, the trees are chordal graphs. 
 33  And indeed, trees are chordal graphs. 
33  34  
34  35  .. WARNING:: 
35  36  
36   At the moment, only the *yes* answers can be assumed to be true ! 
 37  At the moment, only *positive* answers are guaranteed correct! 
37  38  
38   ISGCI may not contain the information necessary to prove that a class A is a 
39   subset of a class B, but that does not mean that class A is **NOT** a subset 
40   of B. It just means ISGCI has no idea. Hence, no **NO** answer can be 
41   assumed to be true at the moment. 
42   
43   When asked to compare Trees and Chordal graphs, though, the correct answer 
44   is returned (as ISGCI obviously does not contain any proof that all chordal 
45   graphs are trees, which would be a mistake):: 
 39  A *negative* answer to ``A <= B`` only means that ISGCI can't 
 40  deduce from the information in its database that ``A`` is a 
 41  subclass of ``B`` nor that it is not. For the reverse inclusion 
 42  between Trees and Chordal graphs the answer is correct though:: 
46  43  
47  44  sage: Chordal <= Trees 
48  45  False 
49  46  
 47  TODO: what about using a Troolean? 
 48  
 49  TODO: what about strict inclusion? 
 50  
50  51  Given a graph class, one can obtain its associated information in the 
51  52  ISGCI database with the :meth:`show 
52  53  <sage.graphs.isgci.GraphClass.show>` method:: 
… 
… 
ISGCI database with the :meth:`show 
73  74  3Colourability : Linear 
74  75  Recognition : Linear 
75  76  
76   It is possible to obtain the complete list of the classes stored in ISGCI by 
77   calling the :meth:`show_all <sage.graphs.isgci.show_all>` method 
 77  It is possible to obtain the complete list of the classes stored in 
 78  ISGCI by calling the :func:`sage.graphs.isgci.show_all` function 
78  79  (beware  long output):: 
79  80  
80  81  sage: sage.graphs.isgci.show_all() 
81   ID  name  type  smallgraph 
 82  ID  name  type  smallgraph 
82  83   
83   gc_309  $K_4$minorfree  base  
84   gc_541  $N^*$  base  
85   gc_215  $N^*$perfect  base  
86   gc_5  $P_4$bipartite  base  
87   gc_3  $P_4$brittle  base  
88   gc_6  $P_4$comparability  base  
89   gc_7  $P_4$extendible  base  
 84  gc_309  $K_4$minorfree  base  
 85  gc_541  $N^*$  base  
 86  gc_215  $N^*$perfect  base  
 87  gc_5  $P_4$bipartite  base  
 88  gc_3  $P_4$brittle  base  
 89  gc_6  $P_4$comparability  base  
 90  gc_7  $P_4$extendible  base  
90  91  ... 
91  92  
92   This lets one find a class which does not appear in ``graph_classes`` (and 
93   should really be replaced by a proper search method). Given the ISGCI ID code 
94   corresponding to a classe, the :meth:`get_class_from_id 
95   <sage.graphs.isgci.GraphClasses.get_class_from_id>` method returns the 
96   corresponding graph class:: 
 93  Until a proper search method is implemented, this lets one find 
 94  classes which are not listed in :obj:`graph_classes <sage.graphs.isgci.GraphClasses>`. 
 95  
 96  TODO: get_class_from_id is currently a method of graph_classes whereas 
 97  show_all is a function of this module. Should show_all be moved to 
 98  graph_classes for consistency? 
 99  
 100  To retrieve a class of graph from its ISGCI ID one may use 
 101  :meth:`graph_classes.get_class_from_id <GraphClasses.get_class_from_id>`:: 
97  102  
98  103  sage: GC = graph_classes.get_class_from_id("gc_5") 
99  104  sage: GC 
… 
… 
corresponding graph class:: 
102  107  Predefined classes 
103  108   
104  109  
105   The ``graph_classes.*`` currently lists the following graph classes 
 110  :obj:`graph_classes <sage.graphs.isgci.GraphClasses>` currently lists the following graph classes 
 111  
 112  TODO: use .. seealso:: in all entries of this list? 
106  113  
107  114  .. listtable:: 
108  115  :widths: 20 30 
… 
… 
Information for developpers 
204  211  the ``.sobj`` files in which it is stored and their information can be 
205  212  accessed by the following variables : 
206  213  
207   * ``sage.graphs.isgci.classes`` (dictionary) 
 214  * ``sage.graphs.isgci.classes`` (dictionary) 
208  215  * ``sage.graphs.isgci.inclusions`` (list of dictionaries) 
209  216  * ``sage.graphs.isgci.class_digraph`` (DiGraph) 
210  217  
… 
… 
Information for developpers 
216  223  class represented by `u` is larger (not necessarily strictly) than the one 
217  224  represented by `v`. 
218  225  
 226  
 227  TODO: this is useful information for the user too. Move it out of 
 228  the developpers section? 
 229  
219  230  Though it represents inclusions of sets, this ``DiGraph`` is **NOT 
220   ACYCLIC**. Several entries exist in the ISGCI database whise represent the 
221   same graph class:: 
 231  ACYCLIC**:: 
 232  
 233  sage: sage.graphs.isgci.class_digraph.is_directed_acyclic() 
 234  False 
 235  
 236  Indeed, several entries exist in the ISGCI database which represent 
 237  the same graph class:: 
222  238  
223  239  sage: Berge = graph_classes.get_class_from_id("gc_274"); Berge 
224  240  Graph class : Berge 
… 
… 
Information for developpers 
228  244  True 
229  245  sage: Perfect <= Berge 
230  246  True 
231   
232   (Which is the same as):: 
 247  
 248  In other words:: 
233  249  
234  250  sage: Perfect == Berge 
235  251  True 
236  252  
237   Indeed :: 
 253  .. todo:: 
238  254  
239   sage: sage.graphs.isgci.class_digraph.is_directed_acyclic() 
240   False 
 255  Technical things: 
241  256  
242   ToDo list 
243    
 257  * Implement a proper search method for the classes not listed in 
 258  :obj:`graph_classes <sage.graphs.isgci.GraphClasses>` 
244  259  
245   Technical things : 
 260  .. seealso: :func:`sage.graphs.isgci.show_all`. 
246  261  
247   * Find a proper way to find a graph class which does not appear in 
248   ``graph_classes`` by can be listed by :meth:`show_all 
249   <sage.graphs.isgci.show_all>`. 
 262  * Some of the graph classes appearing in :obj:`graph_classes 
 263  <sage.graphs.isgci.GraphClasses>` already have a recognition 
 264  algorithm implemented in Sage. It would be so nice to be able to 
 265  write ``g in Trees``, ``g in Perfect``, ``g in Chordal``, ... `:)` 
250  266  
251   * Some of the graph classes appearing in ``graph_classes.*`` already have a 
252   recognition algorithm in Sage. It would be so nice to be able to write ``g in 
253   Trees``, ``g in Perfect``, ``g in Chordal``, ... `:)` 
 267  Longterm stuff: 
254  268  
255   Longterm stuff : 
 269  * Implement simple accessors for all the information in the ISGCI 
 270  database (as can be done from the website) 
256  271  
257   * To be able to ask questions to ISGCI from the inside of Sage, easily enough, 
258   related to any information it contains (for instance everything that can be 
259   done through the website) 
 272  * Implement intersection of graph classes 
260  273  
261   * Write generic recognition algorithms for specific classes (when a graph class 
262   is defined by the exclusion of subgraphs, one can write a generic algorithm 
263   checking the existence of each of the graphs, and this method already exists 
264   in Sage). 
 274  * Write generic recognition algorithms for specific classes (when a graph class 
 275  is defined by the exclusion of subgraphs, one can write a generic algorithm 
 276  checking the existence of each of the graphs, and this method already exists 
 277  in Sage). 
265  278  
266   * Modify our algorithms so that they could use the properties of a graph class 
267   to improve their performances. Right now, the same algorithm is used to find 
268   an independent set in a tree, in a planar graph, or any other graph. 
 279  * Improve the performance of Sage's graph library by letting it 
 280  take advantage of the properties of graph classes. For example, 
 281  :meth:`Graph.independent_set` could use the library to detect 
 282  that a given graph is, say, a tree or a planar graph, and use a 
 283  specialized algorithm for finding an independent set. 
269  284  
270  285  
271  286  AUTHORS: 
272  287   
273  288  
274  289  * H.N. de Ridder et al. (ISGCI database) 
275   * Nathann Cohen (Sage implementation) 
 290  * Nathann Cohen (Sage implementation) 
276  291  
277  292  Methods 
278  293   
… 
… 
class_digraph = None 
284  299  
285  300  
286  301  #***************************************************************************** 
287   # Copyright (C) 2011 Nathann Cohen <nathann.cohen@gmail.com> 
 302  # Copyright (C) 2011 Nathann Cohen <nathann.cohen@gmail.com> 
288  303  # 
289  304  # Distributed under the terms of the GNU General Public License (GPL) 
290  305  # http://www.gnu.org/licenses/ 
… 
… 
def _create_db(): 
328  343  # want from the XML file and that's more or less all we ask it to do :p 
329  344  
330  345  doc = xml.dom.minidom.parse(SAGE_TMP+_XML_FILE) 
331   
 346  
332  347  classes = {} 
333  348  
334  349  giveme = lambda x,y : str(x.getAttribute(y)) 
… 
… 
def _create_db(): 
358  373  if giveme(node, "type"): 
359  374  Dl["type"] = giveme(node, "type") 
360  375  
361   
 376  
362  377  classes[giveme(node, "id")] = Dl 
363  378  
364  379  inclusions = [] 
… 
… 
def _create_db(): 
366  381  Dl = {} 
367  382  for name in ["proper", "confidence", "super", "sub"]: 
368  383  if giveme(node, name): 
369   Dl[name] = giveme(node, name) 
 384  Dl[name] = giveme(node, name) 
370  385  
371  386  for node2 in node.childNodes: 
372  387  name = str(node2.nodeName) 
… 
… 
def _create_db(): 
379  394  
380  395  def update_db(verbose = False): 
381  396  r""" 
382   Updates the current version of the ISGCI database by download it from internet. 
 397  Updates the ISGCI database by downloading the latest version from internet. 
383  398  
384  399  This method downloads the ISGCI database from the website `GraphClasses.org 
385   <http://www.graphclasses.org/>`_. 
 400  <http://www.graphclasses.org/>`_. 
386  401  
387   It then extracts the zip file and parses its XML content, which is stored as 
388   two .sobj files, the first one representing the graph classes, and the 
389   second representing their inclusions. 
 402  It then extracts the zip file and parses its XML content, which is 
 403  stored as two ``.sobj`` files, the first one representing the 
 404  graph classes, and the second representing their inclusions. 
390  405  
391   Depending on the rights of the user running Sage when this command is run, 
392   one attempt is made at saving its result in Sage's directory so that all 
393   users would benefit from it. If the rights are not sufficient, the .sobj 
394   files are saved to the current user's directory, so that he will be the only 
395   one to benefit from the update. 
 406  Depending on the credentials of the user running Sage when this 
 407  command is run, one attempt is made at saving the result in Sage's 
 408  directory so that all users can benefit from it. If the 
 409  credentials are not sufficient, the ``.sobj`` files are saved 
 410  instead in the user's directory. #TODO: be more specific about where in the user's directory? 
396  411  
397  412  INPUT: 
398  413  
399    ``verbose`` (boolean)  whether to output information of the update 
400   process. 
 414   ``verbose``  a boolean (default: False); whether to output 
 415  information of the update process 
401  416  
402  417  EXAMPLE:: 
403  418  
… 
… 
def update_db(verbose = False): 
405  420  """ 
406  421  from sage.misc.misc import SAGE_TMP, SAGE_ROOT, SAGE_LOCAL, SAGE_DB 
407  422  global classes, inclusions, class_digraph 
408   
 423  
409  424  try: 
410  425  _download_db() 
411  426  except: 
 427  # TODO: as such, the user won't get the, probably more 
 428  # informative, error message thrown by download_db. What about 
 429  # not catching it? Or at least retransmitting the error 
 430  # message? 
412  431  raise Exception("There has been a problem while downloading or unzipping the ISGCI database O_o") 
413  432  
414  433  
… 
… 
def update_db(verbose = False): 
419  438  
420  439  if verbose: 
421  440  print "XML file converted to Python dictionaries" 
422   
 441  
423  442  
424  443  from sage.all import save 
425   
 444  
426  445  # Trying to save to Sage's directory 
427  446  try: 
428  447  save(classes, SAGE_ROOT+'/data/graphs/isgci_classes.sobj') 
… 
… 
def update_db(verbose = False): 
434  453  
435  454  return 
436  455  except IOError: 
 456  if verbose: 
 457  # TODO: improve! 
 458  print "Could not save save database in "+SAGE_ROOT+'/data/graphs/' 
437  459  pass 
438  460  
439  461  # Trying to save to the user's home directory 
… 
… 
def update_db(verbose = False): 
449  471  except IOError: 
450  472  pass 
451  473  
 474  # TODO: as above: don't hide the more informative error message 
 475  # from the system 
 476  
452  477  # Gloops ! 
453  478  raise Exception("Sage is unable to write the files to your"+ 
454  479  "computer ! This shouldn't have happened. "+ 
… 
… 
def get_ISGCI(): 
465  490  
466  491  A pair ``(classes, inclusions)`` where ``classes`` is a dict of dict, and 
467  492  ``inclusions`` is a list of dicts. 
468   
 493  
469  494  .. NOTE:: 
470  495  
471   This method returns the data contained in the most recent version of the 
472   ISGCI database present on the computer. See ``update_db`` to update it. 
 496  This method returns the data contained in the most recent ISGCI database 
 497  present on the computer. See :func:`update_db` to update the latter. 
473  498  
474  499  EXAMPLE:: 
475  500  
… 
… 
def get_ISGCI(): 
480  505  from sage.all import save, load 
481  506  from sage.misc.misc import SAGE_TMP, SAGE_ROOT, SAGE_LOCAL, SAGE_DB 
482  507  
 508  # TODO: or systematically use the user's version if it exists, 
 509  # throwing a warning if it is not the most recent? 
 510  
483  511  try: 
484  512  open(SAGE_DB+"/isgci_classes.sobj") 
485  513  
486  514  # Which copy is the most recent on the disk ? 
487  515  if (os.path.getmtime(SAGE_DB+"/isgci_classes.sobj") > 
488  516  os.path.getmtime(SAGE_ROOT+"/data/graphs/isgci_classes.sobj")): 
489   
 517  
490  518  classes = load(SAGE_DB+"/isgci_classes.sobj") 
491  519  inclusions = load(SAGE_DB+"/isgci_inclusions.sobj") 
492  520  
… 
… 
def show_all(): 
508  536  EXAMPLE:: 
509  537  
510  538  sage: sage.graphs.isgci.show_all() 
511   ID  name  type  smallgraph 
 539  ID  name  type  smallgraph 
512  540   
513   gc_309  $K_4$minorfree  base  
514   gc_541  $N^*$  base  
515   gc_215  $N^*$perfect  base  
516   gc_5  $P_4$bipartite  base  
517   gc_3  $P_4$brittle  base  
518   gc_6  $P_4$comparability  base  
519   gc_7  $P_4$extendible  base  
 541  gc_309  $K_4$minorfree  base  
 542  gc_541  $N^*$  base  
 543  gc_215  $N^*$perfect  base  
 544  gc_5  $P_4$bipartite  base  
 545  gc_3  $P_4$brittle  base  
 546  gc_6  $P_4$comparability  base  
 547  gc_7  $P_4$extendible  base  
520  548  ... 
521  549  """ 
522  550  global classes 
… 
… 
def show_all(): 
553  581  
554  582  # Entries 
555  583  for entry in classes_list: 
556   ID = entry.get("ID","") 
 584  ID = entry.get("ID","") 
557  585  name = entry.get("name","") 
558  586  type = entry.get("type","") 
559  587  smallgraph = entry.get("smallgraph","") 
… 
… 
def _build_class_digraph_if_not_built(): 
589  617  continue 
590  618  
591  619  class_digraph.add_edge(edge['super'], edge['sub']) 
592   
 620  
593  621  inclusions = [] 
594  622  
 623  # TODO: maybe inherit from SageObject? 
595  624  class GraphClass: 
596  625  r""" 
597  626  An instance of this class represents a Graph Class, matching some entry in 
… 
… 
class GraphClass: 
700  729  if value != "": 
701  730  print "{0:30} : ".format(key), 
702  731  print value 
703   
 732  
704  733  
705  734  
706  735  class GraphClasses: 
… 
… 
class GraphClasses: 
722  751  Traceback (most recent call last): 
723  752  ... 
724  753  ValueError: The given class ID does not exist in the ISGCI database. Is the db too old ? You can update it with sage.graphs.isgci.update_db(). 
 754  
 755  
 756  TODO: rename ot ``graph_classes.get_class(id = "gc_151")``? 
 757  or even ``graph_classes.Class(id = "gc_151")``? 
725  758  """ 
726  759  global classes 
727  760  _load_ISGCI_if_not_loaded() 
… 
… 
graph_classes.Split = GraphClass("Split" 
764  797  graph_classes.Tree = GraphClass("Tree", "gc_342") 
765  798  graph_classes.UnitDisk = GraphClass("UnitDisk", "gc_389") 
766  799  graph_classes.UnitInterval = GraphClass("UnitInterval", "gc_299") 
767   