# HG changeset patch
# User Nicolas M. Thiery <nthiery@users.sf.net>
# Date 1329571825 -3600
# Node ID 93a220ee001973026d6326b12ee5f98ff263632f
# Parent 36cfce1f5e12bc409a3cf23db990224bb4de9bd6
[mq]: trac_11880-graph_classes-review-nt.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 | 3-Colourability : 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$--minor--free | 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$--minor--free | 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 | .. list-table:: |
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 | Long-term stuff: |
254 | 268 | |
255 | | Long-term 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$--minor--free | 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$--minor--free | 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 | | |