Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/graph_database.py
8815 views
1
"""
2
Graph database
3
4
INFO:
5
6
This module implements classes (GraphDatabase, GraphQuery,
7
GenericGraphQuery) for interfacing with the sqlite database
8
graphs.db.
9
10
The GraphDatabase class interfaces with the sqlite database
11
graphs.db. It is an immutable database that inherits from
12
SQLDatabase (see sage.databases.database.py).
13
14
The database contains all unlabeled graphs with 7 or fewer nodes.
15
This class will also interface with the optional database package
16
containing all unlabeled graphs with 8 or fewer nodes. The
17
database(s) consists of five tables, and has the structure given by
18
the function graph_info. (For a full description including column
19
data types, create a GraphDatabase instance and call the method
20
get_skeleton).
21
22
AUTHORS:
23
24
- Emily A. Kirkman (2008-09-20): first version of interactive queries,
25
cleaned up code and generalized many elements to
26
sage.databases.database.py
27
28
- Emily A. Kirkman (2007-07-23): inherits GenericSQLDatabase, also
29
added classes: GraphQuery and GenericGraphQuery
30
31
- Emily A. Kirkman (2007-05-11): initial sqlite version
32
33
- Emily A. Kirkman (2007-02-13): initial version (non-sqlite)
34
35
REFERENCES:
36
37
- Data provided by Jason Grout (Brigham Young University). [Online]
38
Available: http://artsci.drake.edu/grout/graphs/
39
"""
40
41
################################################################################
42
# Copyright (C) 2007 Emily A. Kirkman
43
#
44
#
45
# Distributed under the terms of the GNU General Public License (GPL)
46
# http://www.gnu.org/licenses/
47
################################################################################
48
49
import graph
50
import os,re
51
from sage.rings.integer import Integer
52
from sqlite3 import dbapi2 as sqlite # if anyone would like to explain why dbapi2...
53
from sage.databases.sql_db import SQLDatabase, SQLQuery
54
from sage.misc.misc import SAGE_SHARE
55
from sage.graphs.graph import Graph
56
dblocation = os.path.join(SAGE_SHARE,'graphs','graphs.db')
57
58
def degseq_to_data(degree_sequence):
59
"""
60
Takes a degree sequence list (of Integers) and converts to a sorted
61
(max-min) integer data type, as used for faster access in the
62
underlying database.
63
64
EXAMPLE::
65
66
sage: from sage.graphs.graph_database import degseq_to_data
67
sage: degseq_to_data([2,2,3,1])
68
3221
69
"""
70
degree_sequence.sort()
71
return sum(degree_sequence[i]*10**i for i in range(len(degree_sequence)))
72
73
def data_to_degseq(data, graph6=None):
74
"""
75
Takes the database integer data type (one digit per vertex
76
representing its degree, sorted high to low) and converts it to
77
degree sequence list. The graph6 identifier is required for all
78
graphs with no edges, so that the correct number of zeros will be
79
returned.
80
81
EXAMPLE::
82
83
sage: from sage.graphs.graph_database import data_to_degseq
84
sage: data_to_degseq(3221)
85
[1, 2, 2, 3]
86
sage: data_to_degseq(0,'D??')
87
[0, 0, 0, 0, 0]
88
"""
89
degseq = Integer(data).digits(10)
90
if not degseq:
91
# compute number of 0's in list from graph6 string
92
from sage.graphs.generic_graph_pyx import N_inverse
93
return N_inverse(str(graph6))[0]*[0]
94
else:
95
return degseq
96
97
def graph6_to_plot(graph6):
98
"""
99
Constructs a graph from a graph6 string and returns a Graphics
100
object with arguments preset for show function.
101
102
EXAMPLE::
103
104
sage: from sage.graphs.graph_database import graph6_to_plot
105
sage: type(graph6_to_plot('D??'))
106
<class 'sage.plot.graphics.Graphics'>
107
"""
108
g = Graph(str(graph6))
109
return g.plot(layout='circular',vertex_size=30,vertex_labels=False,graph_border=False)
110
111
def subgraphs_to_query(subgraphs, db):
112
"""
113
Constructs and returns a GraphQuery object respecting the special
114
input required for the induced_subgraphs parameter. This input can
115
be an individual graph6 string (in which case it is evaluated
116
without the use of this method) or a list of strings. In the latter
117
case, the list should be of one of the following two formats: 1.
118
['one_of',String,...,String] Will search for graphs containing a
119
subgraph isomorphic to any of the graph6 strings in the list. 2.
120
['all_of',String,...,String] Will search for graphs containing a
121
subgraph isomorphic to each of the graph6 strings in the list.
122
123
This is a helper method called by the GraphQuery constructor to
124
handle this special format. This method should not be used on its
125
own because it doesn't set any display columns in the query string,
126
causing a failure to fetch the data when run.
127
128
EXAMPLE::
129
130
sage: from sage.graphs.graph_database import subgraphs_to_query
131
sage: gd = GraphDatabase()
132
sage: q = subgraphs_to_query(['all_of','A?','B?','C?'],gd)
133
sage: q.get_query_string()
134
'SELECT ,,,,, FROM misc WHERE ( ( misc.induced_subgraphs regexp ? ) AND (
135
misc.induced_subgraphs regexp ? ) ) AND ( misc.induced_subgraphs regexp ? )'
136
"""
137
q = GraphQuery(graph_db=db,induced_subgraphs=subgraphs[1])
138
if subgraphs[0] == 'all_of':
139
for i in range(len(subgraphs))[2:]:
140
q.intersect(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]),in_place=True)
141
elif subgraphs[0] == 'one_of':
142
for i in range(len(subgraphs))[2:]:
143
q.union(GraphQuery(graph_db=db, induced_subgraphs=subgraphs[i]),in_place=True)
144
else:
145
raise KeyError, 'Unable to initiate query: Illegal input format for induced_subgraphs.'
146
return q
147
148
# tables columns input data type sqlite data type
149
# -----------------------------------------------------------------------------
150
aut_grp = ['aut_grp_size', # Integer INTEGER
151
'num_orbits', # Integer INTEGER
152
'num_fixed_points', # Integer INTEGER
153
'vertex_transitive', # bool BOOLEAN
154
'edge_transitive'] # bool BOOLEAN
155
degrees = ['degree_sequence', # list INTEGER (see degseq_to_data module function)
156
'min_degree', # Integer INTEGER
157
'max_degree', # Integer INTEGER
158
'average_degree', # Real REAL
159
'degrees_sd', # Real REAL
160
'regular'] # bool BOOLEAN
161
misc = ['vertex_connectivity', # Integer INTEGER
162
'edge_connectivity', # Integer INTEGER
163
'num_components', # Integer INTEGER
164
'girth', # Integer INTEGER
165
'radius', # Integer INTEGER
166
'diameter', # Integer INTEGER
167
'clique_number', # Integer INTEGER
168
'independence_number', # Integer INTEGER
169
'num_cut_vertices', # Integer INTEGER
170
'min_vertex_cover_size', # Integer INTEGER
171
'num_spanning_trees', # Integer INTEGER
172
'induced_subgraphs'] # String STRING
173
spectrum = ['spectrum', # String STRING
174
'min_eigenvalue', # Real REAL
175
'max_eigenvalue', # Real REAL
176
'eigenvalues_sd', # Real REAL
177
'energy'] # Real REAL
178
graph_data=['complement_graph6', # String STRING
179
'eulerian', # bool BOOLEAN
180
'graph6', # String STRING
181
'lovasz_number', # Real REAL
182
'num_cycles', # Integer INTEGER
183
'num_edges', # Integer INTEGER
184
'num_hamiltonian_cycles', # Integer INTEGER
185
'num_vertices', # Integer INTEGER
186
'perfect', # bool BOOLEAN
187
'planar'] # bool BOOLEAN
188
189
valid_kwds = aut_grp + degrees + misc + spectrum + graph_data
190
191
def graph_db_info(tablename=None):
192
"""
193
Returns a dictionary of allowed table and column names.
194
195
INPUT:
196
197
198
- ``tablename`` - restricts the output to a single
199
table
200
201
202
EXAMPLE::
203
204
sage: graph_db_info().keys()
205
['graph_data', 'degrees', 'spectrum', 'misc', 'aut_grp']
206
207
::
208
209
sage: graph_db_info(tablename='graph_data')
210
['complement_graph6',
211
'eulerian',
212
'graph6',
213
'lovasz_number',
214
'num_cycles',
215
'num_edges',
216
'num_hamiltonian_cycles',
217
'num_vertices',
218
'perfect',
219
'planar']
220
"""
221
info = {'graph_data': graph_data,
222
'aut_grp': aut_grp,
223
'degrees': degrees,
224
'misc': misc,
225
'spectrum': spectrum}
226
if tablename is not None:
227
info = info[tablename]
228
return info
229
230
class GenericGraphQuery(SQLQuery):
231
232
def __init__(self, query_string, database=None, param_tuple=None):
233
"""
234
A query for a GraphDatabase.
235
236
INPUT:
237
238
239
- ``database`` - the GraphDatabase instance to query
240
(if None then a new instance is created)
241
242
- ``query_string`` - a string representing the SQL
243
query
244
245
- ``param_tuple`` - a tuple of strings - what to
246
replace question marks in query_string with (optional, but a good
247
idea)
248
249
250
.. note::
251
252
This query class is generally intended for developers and
253
more advanced users. It allows you to execute any query,
254
and so may be considered unsafe.
255
256
See GraphDatabase class docstrings or enter::
257
258
sage: G = GraphDatabase()
259
sage: G.get_skeleton()
260
{...
261
262
to see the underlying structure of the database. Also see
263
SQLQuery in sage.databases.database for more info and a
264
tutorial.
265
266
A piece of advice about '?' and param_tuple: It is generally
267
considered safer to query with a '?' in place of each value
268
parameter, and using a second argument (a tuple of strings) in a
269
call to the sqlite database. Successful use of the param_tuple
270
argument is exemplified::
271
272
sage: G = GraphDatabase()
273
sage: q = 'select graph_id,graph6,num_vertices,num_edges from graph_data where graph_id<=(?) and num_vertices=(?)'
274
sage: param = (22,5)
275
sage: Q = SQLQuery(G,q,param)
276
sage: Q.show()
277
graph_id graph6 num_vertices num_edges
278
--------------------------------------------------------------------------------
279
18 D?? 5 0
280
19 D?C 5 1
281
20 D?K 5 2
282
21 D@O 5 2
283
22 D?[ 5 3
284
"""
285
if database == None: database = GraphDatabase()
286
if not isinstance(database, GraphDatabase):
287
raise TypeError('%s is not a valid GraphDatabase'%database)
288
SQLQuery.__init__(self,database,query_string,param_tuple)
289
290
class GraphQuery(GenericGraphQuery):
291
292
def __init__(self, graph_db=None, query_dict=None, display_cols=None, **kwds):
293
"""
294
A query for an instance of GraphDatabase. This class nicely wraps
295
the SQLQuery class located in sage.databases.database.py to make
296
the query constraints intuitive and with as many pre-definitions as
297
possible. (i.e.: since it has to be a GraphDatabase, we already know
298
the table structure and types; and since it is immutable, we can
299
treat these as a guarantee).
300
301
.. note::
302
303
SQLQuery functions are available for GraphQuery. See
304
sage.dataabases.database.py for more details.
305
306
INPUT:
307
308
309
- ``graph_db`` - The GraphDatabase instance to apply
310
the query to. (If None, then a new instance is created).
311
312
- ``query_dict`` - A dictionary specifying the query
313
itself. Format is: 'table_name': 'tblname', 'display_cols':
314
['col1', 'col2'], 'expression':[col, operator, value] If not None,
315
query_dict will take precedence over all other arguments.
316
317
- ``display_cols`` - A list of column names (strings)
318
to display in the result when running or showing a query.
319
320
- ``kwds`` - The columns of the database are all
321
keywords. For a database table/column structure dictionary, call
322
graph_db_info. Keywords accept both single values and lists of
323
length 2. The list allows the user to specify an expression other
324
than equality. Valid expressions are strings, and for numeric
325
values (i.e. Reals and Integers) are: '=','','','=','='. String
326
values also accept 'regexp' as an expression argument. The only
327
keyword exception to this format is induced_subgraphs, which
328
accepts one of the following options: 1.
329
['one_of',String,...,String] Will search for graphs containing a
330
subgraph isomorphic to any of the graph6 strings in the list. 2.
331
['all_of',String,...,String] Will search for graphs containing a
332
subgraph isomorphic to each of the graph6 strings in the list.
333
334
335
EXAMPLES::
336
337
sage: Q = GraphQuery(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],min_degree=1)
338
sage: Q.number_of()
339
35
340
sage: Q.show()
341
Graph6 Num Vertices Degree Sequence
342
------------------------------------------------------------
343
A_ 2 [1, 1]
344
BW 3 [1, 1, 2]
345
CF 4 [1, 1, 1, 3]
346
CK 4 [1, 1, 1, 1]
347
CL 4 [1, 1, 2, 2]
348
CN 4 [1, 2, 2, 3]
349
D?{ 5 [1, 1, 1, 1, 4]
350
D@s 5 [1, 1, 1, 2, 3]
351
D@{ 5 [1, 1, 2, 2, 4]
352
DBg 5 [1, 1, 2, 2, 2]
353
DBk 5 [1, 1, 2, 3, 3]
354
DIk 5 [1, 2, 2, 2, 3]
355
DK[ 5 [1, 2, 2, 2, 3]
356
D_K 5 [1, 1, 1, 1, 2]
357
D`K 5 [1, 1, 2, 2, 2]
358
E?Bw 6 [1, 1, 1, 1, 1, 5]
359
E?Fg 6 [1, 1, 1, 1, 2, 4]
360
E?N? 6 [1, 1, 1, 1, 2, 2]
361
E?NG 6 [1, 1, 1, 1, 3, 3]
362
E@FG 6 [1, 1, 1, 2, 2, 3]
363
E@N? 6 [1, 1, 2, 2, 2, 2]
364
E@Q? 6 [1, 1, 1, 1, 1, 1]
365
E@QW 6 [1, 1, 1, 2, 2, 3]
366
E@YO 6 [1, 1, 2, 2, 2, 2]
367
E_?w 6 [1, 1, 1, 1, 1, 3]
368
E_Cg 6 [1, 1, 1, 1, 2, 2]
369
E_Cw 6 [1, 1, 1, 2, 2, 3]
370
E_Ko 6 [1, 1, 2, 2, 2, 2]
371
F??^? 7 [1, 1, 1, 1, 1, 2, 3]
372
F?LCG 7 [1, 1, 1, 1, 2, 2, 2]
373
FK??W 7 [1, 1, 1, 1, 1, 1, 2]
374
FK?GW 7 [1, 1, 1, 1, 2, 2, 2]
375
F_?@w 7 [1, 1, 1, 1, 1, 1, 4]
376
F_?Hg 7 [1, 1, 1, 1, 1, 2, 3]
377
F_?XO 7 [1, 1, 1, 1, 2, 2, 2]
378
"""
379
if graph_db == None: graph_db = GraphDatabase()
380
if query_dict is not None:
381
if query_dict['expression'][0] == 'degree_sequence':
382
query_dict['expression'][3] = degseq_to_data(query_dict['expression'][3])
383
elif query_dict['expression'][0] == 'induced_subgraphs':
384
query_dict['expression'][3] = subgraphs_to_data(query_dict['expression'][3])
385
SQLQuery.__init__(self, graph_db, query_dict)
386
else:
387
# construct a query from the given parameters
388
SQLQuery.__init__(self, graph_db)
389
390
#if display_cols is None:
391
# raise TypeError, 'Nonetype display_cols cannot retrieve data.'
392
393
master_join = {}
394
395
for key in kwds:
396
# check validity
397
if not key in valid_kwds:
398
raise KeyError, '%s is not a valid key for this database.'%str(key)
399
400
# designate a query_dict
401
qdict = {'display_cols': None} # reserve display cols until end
402
# (database.py currently concatenates
403
# them including repeats)
404
405
# set table name
406
if key in graph_data: qdict['table_name'] = 'graph_data'
407
elif key in aut_grp: qdict['table_name'] = 'aut_grp'
408
elif key in degrees: qdict['table_name'] = 'degrees'
409
elif key in misc: qdict['table_name'] = 'misc'
410
elif key in spectrum: qdict['table_name'] = 'spectrum'
411
412
# set expression
413
if not isinstance(kwds[key],list):
414
if key == 'induced_subgraphs':
415
qdict['expression'] = [key, 'regexp', '.*%s.*'%(graph.Graph(kwds[key]).canonical_label()).graph6_string()]
416
else:
417
qdict['expression'] = [key, '=', kwds[key]]
418
elif key == 'degree_sequence':
419
qdict['expression'] = [key, '=', degseq_to_data(kwds[key])]
420
elif key != 'induced_subgraphs':
421
qdict['expression'] = [key] + kwds[key]
422
423
# add key parameter to query
424
join_dict = {qdict['table_name']: ('graph_id', 'graph_id')}
425
if key == 'induced_subgraphs' and isinstance(kwds[key],list):
426
self.intersect(subgraphs_to_query(kwds[key], graph_db),
427
'graph_data', join_dict, in_place=True)
428
else:
429
self.intersect(SQLQuery(graph_db, qdict), 'graph_data', join_dict,in_place=True)
430
431
# include search params (keys) in join clause
432
# again, we exclude graph_data because it is the base table
433
if qdict['table_name'] != 'graph_data':
434
master_join[qdict['table_name']] = ('graph_id', 'graph_id')
435
436
# display columns from each table
437
aut_grp_disp = ['aut_grp']
438
degrees_disp = ['degrees']
439
misc_disp = ['misc']
440
spectrum_disp = ['spectrum']
441
graph_data_disp = ['graph_data']
442
443
disp_tables = [aut_grp_disp, degrees_disp, misc_disp, spectrum_disp]
444
# graph_data intentionally left out because it is always called
445
446
# organize display
447
if display_cols is not None:
448
for col in display_cols:
449
if col in graph_data: graph_data_disp.append(col)
450
elif col in aut_grp: aut_grp_disp.append(col)
451
elif col in degrees: degrees_disp.append(col)
452
elif col in misc: misc_disp.append(col)
453
elif col in spectrum: spectrum_disp.append(col)
454
455
# finish filling master join with display tables
456
for tab in disp_tables:
457
if len(tab) > 1:
458
master_join[tab[0]] = ('graph_id', 'graph_id')
459
460
# join clause for display tables
461
join_str = 'FROM graph_data '
462
for tab in master_join:
463
join_str += 'INNER JOIN %s ON graph_data.graph_id=%s.graph_id '%(tab, tab)
464
465
# construct sql syntax substring for display cols
466
disp_str = 'SELECT graph_data.graph6, '
467
for col in graph_data_disp[1:]:
468
if col != 'graph6': disp_str += 'graph_data.%s, '%col
469
for col in aut_grp_disp[1:]: disp_str += 'aut_grp.%s, '%col
470
for col in degrees_disp[1:]: disp_str += 'degrees.%s, '%col
471
for col in misc_disp[1:]: disp_str += 'misc.%s, '%col
472
for col in spectrum_disp[1:]: disp_str += 'spectrum.%s, '%col
473
disp_str = disp_str.rstrip(', ') + ' '
474
475
# substitue disp_str and join_str back into self's query string
476
self.__query_string__ = re.sub('SELECT.*WHERE ', disp_str + join_str + \
477
'WHERE ', self.__query_string__)
478
self.__query_string__ += ' ORDER BY graph_data.graph6'
479
480
def query_iterator(self):
481
"""
482
Returns an iterator over the results list of the GraphQuery.
483
484
EXAMPLE::
485
486
sage: Q = GraphQuery(display_cols=['graph6'],num_vertices=7, diameter=5)
487
sage: for g in Q:
488
... print g.graph6_string()
489
F?`po
490
F?gqg
491
F@?]O
492
F@OKg
493
F@R@o
494
FA_pW
495
FEOhW
496
FGC{o
497
FIAHo
498
sage: Q = GraphQuery(display_cols=['graph6'],num_vertices=7, diameter=5)
499
sage: it = iter(Q)
500
sage: while True:
501
... try: print it.next().graph6_string()
502
... except StopIteration: break
503
F?`po
504
F?gqg
505
F@?]O
506
F@OKg
507
F@R@o
508
FA_pW
509
FEOhW
510
FGC{o
511
FIAHo
512
"""
513
return iter(self.get_graphs_list())
514
515
__iter__ = query_iterator
516
517
def show(self, max_field_size=20, with_picture=False):
518
"""
519
Displays the results of a query in table format.
520
521
INPUT:
522
523
524
- ``max_field_size`` - width of fields in command
525
prompt version
526
527
- ``with_picture`` - whether or not to display
528
results with a picture of the graph (available only in the
529
notebook)
530
531
532
EXAMPLES::
533
534
sage: G = GraphDatabase()
535
sage: Q = GraphQuery(G, display_cols=['graph6','num_vertices','aut_grp_size'], num_vertices=4, aut_grp_size=4)
536
sage: Q.show()
537
Graph6 Num Vertices Aut Grp Size
538
------------------------------------------------------------
539
C@ 4 4
540
C^ 4 4
541
542
::
543
544
sage: R = GraphQuery(G, display_cols=['graph6','num_vertices','degree_sequence'], num_vertices=4)
545
sage: R.show()
546
Graph6 Num Vertices Degree Sequence
547
------------------------------------------------------------
548
C? 4 [0, 0, 0, 0]
549
C@ 4 [0, 0, 1, 1]
550
CB 4 [0, 1, 1, 2]
551
CF 4 [1, 1, 1, 3]
552
CJ 4 [0, 2, 2, 2]
553
CK 4 [1, 1, 1, 1]
554
CL 4 [1, 1, 2, 2]
555
CN 4 [1, 2, 2, 3]
556
C] 4 [2, 2, 2, 2]
557
C^ 4 [2, 2, 3, 3]
558
C~ 4 [3, 3, 3, 3]
559
560
Show the pictures (in notebook mode only)::
561
562
sage: S = GraphQuery(G, display_cols=['graph6','aut_grp_size'], num_vertices=4)
563
sage: S.show(with_picture=True)
564
Traceback (most recent call last):
565
...
566
NotImplementedError: Cannot display plot on command line.
567
568
Note that pictures can be turned off::
569
570
sage: S.show(with_picture=False)
571
Graph6 Aut Grp Size
572
----------------------------------------
573
C? 24
574
C@ 4
575
CB 2
576
CF 6
577
CJ 6
578
CK 8
579
CL 2
580
CN 2
581
C] 8
582
C^ 4
583
C~ 24
584
585
Show your own query (note that the output is not reformatted for
586
generic queries)::
587
588
sage: (GenericGraphQuery('select degree_sequence from degrees where max_degree=2 and min_degree >= 1',G)).show()
589
degree_sequence
590
--------------------
591
211
592
222
593
2211
594
2222
595
21111
596
22211
597
22211
598
22222
599
221111
600
221111
601
222211
602
222211
603
222211
604
222222
605
222222
606
2111111
607
2221111
608
2221111
609
2221111
610
2222211
611
2222211
612
2222211
613
2222211
614
2222222
615
2222222
616
"""
617
relabel = {}
618
for col in valid_kwds:
619
relabel[col] = ' '.join([word.capitalize() for word in col.split('_')])
620
621
if re.search('SELECT .*degree_sequence.* FROM',self.__query_string__):
622
format_cols = {'degree_sequence': (lambda x,y: data_to_degseq(x,y))}
623
else: format_cols = {}
624
if with_picture:
625
SQLQuery.show(self, max_field_size=max_field_size, \
626
plot_cols={'graph6': (lambda x: graph6_to_plot(x))}, \
627
format_cols=format_cols, id_col='graph6', \
628
relabel_cols=relabel)
629
else:
630
SQLQuery.show(self, max_field_size=max_field_size, \
631
format_cols=format_cols, relabel_cols=relabel, \
632
id_col='graph6')
633
634
def get_graphs_list(self):
635
"""
636
Returns a list of Sage Graph objects that satisfy the query.
637
638
EXAMPLES::
639
640
sage: Q = GraphQuery(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],min_degree=1)
641
sage: L = Q.get_graphs_list()
642
sage: L[0]
643
Graph on 2 vertices
644
sage: len(L)
645
35
646
"""
647
from sage.graphs.graph_list import from_graph6
648
649
s = self.__query_string__
650
re.sub('SELECT.*FROM ', 'SELECT graph6 FROM ', s)
651
q = GenericGraphQuery(s, self.__database__, self.__param_tuple__)
652
graph6_list = q.query_results()
653
return [Graph(str(g[0])) for g in graph6_list]
654
655
def number_of(self):
656
"""
657
Returns the number of graphs in the database that satisfy the
658
query.
659
660
EXAMPLES::
661
662
sage: Q = GraphQuery(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],min_degree=1)
663
sage: Q.number_of()
664
35
665
"""
666
# run graphs_list and return len
667
s = self.__query_string__
668
re.sub('SELECT.*FROM ', 'SELECT graph6 FROM ', s)
669
q = GenericGraphQuery(s, self.__database__, self.__param_tuple__)
670
return len(q.query_results())
671
672
class GraphDatabase(SQLDatabase):
673
674
def __init__(self):
675
"""
676
Graph Database
677
678
INFO:
679
680
This class interfaces with the sqlite database graphs.db. It is an
681
immutable database that inherits from SQLDatabase (see
682
sage.databases.database.py). The display functions and get_graphs_list
683
create their own queries, but it is also possible to query the
684
database by constructing either a SQLQuery.
685
686
The database contains all unlabeled graphs with 7 or fewer nodes.
687
This class will also interface with the optional database package
688
containing all unlabeled graphs with 8 or fewer nodes. The database
689
consists of five tables. For a full table and column structure,
690
call graph_db_info.
691
692
USE: The tables are associated by the unique primary key graph_id
693
(int).
694
695
To query this database, we create a GraphQuery. This can be done
696
directly with the query method or by initializing one of 1.
697
GenericGraphQuery - allows direct entry of a query string and tuple
698
of parameters. This is the route for more advanced users that are
699
familiar with SQL. 2. GraphQuery - is a wrapper of SQLQuery, a
700
general database/query wrapper of SQLite for new users.
701
702
REFERENCES:
703
704
- Data provided by Jason Grout (Brigham Young
705
University). [Online] Available:
706
http://artsci.drake.edu/grout/graphs/
707
708
EXAMPLE::
709
710
sage: G = GraphDatabase()
711
sage: G.get_skeleton()
712
{u'aut_grp': {u'edge_transitive': {'index': True,
713
'unique': False,
714
'primary_key': False,
715
'sql': u'BOOLEAN'},
716
u'vertex_transitive': {'index': True,
717
'unique': False,
718
'primary_key': False,
719
'sql': u'BOOLEAN'},
720
u'aut_grp_size': {'index': True,
721
'unique': False,
722
'primary_key': False,
723
'sql': u'INTEGER'},
724
u'graph_id': {'index': False,
725
'unique': False,
726
'primary_key': False,
727
'sql': u'INTEGER'},
728
u'num_orbits': {'index': True,
729
'unique': False,
730
'primary_key': False,
731
'sql': u'INTEGER'},
732
u'num_fixed_points': {'index': True,
733
'unique': False,
734
'primary_key': False,
735
'sql': u'INTEGER'}},
736
u'degrees': {u'graph_id': {'index': False,
737
'unique': False,
738
'primary_key': False,
739
'sql': u'INTEGER'},
740
u'degrees_sd': {'index': True,
741
'unique': False,
742
'primary_key': False,
743
'sql': u'REAL'},
744
u'max_degree': {'index': True,
745
'unique': False,
746
'primary_key': False,
747
'sql': u'INTEGER'},
748
u'regular': {'index': True,
749
'unique': False,
750
'primary_key': False,
751
'sql': u'BOOLEAN'},
752
u'average_degree': {'index': True,
753
'unique': False,
754
'primary_key': False,
755
'sql': u'REAL'},
756
u'degree_sequence': {'index': False,
757
'unique': False,
758
'primary_key': False,
759
'sql': u'INTEGER'},
760
u'min_degree': {'index': True,
761
'unique': False,
762
'primary_key': False,
763
'sql': u'INTEGER'}},
764
u'spectrum': {u'max_eigenvalue': {'index': True,
765
'unique': False,
766
'primary_key': False,
767
'sql': u'REAL'},
768
u'energy': {'index': True,
769
'unique': False,
770
'primary_key': False,
771
'sql': u'REAL'},
772
u'spectrum': {'index': False,
773
'unique': False,
774
'primary_key': False,
775
'sql': u'TEXT'},
776
u'eigenvalues_sd': {'index': True,
777
'unique': False,
778
'primary_key': False,
779
'sql': u'REAL'},
780
u'graph_id': {'index': False,
781
'unique': False,
782
'primary_key': False,
783
'sql': u'INTEGER'},
784
u'min_eigenvalue': {'index': True,
785
'unique': False,
786
'primary_key': False,
787
'sql': u'REAL'}},
788
u'misc': {u'diameter': {'index': True,
789
'unique': False,
790
'primary_key': False,
791
'sql': u'INTEGER'},
792
u'vertex_connectivity': {'index': True,
793
'unique': False,
794
'primary_key': False,
795
'sql': u'BOOLEAN'},
796
u'graph_id': {'index': False,
797
'unique': False,
798
'primary_key': False,
799
'sql': u'INTEGER'},
800
u'num_components': {'index': True,
801
'unique': False,
802
'primary_key': False,
803
'sql': u'INTEGER'},
804
u'min_vertex_cover_size': {'index': True,
805
'unique': False,
806
'primary_key': False,
807
'sql': u'INTEGER'},
808
u'edge_connectivity': {'index': True,
809
'unique': False,
810
'primary_key': False,
811
'sql': u'BOOLEAN'},
812
u'num_spanning_trees': {'index': True,
813
'unique': False,
814
'primary_key': False,
815
'sql': u'INTEGER'},
816
u'induced_subgraphs': {'index': True,
817
'unique': False,
818
'primary_key': False,
819
'sql': u'TEXT'},
820
u'radius': {'index': True,
821
'unique': False,
822
'primary_key': False,
823
'sql': u'INTEGER'},
824
u'num_cut_vertices': {'index': True,
825
'unique': False,
826
'primary_key': False,
827
'sql': u'INTEGER'},
828
u'clique_number': {'index': True,
829
'unique': False,
830
'primary_key': False,
831
'sql': u'INTEGER'},
832
u'independence_number': {'index': True,
833
'unique': False,
834
'primary_key': False,
835
'sql': u'INTEGER'},
836
u'girth': {'index': True,
837
'unique': False,
838
'primary_key': False,
839
'sql': u'INTEGER'}},
840
u'graph_data': {u'perfect': {'index': True,
841
'unique': False,
842
'primary_key': False,
843
'sql': u'BOOLEAN'},
844
u'planar': {'index': True,
845
'unique': False,
846
'primary_key': False,
847
'sql': u'BOOLEAN'},
848
u'graph_id': {'index': True,
849
'unique': True,
850
'primary_key': False,
851
'sql': u'INTEGER'},
852
u'complement_graph6': {'index': True,
853
'unique': False,
854
'primary_key': False,
855
'sql': u'TEXT'},
856
u'num_edges': {'index': True,
857
'unique': False,
858
'primary_key': False,
859
'sql': u'INTEGER'},
860
u'num_cycles': {'index': True,
861
'unique': False,
862
'primary_key': False,
863
'sql': u'INTEGER'},
864
u'graph6': {'index': True,
865
'unique': False,
866
'primary_key': False,
867
'sql': u'TEXT'},
868
u'num_hamiltonian_cycles': {'index': True,
869
'unique': False,
870
'primary_key': False,
871
'sql': u'INTEGER'},
872
u'lovasz_number': {'index': True,
873
'unique': False,
874
'primary_key': False,
875
'sql': u'REAL'},
876
u'eulerian': {'index': True,
877
'unique': False,
878
'primary_key': False,
879
'sql': u'BOOLEAN'},
880
u'num_vertices': {'index': True,
881
'unique': False,
882
'primary_key': False,
883
'sql': u'INTEGER'}}}
884
"""
885
SQLDatabase.__init__(self,dblocation)
886
887
def _gen_interact_func(self, display, **kwds):
888
"""
889
Generates and returns a function to interact with GraphQuery
890
parameters and results. This is a helper method for the
891
interactive_query method and should not be called directly.
892
893
EXAMPLE::
894
895
sage: D = GraphDatabase()
896
sage: D.interactive_query(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5],max_degree=3)
897
<html>...</html>
898
899
::
900
901
sage: G = GraphDatabase()
902
sage: f = G._gen_interact_func(display=['graph6'], num_vertices=3)
903
sage: type(f)
904
<type 'function'>
905
sage: interact(f)
906
<html>...
907
"""
908
from sagenb.notebook.interact import input_grid
909
arg=['%s=%s'%(word,kwds[word]) for word in kwds]
910
boxes=["%s=input_grid(1,2,['=',%s])"%(word,kwds[word]) for word in kwds]
911
params = ['%s=%s[0]'%tuple(2*[arg[i].split('=')[0]]) for i in range(len(arg))]
912
913
s = 'def _(%s):'%','.join(boxes)
914
t = """
915
print '<html><h2>Query Results:</h2></html>'
916
GraphQuery(display_cols=%s,%s).show(with_picture=True)
917
"""%tuple([display,','.join(params)])
918
s += '\t'+'\n\t'.join(t.split('\n'))+'\n'
919
exec(s)
920
return _
921
922
def query(self, query_dict=None, display_cols=None, **kwds):
923
"""
924
Creates a GraphQuery on this database. For full class details, type
925
GraphQuery? and press shift+enter.
926
927
EXAMPLE::
928
929
sage: D = GraphDatabase()
930
sage: q = D.query(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=['<=',5])
931
sage: q.show()
932
Graph6 Num Vertices Degree Sequence
933
------------------------------------------------------------
934
@ 1 [0]
935
A? 2 [0, 0]
936
A_ 2 [1, 1]
937
B? 3 [0, 0, 0]
938
BG 3 [0, 1, 1]
939
BW 3 [1, 1, 2]
940
Bw 3 [2, 2, 2]
941
C? 4 [0, 0, 0, 0]
942
C@ 4 [0, 0, 1, 1]
943
CB 4 [0, 1, 1, 2]
944
CF 4 [1, 1, 1, 3]
945
CJ 4 [0, 2, 2, 2]
946
CK 4 [1, 1, 1, 1]
947
CL 4 [1, 1, 2, 2]
948
CN 4 [1, 2, 2, 3]
949
C] 4 [2, 2, 2, 2]
950
C^ 4 [2, 2, 3, 3]
951
D?? 5 [0, 0, 0, 0, 0]
952
D?C 5 [0, 0, 0, 1, 1]
953
D?K 5 [0, 0, 1, 1, 2]
954
D?[ 5 [0, 1, 1, 1, 3]
955
D?{ 5 [1, 1, 1, 1, 4]
956
D@K 5 [0, 0, 2, 2, 2]
957
D@O 5 [0, 1, 1, 1, 1]
958
D@S 5 [0, 1, 1, 2, 2]
959
D@[ 5 [0, 1, 2, 2, 3]
960
D@s 5 [1, 1, 1, 2, 3]
961
D@{ 5 [1, 1, 2, 2, 4]
962
DBW 5 [0, 2, 2, 2, 2]
963
DB[ 5 [0, 2, 2, 3, 3]
964
DBg 5 [1, 1, 2, 2, 2]
965
DBk 5 [1, 1, 2, 3, 3]
966
DIk 5 [1, 2, 2, 2, 3]
967
DK[ 5 [1, 2, 2, 2, 3]
968
DLo 5 [2, 2, 2, 2, 2]
969
D_K 5 [1, 1, 1, 1, 2]
970
D`K 5 [1, 1, 2, 2, 2]
971
E??? 6 [0, 0, 0, 0, 0, 0]
972
E??G 6 [0, 0, 0, 0, 1, 1]
973
E??W 6 [0, 0, 0, 1, 1, 2]
974
E??w 6 [0, 0, 1, 1, 1, 3]
975
E?@w 6 [0, 1, 1, 1, 1, 4]
976
E?Bw 6 [1, 1, 1, 1, 1, 5]
977
E?CW 6 [0, 0, 0, 2, 2, 2]
978
E?C_ 6 [0, 0, 1, 1, 1, 1]
979
E?Cg 6 [0, 0, 1, 1, 2, 2]
980
E?Cw 6 [0, 0, 1, 2, 2, 3]
981
E?Dg 6 [0, 1, 1, 1, 2, 3]
982
E?Dw 6 [0, 1, 1, 2, 2, 4]
983
E?Fg 6 [1, 1, 1, 1, 2, 4]
984
E?Ko 6 [0, 0, 2, 2, 2, 2]
985
E?Kw 6 [0, 0, 2, 2, 3, 3]
986
E?LO 6 [0, 1, 1, 2, 2, 2]
987
E?LW 6 [0, 1, 1, 2, 3, 3]
988
E?N? 6 [1, 1, 1, 1, 2, 2]
989
E?NG 6 [1, 1, 1, 1, 3, 3]
990
E@FG 6 [1, 1, 1, 2, 2, 3]
991
E@HW 6 [0, 1, 2, 2, 2, 3]
992
E@N? 6 [1, 1, 2, 2, 2, 2]
993
E@Ow 6 [0, 1, 2, 2, 2, 3]
994
E@Q? 6 [1, 1, 1, 1, 1, 1]
995
E@QW 6 [1, 1, 1, 2, 2, 3]
996
E@T_ 6 [0, 2, 2, 2, 2, 2]
997
E@YO 6 [1, 1, 2, 2, 2, 2]
998
EG?W 6 [0, 1, 1, 1, 1, 2]
999
EGCW 6 [0, 1, 1, 2, 2, 2]
1000
E_?w 6 [1, 1, 1, 1, 1, 3]
1001
E_Cg 6 [1, 1, 1, 1, 2, 2]
1002
E_Cw 6 [1, 1, 1, 2, 2, 3]
1003
E_Ko 6 [1, 1, 2, 2, 2, 2]
1004
F???? 7 [0, 0, 0, 0, 0, 0, 0]
1005
F???G 7 [0, 0, 0, 0, 0, 1, 1]
1006
F???W 7 [0, 0, 0, 0, 1, 1, 2]
1007
F???w 7 [0, 0, 0, 1, 1, 1, 3]
1008
F??@w 7 [0, 0, 1, 1, 1, 1, 4]
1009
F??Bw 7 [0, 1, 1, 1, 1, 1, 5]
1010
F??GW 7 [0, 0, 0, 0, 2, 2, 2]
1011
F??G_ 7 [0, 0, 0, 1, 1, 1, 1]
1012
F??Gg 7 [0, 0, 0, 1, 1, 2, 2]
1013
F??Gw 7 [0, 0, 0, 1, 2, 2, 3]
1014
F??Hg 7 [0, 0, 1, 1, 1, 2, 3]
1015
F??Hw 7 [0, 0, 1, 1, 2, 2, 4]
1016
F??Jg 7 [0, 1, 1, 1, 1, 2, 4]
1017
F??Wo 7 [0, 0, 0, 2, 2, 2, 2]
1018
F??Ww 7 [0, 0, 0, 2, 2, 3, 3]
1019
F??XO 7 [0, 0, 1, 1, 2, 2, 2]
1020
F??XW 7 [0, 0, 1, 1, 2, 3, 3]
1021
F??Z? 7 [0, 1, 1, 1, 1, 2, 2]
1022
F??ZG 7 [0, 1, 1, 1, 1, 3, 3]
1023
F??^? 7 [1, 1, 1, 1, 1, 2, 3]
1024
F?CJG 7 [0, 1, 1, 1, 2, 2, 3]
1025
F?CPW 7 [0, 0, 1, 2, 2, 2, 3]
1026
F?CZ? 7 [0, 1, 1, 2, 2, 2, 2]
1027
F?C_w 7 [0, 0, 1, 2, 2, 2, 3]
1028
F?Ca? 7 [0, 1, 1, 1, 1, 1, 1]
1029
F?CaW 7 [0, 1, 1, 1, 2, 2, 3]
1030
F?Ch_ 7 [0, 0, 2, 2, 2, 2, 2]
1031
F?CqO 7 [0, 1, 1, 2, 2, 2, 2]
1032
F?LCG 7 [1, 1, 1, 1, 2, 2, 2]
1033
F@??W 7 [0, 0, 1, 1, 1, 1, 2]
1034
F@?GW 7 [0, 0, 1, 1, 2, 2, 2]
1035
FG??w 7 [0, 1, 1, 1, 1, 1, 3]
1036
FG?Gg 7 [0, 1, 1, 1, 1, 2, 2]
1037
FG?Gw 7 [0, 1, 1, 1, 2, 2, 3]
1038
FG?Wo 7 [0, 1, 1, 2, 2, 2, 2]
1039
FK??W 7 [1, 1, 1, 1, 1, 1, 2]
1040
FK?GW 7 [1, 1, 1, 1, 2, 2, 2]
1041
F_?@w 7 [1, 1, 1, 1, 1, 1, 4]
1042
F_?Hg 7 [1, 1, 1, 1, 1, 2, 3]
1043
F_?XO 7 [1, 1, 1, 1, 2, 2, 2]
1044
"""
1045
return GraphQuery(self, query_dict, display_cols, **kwds)
1046
1047
def interactive_query(self, display_cols, **kwds):
1048
"""
1049
TODO: This function could use improvement. Add full options of
1050
typical GraphQuery (i.e.: have it accept list input); and update
1051
options in interact to make it less annoying to put in operators.
1052
1053
Generates an interact shell (in the notebook only) that allows the
1054
user to manipulate query parameters and see the updated results.
1055
1056
EXAMPLE::
1057
1058
sage: D = GraphDatabase()
1059
sage: D.interactive_query(display_cols=['graph6','num_vertices','degree_sequence'],num_edges=5,max_degree=3)
1060
<html>...</html>
1061
"""
1062
from sagenb.notebook.interact import interact
1063
print '<html><h1>Interactive Graph Query</h1></html>'
1064
f = self._gen_interact_func(display=display_cols,**kwds)
1065
interact(f)
1066
1067
1068